- * Does not return until the encoder thread has stopped.
- */
- public fun shutdown(): Boolean {
- if (VERBOSE) Log.d(TAG, "releasing encoder objects")
-
- if (mUseMediaRecorder) {
- try {
- mMediaRecorder!!.stop()
- } catch (e: RuntimeException) {
- // https://developer.android.com/reference/android/media/MediaRecorder.html#stop()
- // RuntimeException will be thrown if no valid audio/video data has been received
- // when stop() is called, it usually happens when stop() is called immediately after
- // start(). In this case the output file is not properly constructed ans should be
- // deleted.
- Log.d(TAG, "RuntimeException: stop() is called immediately after start()");
- //noinspection ResultOfMethodCallIgnored
- mOutputFile.delete()
- return false
- }
- } else {
- val handler = mEncoderThread!!.getHandler()
- handler.sendMessage(handler.obtainMessage(EncoderThread.EncoderHandler.MSG_SHUTDOWN))
- try {
- mEncoderThread!!.join()
- } catch (ie: InterruptedException ) {
- Log.w(TAG, "Encoder thread join() was interrupted", ie)
- }
-
- mEncoder!!.stop()
- mEncoder!!.release()
- }
- return true
- }
-
- /**
- * Notifies the encoder thread that a new frame is available to the encoder.
- */
- public fun frameAvailable() {
- if (!mUseMediaRecorder) {
- val handler = mEncoderThread!!.getHandler()
- handler.sendMessage(handler.obtainMessage(
- EncoderThread.EncoderHandler.MSG_FRAME_AVAILABLE))
- }
- }
-
- public fun waitForFirstFrame() {
- if (!mUseMediaRecorder) {
- mEncoderThread!!.waitForFirstFrame()
- }
- }
-
- /**
- * Object that encapsulates the encoder thread.
- *
- * We want to sleep until there's work to do. We don't actually know when a new frame
- * arrives at the encoder, because the other thread is sending frames directly to the
- * input surface. We will see data appear at the decoder output, so we can either use
- * an infinite timeout on dequeueOutputBuffer() or wait() on an object and require the
- * calling app wake us. It's very useful to have all of the buffer management local to
- * this thread -- avoids synchronization -- so we want to do the file muxing in here.
- * So, it's best to sleep on an object and do something appropriate when awakened.
- *
- * This class does not manage the MediaCodec encoder startup/shutdown. The encoder
- * should be fully started before the thread is created, and not shut down until this
- * thread has been joined.
- */
- private class EncoderThread(mediaCodec: MediaCodec,
- outputFile: File,
- orientationHint: Int): Thread() {
- val mEncoder = mediaCodec
- var mEncodedFormat: MediaFormat? = null
- val mBufferInfo = MediaCodec.BufferInfo()
- val mMuxer = MediaMuxer(outputFile.getPath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
- val mOrientationHint = orientationHint
- var mVideoTrack: Int = -1
-
- var mHandler: EncoderHandler? = null
- var mFrameNum: Int = 0
-
- val mLock: Object = Object()
-
- @Volatile
- var mReady: Boolean = false
-
- /**
- * Thread entry point.
- *
- * Prepares the Looper, Handler, and signals anybody watching that we're ready to go.
- */
- public override fun run() {
- Looper.prepare()
- mHandler = EncoderHandler(this) // must create on encoder thread
- Log.d(TAG, "encoder thread ready")
- synchronized (mLock) {
- mReady = true
- mLock.notify() // signal waitUntilReady()
- }
-
- Looper.loop()
-
- synchronized (mLock) {
- mReady = false
- mHandler = null
- }
- Log.d(TAG, "looper quit")
- }
-
- /**
- * Waits until the encoder thread is ready to receive messages.
- *
- * Call from non-encoder thread.
- */
- public fun waitUntilReady() {
- synchronized (mLock) {
- while (!mReady) {
- try {
- mLock.wait()
- } catch (ie: InterruptedException) { /* not expected */ }
- }
- }
- }
-
- /**
- * Waits until the encoder has processed a single frame.
- *
- * Call from non-encoder thread.
- */
- public fun waitForFirstFrame() {
- synchronized (mLock) {
- while (mFrameNum < 1) {
- try {
- mLock.wait()
- } catch (ie: InterruptedException) {
- ie.printStackTrace();
- }
- }
- }
- Log.d(TAG, "Waited for first frame");
- }
-
- /**
- * Returns the Handler used to send messages to the encoder thread.
- */
- public fun getHandler(): EncoderHandler {
- synchronized (mLock) {
- // Confirm ready state.
- if (!mReady) {
- throw RuntimeException("not ready")
- }
- }
- return mHandler!!
- }
-
- /**
- * Drains all pending output from the encoder, and adds it to the circular buffer.
- */
- public fun drainEncoder(): Boolean {
- val TIMEOUT_USEC: Long = 0 // no timeout -- check for buffers, bail if none
- var encodedFrame = false
-
- while (true) {
- var encoderStatus: Int = mEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC)
- if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
- // no output available yet
- break;
- } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
- // Should happen before receiving buffers, and should only happen once.
- // The MediaFormat contains the csd-0 and csd-1 keys, which we'll need
- // for MediaMuxer. It's unclear what else MediaMuxer might want, so
- // rather than extract the codec-specific data and reconstruct a new
- // MediaFormat later, we just grab it here and keep it around.
- mEncodedFormat = mEncoder.getOutputFormat()
- Log.d(TAG, "encoder output format changed: " + mEncodedFormat)
- } else if (encoderStatus < 0) {
- Log.w(TAG, "unexpected result from encoder.dequeueOutputBuffer: " +
- encoderStatus)
- // let's ignore it
- } else {
- var encodedData: ByteBuffer? = mEncoder.getOutputBuffer(encoderStatus)
- if (encodedData == null) {
- throw RuntimeException("encoderOutputBuffer " + encoderStatus +
- " was null");
- }
-
- if ((mBufferInfo.flags and MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
- // The codec config data was pulled out when we got the
- // INFO_OUTPUT_FORMAT_CHANGED status. The MediaMuxer won't accept
- // a single big blob -- it wants separate csd-0/csd-1 chunks --
- // so simply saving this off won't work.
- if (VERBOSE) Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG")
- mBufferInfo.size = 0
- }
-
- if (mBufferInfo.size != 0) {
- // adjust the ByteBuffer values to match BufferInfo (not needed?)
- encodedData.position(mBufferInfo.offset)
- encodedData.limit(mBufferInfo.offset + mBufferInfo.size)
-
- if (mVideoTrack == -1) {
- mVideoTrack = mMuxer.addTrack(mEncodedFormat!!)
- mMuxer.setOrientationHint(mOrientationHint)
- mMuxer.start()
- Log.d(TAG, "Started media muxer")
- }
-
- // mEncBuffer.add(encodedData, mBufferInfo.flags,
- // mBufferInfo.presentationTimeUs)
- mMuxer.writeSampleData(mVideoTrack, encodedData, mBufferInfo)
- encodedFrame = true
-
- if (VERBOSE) {
- Log.d(TAG, "sent " + mBufferInfo.size + " bytes to muxer, ts=" +
- mBufferInfo.presentationTimeUs)
- }
- }
-
- mEncoder.releaseOutputBuffer(encoderStatus, false)
-
- if ((mBufferInfo.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
- Log.w(TAG, "reached end of stream unexpectedly")
- break // out of while
- }
- }
- }
-
- return encodedFrame
- }
-
- /**
- * Drains the encoder output.
- *
- * See notes for {@link EncoderWrapper#frameAvailable()}.
- */
- fun frameAvailable() {
- if (VERBOSE) Log.d(TAG, "frameAvailable")
- if (drainEncoder()) {
- synchronized (mLock) {
- mFrameNum++
- mLock.notify()
- }
- }
- }
-
- /**
- * Tells the Looper to quit.
- */
- fun shutdown() {
- if (VERBOSE) Log.d(TAG, "shutdown")
- Looper.myLooper()!!.quit()
- mMuxer.stop()
- mMuxer.release()
- }
-
- /**
- * Handler for EncoderThread. Used for messages sent from the UI thread (or whatever
- * is driving the encoder) to the encoder thread.
- *
- * The object is created on the encoder thread.
- */
- public class EncoderHandler(et: EncoderThread): Handler() {
- companion object {
- val MSG_FRAME_AVAILABLE: Int = 0
- val MSG_SHUTDOWN: Int = 1
- }
-
- // This shouldn't need to be a weak ref, since we'll go away when the Looper quits,
- // but no real harm in it.
- private val mWeakEncoderThread = WeakReference(et)
-
- // runs on encoder thread
- public override fun handleMessage(msg: Message) {
- val what: Int = msg.what
- if (VERBOSE) {
- Log.v(TAG, "EncoderHandler: what=" + what)
- }
-
- val encoderThread: EncoderThread? = mWeakEncoderThread.get()
- if (encoderThread == null) {
- Log.w(TAG, "EncoderHandler.handleMessage: weak ref is null")
- return
- }
-
- when (what) {
- MSG_FRAME_AVAILABLE -> encoderThread.frameAvailable()
- MSG_SHUTDOWN -> encoderThread.shutdown()
- else -> throw RuntimeException("unknown message " + what)
- }
- }
- }
- }
-}
diff --git a/Camera2Video/app/src/main/java/com/example/android/camera2/video/HardwarePipeline.kt b/Camera2Video/app/src/main/java/com/example/android/camera2/video/HardwarePipeline.kt
deleted file mode 100644
index 1571ae54..00000000
--- a/Camera2Video/app/src/main/java/com/example/android/camera2/video/HardwarePipeline.kt
+++ /dev/null
@@ -1,1245 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.camera2.video.fragments
-
-import android.graphics.Rect
-import android.graphics.SurfaceTexture
-import android.hardware.DataSpace
-import android.hardware.camera2.CameraCaptureSession
-import android.hardware.camera2.CameraCharacteristics
-import android.hardware.camera2.CameraDevice
-import android.hardware.camera2.CaptureRequest
-import android.hardware.camera2.params.DynamicRangeProfiles
-import android.hardware.HardwareBuffer
-import android.hardware.SyncFence
-import android.opengl.EGL14
-import android.opengl.EGL14.EGL_NO_DISPLAY
-import android.opengl.EGL14.EGL_NO_SURFACE
-import android.opengl.EGL15
-import android.opengl.EGLConfig
-import android.opengl.EGLExt
-import android.opengl.EGLSurface
-import android.opengl.GLES11Ext
-import android.opengl.GLES20.GL_COLOR_ATTACHMENT0
-import android.opengl.GLES20.GL_FRAMEBUFFER
-import android.opengl.GLES20.GL_TEXTURE_2D
-import android.opengl.GLES20.glFinish
-import android.opengl.GLES20.glFlush
-import android.opengl.GLES30
-import android.os.Build
-import android.os.ConditionVariable
-import android.os.Handler
-import android.os.HandlerThread
-import android.os.Looper
-import android.os.Message
-import android.util.Log
-import android.util.Range
-import android.util.Size
-import android.view.Surface
-import android.view.SurfaceControl
-import androidx.annotation.RequiresApi
-import androidx.opengl.EGLExt.Companion.eglCreateSyncKHR
-import androidx.opengl.EGLImageKHR
-import com.example.android.camera.utils.AutoFitSurfaceView
-import java.nio.ByteBuffer
-import java.nio.ByteOrder
-import java.nio.IntBuffer
-import kotlin.RuntimeException
-
-import com.example.android.camera2.video.EncoderWrapper
-
-/** Generates a fullscreen quad to cover the entire viewport. Applies the transform set on the
- camera surface to adjust for orientation and scaling when used for copying from the camera
- surface to the render surface. We will pass an identity matrix when copying from the render
- surface to the recording / preview surfaces. */
-private val TRANSFORM_VSHADER = """
-attribute vec4 vPosition;
-uniform mat4 texMatrix;
-varying vec2 vTextureCoord;
-void main() {
- gl_Position = vPosition;
- vec4 texCoord = vec4((vPosition.xy + vec2(1.0, 1.0)) / 2.0, 0.0, 1.0);
- vTextureCoord = (texMatrix * texCoord).xy;
-}
-"""
-
-/**
- * Fragment shaders
- */
-private val INCLUDE_HLG_EOTF = """
-// BT.2100 / BT.2020 HLG EOTF for one channel.
-highp float hlgEotfSingleChannel(highp float hlgChannel) {
- // Specification:
- // https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_HLG
- // Reference implementation:
- // https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=265-279;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa
- const highp float a = 0.17883277;
- const highp float b = 0.28466892;
- const highp float c = 0.55991073;
- return hlgChannel <= 0.5 ? hlgChannel * hlgChannel / 3.0 :
- (b + exp((hlgChannel - c) / a)) / 12.0;
-}
-
-// BT.2100 / BT.2020 HLG EOTF.
-highp vec3 hlgEotf(highp vec3 hlgColor) {
- return vec3(
- hlgEotfSingleChannel(hlgColor.r),
- hlgEotfSingleChannel(hlgColor.g),
- hlgEotfSingleChannel(hlgColor.b)
- );
-}
-"""
-
-private val INCLUDE_YUV_TO_RGB = """
-vec3 yuvToRgb(vec3 yuv) {
- const mat3 yuvToRgbColorTransform = mat3(
- 1.1689f, 1.1689f, 1.1689f,
- 0.0000f, -0.1881f, 2.1502f,
- 1.6853f, -0.6530f, 0.0000f
- );
- const vec3 yuvOffset = vec3(0.0625, 0.5, 0.5);
- yuv = yuv - yuvOffset;
- return clamp(yuvToRgbColorTransform * yuv, 0.0, 1.0);
-}
-"""
-
-private val TRANSFORM_HDR_VSHADER = """#version 300 es
-in vec4 vPosition;
-uniform mat4 texMatrix;
-out vec2 vTextureCoord;
-out vec4 outPosition;
-void main() {
- outPosition = vPosition;
- vec4 texCoord = vec4((vPosition.xy + vec2(1.0, 1.0)) / 2.0, 0.0, 1.0);
- vTextureCoord = (texMatrix * texCoord).xy;
- gl_Position = vPosition;
-}
-"""
-
-/** Passthrough fragment shader, simply copies from the source texture */
-private val PASSTHROUGH_FSHADER = """
-#extension GL_OES_EGL_image_external : require
-precision mediump float;
-varying vec2 vTextureCoord;
-uniform samplerExternalOES sTexture;
-void main() {
- gl_FragColor = texture2D(sTexture, vTextureCoord);
-}
-"""
-
-private val PASSTHROUGH_HDR_FSHADER = """#version 300 es
-#extension GL_OES_EGL_image_external_essl3 : require
-precision mediump float;
-in vec2 vTextureCoord;
-uniform samplerExternalOES sTexture;
-out vec4 outColor;
-void main() {
- outColor = texture(sTexture, vTextureCoord);
-}
-"""
-
-private val YUV_TO_RGB_PASSTHROUGH_HDR_FSHADER = """#version 300 es
-#extension GL_EXT_YUV_target : require
-#extension GL_OES_EGL_image_external_essl3 : require
-precision mediump float;
-uniform __samplerExternal2DY2YEXT sTexture;
-in vec2 vTextureCoord;
-out vec4 outColor;
-""" + INCLUDE_YUV_TO_RGB +
-"""
-void main() {
- vec4 color = texture(sTexture, vTextureCoord);
- color.rgb = yuvToRgb(color.rgb);
- outColor = color;
-}
-"""
-
-private val YUV_TO_RGB_PORTRAIT_HDR_FSHADER = """#version 300 es
-#extension GL_EXT_YUV_target : require
-#extension GL_OES_EGL_image_external_essl3 : require
-precision mediump float;
-uniform __samplerExternal2DY2YEXT sTexture;
-in vec2 vTextureCoord;
-out vec4 outColor;
-""" + INCLUDE_YUV_TO_RGB +
-"""
-// BT.2100 / BT.2020 HLG OETF for one channel.
-highp float hlgOetfSingleChannel(highp float linearChannel) {
- // Specification:
- // https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_HLG
- // Reference implementation:
- // https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=529-543;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa
- const highp float a = 0.17883277;
- const highp float b = 0.28466892;
- const highp float c = 0.55991073;
-
- return linearChannel <= 1.0 / 12.0 ? sqrt(3.0 * linearChannel) :
- a * log(12.0 * linearChannel - b) + c;
-}
-
-// BT.2100 / BT.2020 HLG OETF.
-highp vec3 hlgOetf(highp vec3 linearColor) {
- return vec3(
- hlgOetfSingleChannel(linearColor.r),
- hlgOetfSingleChannel(linearColor.g),
- hlgOetfSingleChannel(linearColor.b)
- );
-}
-""" + INCLUDE_HLG_EOTF +
-"""
-void main() {
- vec4 color = texture(sTexture, vTextureCoord);
-
- // Convert from YUV to RGB
- color.rgb = yuvToRgb(color.rgb);
-
- // Convert from HLG to linear
- color.rgb = hlgEotf(color.rgb);
-
- // Apply the portrait effect. Use gamma 2.4, roughly equivalent to what we expect in sRGB
- float x = vTextureCoord.x * 2.0 - 1.0, y = vTextureCoord.y * 2.0 - 1.0;
- float r = sqrt(x * x + y * y);
- color.rgb *= pow(1.0f - r, 2.4f);
-
- // Convert back to HLG
- color.rgb = hlgOetf(color.rgb);
- outColor = color;
-}
-"""
-
-private val HLG_TO_LINEAR_HDR_FSHADER = """#version 300 es
-#extension GL_OES_EGL_image_external_essl3 : require
-precision mediump float;
-uniform samplerExternalOES sTexture;
-in vec2 vTextureCoord;
-out vec4 outColor;
-""" + INCLUDE_HLG_EOTF +
-"""
-void main() {
- vec4 color = texture(sTexture, vTextureCoord);
-
- // Convert from HLG electrical to linear optical [0.0, 1.0]
- color.rgb = hlgEotf(color.rgb);
-
- outColor = color;
-}
-"""
-
-private val HLG_TO_PQ_HDR_FSHADER = """#version 300 es
-#extension GL_OES_EGL_image_external_essl3 : require
-precision mediump float;
-uniform samplerExternalOES sTexture;
-in vec2 vTextureCoord;
-out vec4 outColor;
-""" + INCLUDE_HLG_EOTF +
-"""
-// BT.2100 / BT.2020, PQ / ST2084 OETF.
-highp vec3 pqOetf(highp vec3 linearColor) {
- // Specification:
- // https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_PQ
- // Reference implementation:
- // https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=514-527;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa
- const highp float m1 = (2610.0 / 16384.0);
- const highp float m2 = (2523.0 / 4096.0) * 128.0;
- const highp float c1 = (3424.0 / 4096.0);
- const highp float c2 = (2413.0 / 4096.0) * 32.0;
- const highp float c3 = (2392.0 / 4096.0) * 32.0;
-
- highp vec3 temp = pow(linearColor, vec3(m1));
- temp = (c1 + c2 * temp) / (1.0 + c3 * temp);
- return pow(temp, vec3(m2));
-}
-
-void main() {
- vec4 color = texture(sTexture, vTextureCoord);
-
- // Convert from HLG electrical to linear optical [0.0, 1.0]
- color.rgb = hlgEotf(color.rgb);
-
- // HLG has a different L = 1 than PQ, which is 10,000 cd/m^2.
- color.rgb /= 40.0f;
-
- // Convert from linear optical [0.0, 1.0] to PQ electrical
- color.rgb = pqOetf(color.rgb);
-
- outColor = color;
-}
-"""
-
-private val PORTRAIT_FSHADER = """
-#extension GL_OES_EGL_image_external : require
-precision mediump float;
-varying vec2 vTextureCoord;
-uniform samplerExternalOES sTexture;
-void main() {
- float x = vTextureCoord.x * 2.0 - 1.0, y = vTextureCoord.y * 2.0 - 1.0;
- vec4 color = texture2D(sTexture, vTextureCoord);
- float r = sqrt(x * x + y * y);
- gl_FragColor = color * (1.0 - r);
-}
-"""
-
-private val IDENTITY_MATRIX = floatArrayOf(
- 1.0f, 0.0f, 0.0f, 0.0f,
- 0.0f, 1.0f, 0.0f, 0.0f,
- 0.0f, 0.0f, 1.0f, 0.0f,
- 0.0f, 0.0f, 0.0f, 1.0f
-)
-
-private val FULLSCREEN_QUAD = floatArrayOf(
- -1.0f, -1.0f, // 0 bottom left
- 1.0f, -1.0f, // 1 bottom right
- -1.0f, 1.0f, // 2 top left
- 1.0f, 1.0f, // 3 top right
-)
-
-private val EGL_GL_COLORSPACE_KHR = 0x309D
-private val EGL_GL_COLORSPACE_BT2020_LINEAR_EXT = 0x333F
-private val EGL_GL_COLORSPACE_BT2020_PQ_EXT = 0x3340
-private val EGL_GL_COLORSPACE_BT2020_HLG_EXT = 0x3540
-private val EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT = 0x3341
-private val EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT = 0x3342
-private val EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT = 0x3343
-private val EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT = 0x3344
-private val EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT = 0x3345
-private val EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT = 0x3346
-private val EGL_SMPTE2086_WHITE_POINT_X_EXT = 0x3347
-private val EGL_SMPTE2086_WHITE_POINT_Y_EXT = 0x3348
-private val EGL_SMPTE2086_MAX_LUMINANCE_EXT = 0x3349
-private val EGL_SMPTE2086_MIN_LUMINANCE_EXT = 0x334A
-
-class HardwarePipeline(width: Int, height: Int, fps: Int, filterOn: Boolean, transfer: Int,
- dynamicRange: Long, characteristics: CameraCharacteristics, encoder: EncoderWrapper,
- viewFinder: AutoFitSurfaceView) : Pipeline(width, height, fps, filterOn, dynamicRange,
- characteristics, encoder, viewFinder) {
- private val renderThread: HandlerThread by lazy {
- val renderThread = HandlerThread("Camera2Video.RenderThread")
- renderThread.start()
- renderThread
- }
-
- private val renderHandler = RenderHandler(renderThread.getLooper(),
- width, height, fps, filterOn, transfer, dynamicRange, characteristics, encoder, viewFinder)
-
- override fun createRecordRequest(session: CameraCaptureSession,
- previewStabilization: Boolean) : CaptureRequest {
- return renderHandler.createRecordRequest(session, previewStabilization)
- }
-
- override fun startRecording() {
- renderHandler.startRecording()
- }
-
- override fun stopRecording() {
- renderHandler.stopRecording()
- }
-
- override fun destroyWindowSurface() {
- renderHandler.sendMessage(renderHandler.obtainMessage(
- RenderHandler.MSG_DESTROY_WINDOW_SURFACE))
- renderHandler.waitDestroyWindowSurface()
- }
-
- override fun setPreviewSize(previewSize: Size) {
- renderHandler.setPreviewSize(previewSize)
- }
-
- override fun createResources(surface: Surface) {
- renderHandler.sendMessage(renderHandler.obtainMessage(
- RenderHandler.MSG_CREATE_RESOURCES, 0, 0, surface))
- }
-
- override fun getPreviewTargets(): List {
- return renderHandler.getTargets()
- }
-
- override fun getRecordTargets(): List {
- return renderHandler.getTargets()
- }
-
- override fun actionDown(encoderSurface: Surface) {
- renderHandler.sendMessage(renderHandler.obtainMessage(
- RenderHandler.MSG_ACTION_DOWN, 0, 0, encoderSurface))
- }
-
- override fun clearFrameListener() {
- renderHandler.sendMessage(renderHandler.obtainMessage(
- RenderHandler.MSG_CLEAR_FRAME_LISTENER))
- renderHandler.waitClearFrameListener()
- }
-
- override fun cleanup() {
- renderHandler.sendMessage(renderHandler.obtainMessage(
- RenderHandler.MSG_CLEANUP))
- renderHandler.waitCleanup()
- }
-
- private class ShaderProgram(id: Int,
- vPositionLoc: Int,
- texMatrixLoc: Int) {
- private val id = id
- private val vPositionLoc = vPositionLoc
- private val texMatrixLoc = texMatrixLoc
-
- public fun setVertexAttribArray(vertexCoords: FloatArray) {
- val nativeBuffer = ByteBuffer.allocateDirect(vertexCoords.size * 4)
- nativeBuffer.order(ByteOrder.nativeOrder())
- val vertexBuffer = nativeBuffer.asFloatBuffer()
- vertexBuffer.put(vertexCoords)
- nativeBuffer.position(0)
- vertexBuffer.position(0)
-
- GLES30.glEnableVertexAttribArray(vPositionLoc)
- checkGlError("glEnableVertexAttribArray")
- GLES30.glVertexAttribPointer(vPositionLoc, 2, GLES30.GL_FLOAT, false, 8, vertexBuffer)
- checkGlError("glVertexAttribPointer")
- }
-
- public fun setTexMatrix(texMatrix: FloatArray) {
- GLES30.glUniformMatrix4fv(texMatrixLoc, 1, false, texMatrix, 0)
- checkGlError("glUniformMatrix4fv")
- }
-
- public fun useProgram() {
- GLES30.glUseProgram(id)
- checkGlError("glUseProgram")
- }
- }
-
- private class RenderHandler(looper: Looper, width: Int, height: Int, fps: Int,
- filterOn: Boolean, transfer: Int, dynamicRange: Long,
- characteristics: CameraCharacteristics, encoder: EncoderWrapper,
- viewFinder: AutoFitSurfaceView): Handler(looper),
- SurfaceTexture.OnFrameAvailableListener {
- companion object {
- val MSG_CREATE_RESOURCES = 0
- val MSG_DESTROY_WINDOW_SURFACE = 1
- val MSG_ACTION_DOWN = 2
- val MSG_CLEAR_FRAME_LISTENER = 3
- val MSG_CLEANUP = 4
- val MSG_ON_FRAME_AVAILABLE = 5
- }
-
- private val width = width
- private val height = height
- private val fps = fps
- private val filterOn = filterOn
- private val transfer = transfer
- private val dynamicRange = dynamicRange
- private val encoder = encoder
- private val viewFinder = viewFinder
-
- private var previewSize = Size(0, 0)
-
- /** OpenGL texture for the SurfaceTexture provided to the camera */
- private var cameraTexId: Int = 0
-
- /** The SurfaceTexture provided to the camera for capture */
- private lateinit var cameraTexture: SurfaceTexture
-
- /** The above SurfaceTexture cast as a Surface */
- private lateinit var cameraSurface: Surface
-
- /** OpenGL texture that will combine the camera output with rendering */
- private var renderTexId: Int = 0
-
- /** The SurfaceTexture we're rendering to */
- private lateinit var renderTexture: SurfaceTexture
-
- /** The above SurfaceTexture cast as a Surface */
- private lateinit var renderSurface: Surface
-
- /** Stuff needed for displaying HLG via SurfaceControl */
- private var contentSurfaceControl: SurfaceControl? = null
- private var windowTexId: Int = 0
- private var windowFboId: Int = 0
-
- private var supportsNativeFences = false
-
- /** Storage space for setting the texMatrix uniform */
- private val texMatrix = FloatArray(16)
-
- /** Orientation of the camera as 0, 90, 180, or 270 degrees */
- private val orientation: Int by lazy {
- characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!
- }
-
- @Volatile
- private var currentlyRecording = false
-
- /** EGL / OpenGL data. */
- private var eglDisplay = EGL14.EGL_NO_DISPLAY
- private var eglContext = EGL14.EGL_NO_CONTEXT
- private var eglConfig: EGLConfig? = null
- private var eglRenderSurface: EGLSurface? = EGL_NO_SURFACE
- private var eglEncoderSurface: EGLSurface? = EGL_NO_SURFACE
- private var eglWindowSurface: EGLSurface? = EGL_NO_SURFACE
- private var vertexShader = 0
- private var cameraToRenderFragmentShader = 0
- private var renderToPreviewFragmentShader = 0
- private var renderToEncodeFragmentShader = 0
-
- private var cameraToRenderShaderProgram: ShaderProgram? = null
- private var renderToPreviewShaderProgram: ShaderProgram? = null
- private var renderToEncodeShaderProgram: ShaderProgram? = null
-
- private val cvResourcesCreated = ConditionVariable(false)
- private val cvDestroyWindowSurface = ConditionVariable(false)
- private val cvClearFrameListener = ConditionVariable(false)
- private val cvCleanup = ConditionVariable(false)
-
- public fun startRecording() {
- currentlyRecording = true
- }
-
- public fun stopRecording() {
- currentlyRecording = false
- }
-
- public fun createRecordRequest(session: CameraCaptureSession,
- previewStabilization: Boolean) : CaptureRequest {
- cvResourcesCreated.block()
-
- // Capture request holds references to target surfaces
- return session.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply {
- // Add the preview surface target
- addTarget(cameraSurface)
-
- set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, Range(fps, fps))
-
- if (previewStabilization) {
- set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE,
- CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION)
- }
- }.build()
- }
-
- public fun setPreviewSize(previewSize: Size) {
- this.previewSize = previewSize
- }
-
- public fun getTargets(): List {
- cvResourcesCreated.block()
-
- return listOf(cameraSurface)
- }
-
- /** Initialize the EGL display, context, and render surface */
- private fun initEGL() {
- eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)
- if (eglDisplay == EGL14.EGL_NO_DISPLAY) {
- throw RuntimeException("unable to get EGL14 display")
- }
- checkEglError("eglGetDisplay")
-
- val version = intArrayOf(0, 0)
- if (!EGL14.eglInitialize(eglDisplay, version, 0, version, 1)) {
- eglDisplay = null
- throw RuntimeException("Unable to initialize EGL14")
- }
- checkEglError("eglInitialize")
-
- val eglVersion = version[0] * 10 + version[1]
- Log.i(TAG, "eglVersion: " + eglVersion)
-
- /** Check that the necessary extensions for color spaces are supported if HDR is enabled */
- if (isHDR()) {
- val requiredExtensionsList = mutableListOf("EGL_KHR_gl_colorspace")
- if (transfer == TransferFragment.PQ_ID) {
- requiredExtensionsList.add("EGL_EXT_gl_colorspace_bt2020_pq")
- } else if (transfer == TransferFragment.LINEAR_ID) {
- requiredExtensionsList.add("EGL_EXT_gl_colorspace_bt2020_linear")
- } else if (transfer == TransferFragment.HLG_ID) {
- requiredExtensionsList.add("EGL_EXT_gl_colorspace_bt2020_hlg")
- }
-
- val eglExtensions = EGL14.eglQueryString(eglDisplay, EGL14.EGL_EXTENSIONS)
-
- for (requiredExtension in requiredExtensionsList) {
- if (!eglExtensions.contains(requiredExtension)) {
- Log.e(TAG, "EGL extension not supported: " + requiredExtension)
- Log.e(TAG, "Supported extensions: ")
- Log.e(TAG, eglExtensions)
- throw RuntimeException("EGL extension not supported: " + requiredExtension)
- }
- }
-
- // More devices can be supported if the eglCreateSyncKHR is used instead of
- // EGL15.eglCreateSync
- supportsNativeFences = eglVersion >= 15
- && eglExtensions.contains("EGL_ANDROID_native_fence_sync")
- }
-
- Log.i(TAG, "isHDR: " + isHDR())
- if (isHDR()) {
- Log.i(TAG, "Preview transfer: " + TransferFragment.idToStr(transfer))
- }
-
- var renderableType = EGL14.EGL_OPENGL_ES2_BIT
- if (isHDR()) {
- renderableType = EGLExt.EGL_OPENGL_ES3_BIT_KHR
- }
-
- var rgbBits = 8
- var alphaBits = 8
- if (isHDR()) {
- rgbBits = 10
- alphaBits = 2
- }
-
- val configAttribList = intArrayOf(
- EGL14.EGL_RENDERABLE_TYPE, renderableType,
- EGL14.EGL_RED_SIZE, rgbBits,
- EGL14.EGL_GREEN_SIZE, rgbBits,
- EGL14.EGL_BLUE_SIZE, rgbBits,
- EGL14.EGL_ALPHA_SIZE, alphaBits,
- EGL14.EGL_NONE
- )
-
- val configs = arrayOfNulls(1)
- val numConfigs = intArrayOf(1)
- EGL14.eglChooseConfig(eglDisplay, configAttribList, 0, configs,
- 0, configs.size, numConfigs, 0)
- eglConfig = configs[0]!!
-
- var requestedVersion = 2
- if (isHDR()) {
- requestedVersion = 3
- }
-
- val contextAttribList = intArrayOf(
- EGL14.EGL_CONTEXT_CLIENT_VERSION, requestedVersion,
- EGL14.EGL_NONE
- )
-
- eglContext = EGL14.eglCreateContext(eglDisplay, eglConfig, EGL14.EGL_NO_CONTEXT,
- contextAttribList, 0)
- if (eglContext == EGL14.EGL_NO_CONTEXT) {
- throw RuntimeException("Failed to create EGL context")
- }
-
- val clientVersion = intArrayOf(0)
- EGL14.eglQueryContext(eglDisplay, eglContext, EGL14.EGL_CONTEXT_CLIENT_VERSION,
- clientVersion, 0)
- Log.v(TAG, "EGLContext created, client version " + clientVersion[0])
-
- val tmpSurfaceAttribs = intArrayOf(
- EGL14.EGL_WIDTH, 1, EGL14.EGL_HEIGHT, 1, EGL14.EGL_NONE)
- val tmpSurface = EGL14.eglCreatePbufferSurface(
- eglDisplay, eglConfig, tmpSurfaceAttribs, /*offset*/ 0)
- EGL14.eglMakeCurrent(eglDisplay, tmpSurface, tmpSurface, eglContext)
- }
-
- private fun createResources(surface: Surface) {
- if (eglContext == EGL14.EGL_NO_CONTEXT) {
- initEGL()
- }
-
- var windowSurfaceAttribs = intArrayOf(EGL14.EGL_NONE)
- if (isHDR()) {
- windowSurfaceAttribs = when (transfer) {
- TransferFragment.PQ_ID -> intArrayOf(
- EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_BT2020_PQ_EXT,
- EGL14.EGL_NONE
- )
- TransferFragment.LINEAR_ID -> intArrayOf(
- EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_BT2020_LINEAR_EXT,
- EGL14.EGL_NONE
- )
- // We configure HLG below
- TransferFragment.HLG_ID -> intArrayOf(
- EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_BT2020_HLG_EXT,
- EGL14.EGL_NONE
- )
- TransferFragment.HLG_WORKAROUND_ID -> intArrayOf(EGL14.EGL_NONE)
- else -> throw RuntimeException("Unexpected transfer " + transfer)
- }
- }
-
- if (!isHDR() or (transfer != TransferFragment.HLG_WORKAROUND_ID)) {
- eglWindowSurface = EGL14.eglCreateWindowSurface(
- eglDisplay, eglConfig, surface,
- windowSurfaceAttribs, 0
- )
- if (eglWindowSurface == EGL_NO_SURFACE) {
- throw RuntimeException("Failed to create EGL texture view surface")
- }
- }
-
- if (eglWindowSurface != EGL_NO_SURFACE) {
- /**
- * This is only experimental for the transfer function. It is intended to be
- * supplied alongside CTA 861.3 metadata
- * https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_surface_CTA861_3_metadata.txt.
- * which describes the max and average luminance of the content).
- *
- * The display will use these parameters to map the source content colors to a
- * colors that fill the display's capabilities.
- *
- * Without providing these parameters, the display will assume "reasonable defaults",
- * which may not be accurate for the source content. This would most likely result
- * in inaccurate colors, although the exact effect is device-dependent.
- *
- * The parameters needs to be tuned.
- * */
- if (isHDR() and (transfer == TransferFragment.PQ_ID)) {
- val SMPTE2086_MULTIPLIER = 50000
- EGL14.eglSurfaceAttrib(
- eglDisplay, eglWindowSurface,
- EGL_SMPTE2086_MAX_LUMINANCE_EXT, 10000 * SMPTE2086_MULTIPLIER
- )
- EGL14.eglSurfaceAttrib(
- eglDisplay, eglWindowSurface,
- EGL_SMPTE2086_MIN_LUMINANCE_EXT, 0
- )
- EGL14.eglSurfaceAttrib(
- eglDisplay, eglWindowSurface,
- EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT,
- (0.708f * SMPTE2086_MULTIPLIER).toInt()
- )
- EGL14.eglSurfaceAttrib(
- eglDisplay, eglWindowSurface,
- EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT,
- (0.292f * SMPTE2086_MULTIPLIER).toInt()
- )
- EGL14.eglSurfaceAttrib(
- eglDisplay, eglWindowSurface,
- EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT,
- (0.170f * SMPTE2086_MULTIPLIER).toInt()
- )
- EGL14.eglSurfaceAttrib(
- eglDisplay, eglWindowSurface,
- EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT,
- (0.797f * SMPTE2086_MULTIPLIER).toInt()
- )
- EGL14.eglSurfaceAttrib(
- eglDisplay, eglWindowSurface,
- EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT,
- (0.131f * SMPTE2086_MULTIPLIER).toInt()
- )
- EGL14.eglSurfaceAttrib(
- eglDisplay, eglWindowSurface,
- EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT,
- (0.046f * SMPTE2086_MULTIPLIER).toInt()
- )
- EGL14.eglSurfaceAttrib(
- eglDisplay, eglWindowSurface,
- EGL_SMPTE2086_WHITE_POINT_X_EXT,
- (0.3127f * SMPTE2086_MULTIPLIER).toInt()
- )
- EGL14.eglSurfaceAttrib(
- eglDisplay, eglWindowSurface,
- EGL_SMPTE2086_WHITE_POINT_Y_EXT,
- (0.3290f * SMPTE2086_MULTIPLIER).toInt()
- )
- }
- }
-
- cameraTexId = createTexture()
- cameraTexture = SurfaceTexture(cameraTexId)
- cameraTexture.setOnFrameAvailableListener(this)
- cameraTexture.setDefaultBufferSize(width, height)
- cameraSurface = Surface(cameraTexture)
-
-
- if (isHDR() and (transfer == TransferFragment.HLG_WORKAROUND_ID)) {
- // Communicating HLG content may not be supported on EGLSurface in API 33, as there
- // is no EGL extension for communicating the surface color space. Instead, create
- // a child SurfaceControl whose parent is the viewFinder's SurfaceView and push
- // buffers directly to the SurfaceControl.
- contentSurfaceControl = SurfaceControl.Builder()
- .setName("HardwarePipeline")
- .setParent(viewFinder.surfaceControl)
- .setHidden(false)
- .build()
- windowTexId = createTexId()
- windowFboId = createFboId()
- }
-
- renderTexId = createTexture()
- renderTexture = SurfaceTexture(renderTexId)
- renderTexture.setDefaultBufferSize(width, height)
- renderSurface = Surface(renderTexture)
-
- var renderSurfaceAttribs = intArrayOf(EGL14.EGL_NONE)
- eglRenderSurface = EGL14.eglCreateWindowSurface(eglDisplay, eglConfig, renderSurface,
- renderSurfaceAttribs, 0)
- if (eglRenderSurface == EGL_NO_SURFACE) {
- throw RuntimeException("Failed to create EGL render surface")
- }
-
- createShaderResources()
- cvResourcesCreated.open()
- }
-
- private fun createShaderResources() {
- if (isHDR()) {
- /** Check that GL_EXT_YUV_target is supported for HDR */
- val extensions = GLES30.glGetString(GLES30.GL_EXTENSIONS)
- if (!extensions.contains("GL_EXT_YUV_target")) {
- throw RuntimeException("Device does not support GL_EXT_YUV_target")
- }
-
- vertexShader = createShader(GLES30.GL_VERTEX_SHADER, TRANSFORM_HDR_VSHADER)
-
- cameraToRenderFragmentShader = when (filterOn) {
- false -> createShader(GLES30.GL_FRAGMENT_SHADER,
- YUV_TO_RGB_PASSTHROUGH_HDR_FSHADER)
- true -> createShader(GLES30.GL_FRAGMENT_SHADER,
- YUV_TO_RGB_PORTRAIT_HDR_FSHADER)
- }
- cameraToRenderShaderProgram = createShaderProgram(cameraToRenderFragmentShader)
-
- renderToPreviewFragmentShader = when (transfer) {
- TransferFragment.PQ_ID -> createShader(GLES30.GL_FRAGMENT_SHADER,
- HLG_TO_PQ_HDR_FSHADER)
- TransferFragment.LINEAR_ID -> createShader(GLES30.GL_FRAGMENT_SHADER,
- HLG_TO_LINEAR_HDR_FSHADER)
- TransferFragment.HLG_ID,
- TransferFragment.HLG_WORKAROUND_ID -> createShader(GLES30.GL_FRAGMENT_SHADER,
- PASSTHROUGH_HDR_FSHADER)
- else -> throw RuntimeException("Unexpected transfer " + transfer)
- }
-
- renderToPreviewShaderProgram = createShaderProgram(
- renderToPreviewFragmentShader)
-
- renderToEncodeFragmentShader = createShader(GLES30.GL_FRAGMENT_SHADER,
- PASSTHROUGH_HDR_FSHADER)
- renderToEncodeShaderProgram = createShaderProgram(renderToEncodeFragmentShader)
- } else {
- vertexShader = createShader(GLES30.GL_VERTEX_SHADER, TRANSFORM_VSHADER)
-
- val passthroughFragmentShader = createShader(GLES30.GL_FRAGMENT_SHADER,
- PASSTHROUGH_FSHADER)
- val passthroughShaderProgram = createShaderProgram(passthroughFragmentShader)
-
- cameraToRenderShaderProgram = when (filterOn) {
- false -> passthroughShaderProgram
- true -> createShaderProgram(createShader(GLES30.GL_FRAGMENT_SHADER,
- PORTRAIT_FSHADER))
- }
-
- renderToPreviewShaderProgram = passthroughShaderProgram
- renderToEncodeShaderProgram = passthroughShaderProgram
- }
- }
-
- /** Creates the shader program used to copy data from one texture to another */
- private fun createShaderProgram(fragmentShader: Int): ShaderProgram {
- var shaderProgram = GLES30.glCreateProgram()
- checkGlError("glCreateProgram")
-
- GLES30.glAttachShader(shaderProgram, vertexShader)
- checkGlError("glAttachShader")
- GLES30.glAttachShader(shaderProgram, fragmentShader)
- checkGlError("glAttachShader")
- GLES30.glLinkProgram(shaderProgram)
- checkGlError("glLinkProgram")
-
- val linkStatus = intArrayOf(0)
- GLES30.glGetProgramiv(shaderProgram, GLES30.GL_LINK_STATUS, linkStatus, 0)
- checkGlError("glGetProgramiv")
- if (linkStatus[0] == 0) {
- val msg = "Could not link program: " + GLES30.glGetProgramInfoLog(shaderProgram)
- GLES30.glDeleteProgram(shaderProgram)
- throw RuntimeException(msg)
- }
-
- var vPositionLoc = GLES30.glGetAttribLocation(shaderProgram, "vPosition")
- checkGlError("glGetAttribLocation")
- var texMatrixLoc = GLES30.glGetUniformLocation(shaderProgram, "texMatrix")
- checkGlError("glGetUniformLocation")
-
- return ShaderProgram(shaderProgram, vPositionLoc, texMatrixLoc)
- }
-
- /** Create a shader given its type and source string */
- private fun createShader(type: Int, source: String): Int {
- var shader = GLES30.glCreateShader(type)
- GLES30.glShaderSource(shader, source)
- checkGlError("glShaderSource")
- GLES30.glCompileShader(shader)
- checkGlError("glCompileShader")
- val compiled = intArrayOf(0)
- GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0)
- checkGlError("glGetShaderiv")
- if (compiled[0] == 0) {
- val msg = "Could not compile shader " + type + ": " + GLES30.glGetShaderInfoLog(shader)
- GLES30.glDeleteShader(shader)
- throw RuntimeException(msg)
- }
- return shader
- }
-
- private fun createTexId(): Int {
- val buffer = IntBuffer.allocate(1)
- GLES30.glGenTextures(1, buffer)
- return buffer.get(0)
- }
-
- private fun destroyTexId(id: Int) {
- val buffer = IntBuffer.allocate(1)
- buffer.put(0, id)
- GLES30.glDeleteTextures(1, buffer)
- }
-
- private fun createFboId(): Int {
- val buffer = IntBuffer.allocate(1)
- GLES30.glGenFramebuffers(1, buffer)
- return buffer.get(0)
- }
-
- private fun destroyFboId(id: Int) {
- val buffer = IntBuffer.allocate(1)
- buffer.put(0, id)
- GLES30.glDeleteFramebuffers(1, buffer)
- }
-
- /** Create an OpenGL texture */
- private fun createTexture(): Int {
- /* Check that EGL has been initialized. */
- if (eglDisplay == null) {
- throw IllegalStateException("EGL not initialized before call to createTexture()");
- }
-
- val texId = createTexId()
- GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId)
- GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MIN_FILTER,
- GLES30.GL_LINEAR)
- GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MAG_FILTER,
- GLES30.GL_LINEAR)
- GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_S,
- GLES30.GL_CLAMP_TO_EDGE)
- GLES30.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_T,
- GLES30.GL_CLAMP_TO_EDGE)
- return texId
- }
-
- private fun destroyWindowSurface() {
- if (eglWindowSurface != EGL_NO_SURFACE && eglDisplay != EGL_NO_DISPLAY) {
- EGL14.eglDestroySurface(eglDisplay, eglWindowSurface)
- }
- eglWindowSurface = EGL14.EGL_NO_SURFACE
- cvDestroyWindowSurface.open()
- }
-
- public fun waitDestroyWindowSurface() {
- cvDestroyWindowSurface.block()
- }
-
- private fun copyTexture(texId: Int, texture: SurfaceTexture, viewportRect: Rect,
- shaderProgram: ShaderProgram, outputIsFramebuffer: Boolean) {
- GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
- checkGlError("glClearColor")
- GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
- checkGlError("glClear")
-
- shaderProgram.useProgram()
- GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
- checkGlError("glActiveTexture")
- GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId)
- checkGlError("glBindTexture")
-
- texture.getTransformMatrix(texMatrix)
-
- // HardwareBuffer coordinates are flipped relative to what GLES expects
- if (outputIsFramebuffer) {
- val flipMatrix = floatArrayOf(
- 1f, 0f, 0f, 0f,
- 0f, -1f, 0f, 0f,
- 0f, 0f, 1f, 0f,
- 0f, 1f, 0f, 1f
- )
- android.opengl.Matrix.multiplyMM(texMatrix, 0, flipMatrix, 0, texMatrix.clone(), 0)
- }
- shaderProgram.setTexMatrix(texMatrix)
-
- shaderProgram.setVertexAttribArray(FULLSCREEN_QUAD)
-
- GLES30.glViewport(viewportRect.left, viewportRect.top, viewportRect.right,
- viewportRect.bottom)
- checkGlError("glViewport")
- GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
- checkGlError("glDrawArrays")
- }
-
- private fun copyCameraToRender() {
- EGL14.eglMakeCurrent(eglDisplay, eglRenderSurface, eglRenderSurface, eglContext)
-
- copyTexture(cameraTexId, cameraTexture, Rect(0, 0, width, height),
- cameraToRenderShaderProgram!!, false)
-
- EGL14.eglSwapBuffers(eglDisplay, eglRenderSurface)
- renderTexture.updateTexImage()
- }
-
- private fun copyRenderToPreview() {
-
- var hardwareBuffer: HardwareBuffer? = null
- var eglImage: EGLImageKHR? = null
- if (transfer == TransferFragment.HLG_WORKAROUND_ID) {
- EGL14.eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, eglContext)
-
- // TODO: use GLFrameBufferRenderer to optimize the performance
- // Note that pooling and reusing HardwareBuffers will have significantly better
- // memory utilization so the HardwareBuffers do not have to be allocated every frame
- hardwareBuffer = HardwareBuffer.create(
- previewSize.width, previewSize.height,
- HardwareBuffer.RGBA_1010102, 1,
- HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
- or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT
- or HardwareBuffer.USAGE_COMPOSER_OVERLAY)
-
- // If we're sending output buffers to a SurfaceControl we cannot render to an
- // EGLSurface. We need to render to a HardwareBuffer instead by importing the
- // HardwareBuffer into EGL, associating it with a texture, and framebuffer, and
- // drawing directly into the HardwareBuffer.
- eglImage = androidx.opengl.EGLExt.eglCreateImageFromHardwareBuffer(
- eglDisplay, hardwareBuffer)
- checkGlError("eglCreateImageFromHardwareBuffer")
-
- GLES30.glBindTexture(GL_TEXTURE_2D, windowTexId)
- checkGlError("glBindTexture")
- androidx.opengl.EGLExt.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglImage!!)
- checkGlError("glEGLImageTargetTexture2DOES")
-
- GLES30.glBindFramebuffer(GL_FRAMEBUFFER, windowFboId);
- checkGlError("glBindFramebuffer")
- GLES30.glFramebufferTexture2D(
- GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, windowTexId, 0);
- checkGlError("glFramebufferTexture2D")
- } else {
- EGL14.eglMakeCurrent(eglDisplay, eglWindowSurface, eglRenderSurface, eglContext)
- }
-
- val cameraAspectRatio = width.toFloat() / height.toFloat()
- val previewAspectRatio = previewSize.width.toFloat() / previewSize.height.toFloat()
- var viewportWidth = previewSize.width
- var viewportHeight = previewSize.height
- var viewportX = 0
- var viewportY = 0
-
- /** The camera display is not the same size as the video. Letterbox the preview so that
- * we can see exactly how the video will turn out. */
- if (previewAspectRatio < cameraAspectRatio) {
- /** Avoid vertical stretching */
- viewportHeight = ((viewportHeight.toFloat() / previewAspectRatio) * cameraAspectRatio).toInt()
- viewportY = (previewSize.height - viewportHeight) / 2
- } else {
- /** Avoid horizontal stretching */
- viewportWidth = ((viewportWidth.toFloat() / cameraAspectRatio) * previewAspectRatio).toInt()
- viewportX = (previewSize.width - viewportWidth) / 2
- }
-
- copyTexture(renderTexId, renderTexture,
- Rect(viewportX, viewportY, viewportWidth, viewportHeight),
- renderToPreviewShaderProgram!!, hardwareBuffer != null)
-
- if (hardwareBuffer != null) {
- if (contentSurfaceControl == null) {
- throw RuntimeException("Forgot to set up SurfaceControl for HLG preview!")
- }
-
- // When rendering to HLG, send each camera frame to the display and communicate the
- // HLG colorspace here.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- val fence = createSyncFence()
- if (fence == null) {
- glFinish()
- checkGlError("glFinish")
- }
- SurfaceControl.Transaction()
- .setBuffer(
- contentSurfaceControl!!,
- hardwareBuffer,
- fence)
- .setDataSpace(
- contentSurfaceControl!!,
- DataSpace.pack(
- DataSpace.STANDARD_BT2020,
- DataSpace.TRANSFER_HLG,
- DataSpace.RANGE_FULL
- ))
- .apply()
- hardwareBuffer.close()
- }
- } else {
- EGL14.eglSwapBuffers(eglDisplay, eglWindowSurface)
- }
-
- if (eglImage != null) {
- GLES30.glBindFramebuffer(GL_FRAMEBUFFER, 0);
- androidx.opengl.EGLExt.eglDestroyImageKHR(eglDisplay, eglImage)
- }
-
- }
-
- private fun copyRenderToEncode() {
- EGL14.eglMakeCurrent(eglDisplay, eglEncoderSurface, eglRenderSurface, eglContext)
-
- var viewportWidth = width
- var viewportHeight = height
-
- /** Swap width and height if the camera is rotated on its side. */
- if (orientation == 90 || orientation == 270) {
- viewportWidth = height
- viewportHeight = width
- }
-
- copyTexture(renderTexId, renderTexture, Rect(0, 0, viewportWidth, viewportHeight),
- renderToEncodeShaderProgram!!, false)
-
- encoder.frameAvailable()
-
- EGL14.eglSwapBuffers(eglDisplay, eglEncoderSurface)
- }
-
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
- private fun createSyncFence() : SyncFence? {
- if (!supportsNativeFences) {
- return null
- }
-
- val eglSync = EGL15.eglCreateSync(
- eglDisplay, androidx.opengl.EGLExt.EGL_SYNC_NATIVE_FENCE_ANDROID,
- longArrayOf(EGL14.EGL_NONE.toLong()), 0)
- checkGlError("eglCreateSync")
- glFlush()
- checkGlError("glFlush")
- return eglSync?.let {
- val fence = EGLExt.eglDupNativeFenceFDANDROID(eglDisplay, it)
- checkGlError("eglDupNativeFenceFDANDROID")
- fence
- }
- }
-
- private fun actionDown(encoderSurface: Surface) {
- val surfaceAttribs = intArrayOf(EGL14.EGL_NONE)
- eglEncoderSurface = EGL14.eglCreateWindowSurface(eglDisplay, eglConfig,
- encoderSurface, surfaceAttribs, 0)
- if (eglEncoderSurface == EGL_NO_SURFACE) {
- val error = EGL14.eglGetError()
- throw RuntimeException("Failed to create EGL encoder surface"
- + ": eglGetError = 0x" + Integer.toHexString(error))
- }
- }
-
- private fun clearFrameListener() {
- cameraTexture.setOnFrameAvailableListener(null)
- cvClearFrameListener.open()
- }
-
- public fun waitClearFrameListener() {
- cvClearFrameListener.block()
- }
-
- private fun cleanup() {
- EGL14.eglDestroySurface(eglDisplay, eglEncoderSurface)
- eglEncoderSurface = EGL_NO_SURFACE
- EGL14.eglDestroySurface(eglDisplay, eglRenderSurface)
- eglRenderSurface = EGL_NO_SURFACE
-
- cameraTexture.release()
-
- if (windowTexId > 0) {
- destroyTexId(windowTexId)
- }
-
- if (windowFboId > 0) {
- destroyFboId(windowFboId)
- }
-
- EGL14.eglDestroyContext(eglDisplay, eglContext)
-
- eglDisplay = EGL14.EGL_NO_DISPLAY
- eglContext = EGL14.EGL_NO_CONTEXT
-
- cvCleanup.open()
- }
-
- public fun waitCleanup() {
- cvCleanup.block()
- }
-
- @Suppress("UNUSED_PARAMETER")
- private fun onFrameAvailableImpl(surfaceTexture: SurfaceTexture) {
- if (eglContext == EGL14.EGL_NO_CONTEXT) {
- return
- }
-
- /** The camera API does not update the tex image. Do so here. */
- cameraTexture.updateTexImage()
-
- /** Copy from the camera texture to the render texture */
- if (eglRenderSurface != EGL_NO_SURFACE) {
- copyCameraToRender()
- }
-
- /** Copy from the render texture to the TextureView */
- copyRenderToPreview()
-
- /** Copy to the encoder surface if we're currently recording. */
- if (eglEncoderSurface != EGL_NO_SURFACE && currentlyRecording) {
- copyRenderToEncode()
- }
- }
-
- private fun isHDR(): Boolean {
- return dynamicRange != DynamicRangeProfiles.STANDARD
- }
-
- override fun onFrameAvailable(surfaceTexture: SurfaceTexture) {
- sendMessage(obtainMessage(MSG_ON_FRAME_AVAILABLE, 0, 0, surfaceTexture))
- }
-
- public override fun handleMessage(msg: Message) {
- when (msg.what) {
- MSG_CREATE_RESOURCES -> createResources(msg.obj as Surface)
- MSG_DESTROY_WINDOW_SURFACE -> destroyWindowSurface()
- MSG_ACTION_DOWN -> actionDown(msg.obj as Surface)
- MSG_CLEAR_FRAME_LISTENER -> clearFrameListener()
- MSG_CLEANUP -> cleanup()
- MSG_ON_FRAME_AVAILABLE -> onFrameAvailableImpl(msg.obj as SurfaceTexture)
- }
- }
- }
-
- companion object {
- private val TAG = HardwarePipeline::class.java.simpleName
-
- /** Check if OpenGL failed, and throw an exception if so */
- private fun checkGlError(op: String) {
- val error = GLES30.glGetError()
- if (error != GLES30.GL_NO_ERROR) {
- val msg = op + ": glError 0x" + Integer.toHexString(error)
- Log.e(TAG, msg)
- throw RuntimeException(msg)
- }
- }
-
- private fun checkEglError(op: String) {
- val eglError = EGL14.eglGetError()
- if (eglError != EGL14.EGL_SUCCESS) {
- val msg = op + ": eglError 0x" + Integer.toHexString(eglError)
- Log.e(TAG, msg)
- throw RuntimeException(msg);
- }
- }
- }
-
-}
diff --git a/Camera2Video/app/src/main/java/com/example/android/camera2/video/Pipeline.kt b/Camera2Video/app/src/main/java/com/example/android/camera2/video/Pipeline.kt
deleted file mode 100644
index 51a54f33..00000000
--- a/Camera2Video/app/src/main/java/com/example/android/camera2/video/Pipeline.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.camera2.video.fragments
-
-import android.hardware.camera2.CameraCaptureSession
-import android.hardware.camera2.CameraCharacteristics
-import android.hardware.camera2.CaptureRequest
-import android.util.Size
-import android.view.Surface
-import com.example.android.camera.utils.AutoFitSurfaceView
-
-import com.example.android.camera2.video.EncoderWrapper
-
-abstract class Pipeline(width: Int, height: Int, fps: Int, filterOn: Boolean,
- dynamicRange: Long, characteristics: CameraCharacteristics, encoder: EncoderWrapper,
- viewFinder: AutoFitSurfaceView) {
- protected val width = width
- protected val height = height
- protected val fps = fps
- protected val filterOn = filterOn
- protected val dynamicRange = dynamicRange
- protected val characteristics = characteristics
- protected val encoder = encoder
- protected val viewFinder = viewFinder
-
- open public fun createPreviewRequest(session: CameraCaptureSession,
- previewStabilization: Boolean): CaptureRequest? {
- return null
- }
-
- public abstract fun createRecordRequest(session: CameraCaptureSession,
- previewStabilization: Boolean): CaptureRequest
-
- open public fun destroyWindowSurface() { }
-
- open public fun setPreviewSize(previewSize: Size) { }
-
- open public fun createResources(surface: Surface) { }
-
- public abstract fun getPreviewTargets(): List
-
- public abstract fun getRecordTargets(): List
-
- open public fun actionDown(encoderSurface: Surface) { }
-
- open public fun clearFrameListener() { }
-
- open public fun cleanup() { }
-
- open public fun startRecording() { }
-
- open public fun stopRecording() { }
-}
\ No newline at end of file
diff --git a/Camera2Video/app/src/main/java/com/example/android/camera2/video/SoftwarePipeline.kt b/Camera2Video/app/src/main/java/com/example/android/camera2/video/SoftwarePipeline.kt
deleted file mode 100644
index 5f9874b2..00000000
--- a/Camera2Video/app/src/main/java/com/example/android/camera2/video/SoftwarePipeline.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.camera2.video.fragments
-
-import android.hardware.camera2.CameraCaptureSession
-import android.hardware.camera2.CameraCharacteristics
-import android.hardware.camera2.CameraDevice
-import android.hardware.camera2.CaptureRequest
-import android.util.Range
-import android.view.Surface
-import com.example.android.camera.utils.AutoFitSurfaceView
-
-import com.example.android.camera2.video.EncoderWrapper
-
-class SoftwarePipeline(width: Int, height: Int, fps: Int, filterOn: Boolean,
- dynamicRange: Long, characteristics: CameraCharacteristics, encoder: EncoderWrapper,
- viewFinder: AutoFitSurfaceView) : Pipeline(width, height, fps, filterOn,
- dynamicRange, characteristics, encoder, viewFinder) {
-
- override fun createPreviewRequest(session: CameraCaptureSession,
- previewStabilization: Boolean): CaptureRequest? {
- // Capture request holds references to target surfaces
- return session.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply {
- // Add the preview surface target
- addTarget(viewFinder.holder.surface)
-
- if (previewStabilization) {
- set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE,
- CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION)
- }
- }.build()
- }
-
- override fun createRecordRequest(session: CameraCaptureSession,
- previewStabilization: Boolean): CaptureRequest {
- // Capture request holds references to target surfaces
- return session.device.createCaptureRequest(CameraDevice.TEMPLATE_RECORD).apply {
- // Add the preview and recording surface targets
- addTarget(viewFinder.holder.surface)
- addTarget(encoder.getInputSurface())
-
- // Sets user requested FPS for all targets
- set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, Range(fps, fps))
-
- if (previewStabilization) {
- set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE,
- CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION)
- }
- }.build()
- }
-
- override fun getPreviewTargets(): List {
- return listOf(viewFinder.holder.surface)
- }
-
- override fun getRecordTargets(): List {
- return listOf(viewFinder.holder.surface, encoder.getInputSurface())
- }
-}
\ No newline at end of file
diff --git a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/ColorSpaceFragment.kt b/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/ColorSpaceFragment.kt
deleted file mode 100644
index 9d03fc60..00000000
--- a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/ColorSpaceFragment.kt
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright 2023 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
- *
- * https://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.android.camera2.video.fragments
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.graphics.ColorSpace
-import android.graphics.ImageFormat
-import android.hardware.camera2.CameraCharacteristics
-import android.hardware.camera2.CameraManager
-import android.hardware.camera2.CameraMetadata
-import android.hardware.camera2.params.ColorSpaceProfiles
-import android.hardware.camera2.params.DynamicRangeProfiles
-import android.os.Bundle
-import android.util.Size
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.fragment.app.Fragment
-import androidx.navigation.Navigation
-import androidx.navigation.fragment.navArgs
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import com.example.android.camera.utils.GenericListAdapter
-import com.example.android.camera2.video.R
-
-/**
- * In this [Fragment] we let users pick a color space profile for the camera.
- */
-class ColorSpaceFragment : Fragment() {
-
- private val args: ColorSpaceFragmentArgs by navArgs()
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? = RecyclerView(requireContext())
-
- @SuppressLint("MissingPermission")
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- view as RecyclerView
- view.apply {
- layoutManager = LinearLayoutManager(requireContext())
-
- val cameraManager =
- requireContext().getSystemService(Context.CAMERA_SERVICE) as CameraManager
-
- val colorSpaceList = enumerateColorSpaces(cameraManager, args.cameraId,
- args.dynamicRange)
-
- val layoutId = android.R.layout.simple_list_item_1
- adapter = GenericListAdapter(colorSpaceList, itemLayoutId = layoutId) {
- view, item, _ ->
- view.findViewById(android.R.id.text1).text = item.name
- view.setOnClickListener {
- navigate(item.value, cameraManager)
- }
- }
- }
- }
-
- private fun navigate(colorSpace: ColorSpace.Named, cameraManager: CameraManager) {
- val navController =
- Navigation.findNavController(requireActivity(), R.id.fragment_container)
-
- if (supportsPreviewStabilization(cameraManager)) {
- navController.navigate(
- ColorSpaceFragmentDirections.actionColorSpaceToPreviewStabilization(
- args.cameraId, args.width, args.height, args.fps, args.dynamicRange,
- colorSpace.ordinal))
- } else if (args.dynamicRange == DynamicRangeProfiles.STANDARD) {
- navController.navigate(
- ColorSpaceFragmentDirections.actionColorSpaceToEncodeApi(
- args.cameraId, args.width, args.height, args.fps, args.dynamicRange,
- colorSpace.ordinal, false))
- }
- }
-
- private fun supportsPreviewStabilization(cameraManager: CameraManager) : Boolean {
- val characteristics = cameraManager.getCameraCharacteristics(args.cameraId)
- val previewStabilizationModes = characteristics.get(
- CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES)!!
- return previewStabilizationModes.contains(
- CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION)
- }
-
- companion object {
-
- private data class ColorSpaceInfo(
- val name: String,
- val value: ColorSpace.Named)
-
- @SuppressLint("NewApi")
- private fun colorSpaceString(value: ColorSpace.Named) = when (value) {
- ColorSpace.Named.ACES -> "ACES"
- ColorSpace.Named.ACESCG -> "ACESCG"
- ColorSpace.Named.ADOBE_RGB -> "ADOBE_RGB"
- ColorSpace.Named.BT2020 -> "BT2020"
- ColorSpace.Named.BT2020_HLG -> "BT2020_HLG"
- ColorSpace.Named.BT2020_PQ -> "BT2020_PQ"
- ColorSpace.Named.BT709 -> "BT709"
- ColorSpace.Named.CIE_LAB -> "CIE_LAB"
- ColorSpace.Named.CIE_XYZ -> "CIE_XYZ"
- ColorSpace.Named.DCI_P3 -> "DCI_P3"
- ColorSpace.Named.DISPLAY_P3 -> "DISPLAY_P3"
- ColorSpace.Named.EXTENDED_SRGB -> "EXTENDED_SRGB"
- ColorSpace.Named.LINEAR_EXTENDED_SRGB -> "LINEAR_EXTENDED_SRGB"
- ColorSpace.Named.LINEAR_SRGB -> "LINEAR_SRGB"
- ColorSpace.Named.NTSC_1953 -> "NTSC_1953"
- ColorSpace.Named.PRO_PHOTO_RGB -> "PRO_PHOTO_RGB"
- ColorSpace.Named.SMPTE_C -> "SMPTE_C"
- ColorSpace.Named.SRGB -> "SRGB"
- else -> "UNKNOWN"
- }
-
- @SuppressLint("InlinedApi")
- private fun enumerateColorSpaces(cameraManager: CameraManager,
- cameraId: String,
- dynamicRange: Long): List {
- val colorSpaceList: MutableList = mutableListOf()
-
- val characteristics = cameraManager.getCameraCharacteristics(cameraId)
- val colorSpaceProfiles = characteristics.get(
- CameraCharacteristics.REQUEST_AVAILABLE_COLOR_SPACE_PROFILES)!!
- colorSpaceProfiles.getSupportedColorSpacesForDynamicRange(ImageFormat.UNKNOWN,
- dynamicRange).forEach { colorSpace ->
- val colorSpaceName = colorSpaceString(colorSpace)
- colorSpaceList.add(ColorSpaceInfo(colorSpaceName, colorSpace))
- }
-
- return colorSpaceList
- }
- }
-}
\ No newline at end of file
diff --git a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/DynamicRangeFragment.kt b/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/DynamicRangeFragment.kt
deleted file mode 100644
index a4926261..00000000
--- a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/DynamicRangeFragment.kt
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright 2023 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
- *
- * https://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.android.camera2.video.fragments
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.hardware.camera2.CameraCharacteristics
-import android.hardware.camera2.CameraManager
-import android.hardware.camera2.CameraMetadata
-import android.hardware.camera2.params.ColorSpaceProfiles
-import android.hardware.camera2.params.DynamicRangeProfiles
-import android.os.Bundle
-import android.util.Size
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.fragment.app.Fragment
-import androidx.navigation.Navigation
-import androidx.navigation.fragment.navArgs
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import com.example.android.camera.utils.GenericListAdapter
-import com.example.android.camera2.video.R
-
-/**
- * In this [Fragment] we let users pick a dynamic range profile for the camera.
- */
-class DynamicRangeFragment : Fragment() {
-
- private val args: DynamicRangeFragmentArgs by navArgs()
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? = RecyclerView(requireContext())
-
- @SuppressLint("MissingPermission")
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- view as RecyclerView
- view.apply {
- layoutManager = LinearLayoutManager(requireContext())
-
- val cameraManager =
- requireContext().getSystemService(Context.CAMERA_SERVICE) as CameraManager
-
- val dynamicRangeList = enumerateDynamicRangeProfiles(cameraManager, args.cameraId)
-
- val layoutId = android.R.layout.simple_list_item_1
- adapter = GenericListAdapter(dynamicRangeList, itemLayoutId = layoutId) {
- view, item, _ ->
- view.findViewById(android.R.id.text1).text = item.name
- view.setOnClickListener {
- navigate(item.value, cameraManager)
- }
- }
- }
- }
-
- private fun navigate(dynamicRangeProfile: Long, cameraManager: CameraManager) {
- val navController =
- Navigation.findNavController(requireActivity(), R.id.fragment_container)
-
- val characteristics = cameraManager.getCameraCharacteristics(args.cameraId)
-
- if (supportsColorSpaces(characteristics)) {
- navController.navigate(
- DynamicRangeFragmentDirections.actionDynamicRangeToColorSpace(
- args.cameraId, args.width, args.height, args.fps, dynamicRangeProfile))
- } else if (supportsPreviewStabilization(characteristics)) {
- navController.navigate(
- DynamicRangeFragmentDirections.actionDynamicRangeToPreviewStabilization(
- args.cameraId, args.width, args.height, args.fps, dynamicRangeProfile,
- ColorSpaceProfiles.UNSPECIFIED))
- } else {
- navController.navigate(
- DynamicRangeFragmentDirections.actionDynamicRangeToEncodeApi(
- args.cameraId, args.width, args.height, args.fps, dynamicRangeProfile,
- ColorSpaceProfiles.UNSPECIFIED, false))
- }
- }
-
- private fun supportsColorSpaces(characteristics: CameraCharacteristics): Boolean {
- if (android.os.Build.VERSION.SDK_INT >=
- android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
- val colorSpaceProfiles = characteristics.get(
- CameraCharacteristics.REQUEST_AVAILABLE_COLOR_SPACE_PROFILES)
- return colorSpaceProfiles != null
- }
- return false
- }
-
- private fun supportsPreviewStabilization(characteristics: CameraCharacteristics) : Boolean {
- val previewStabilizationModes = characteristics.get(
- CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES)!!
- return previewStabilizationModes.contains(
- CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION)
- }
-
- companion object {
-
- private data class DynamicRangeInfo(
- val name: String,
- val value: Long)
-
- private fun dynamicRangeProfileString(value: Long) = when (value) {
- DynamicRangeProfiles.STANDARD -> "STANDARD"
- DynamicRangeProfiles.HLG10 -> "HLG10"
- DynamicRangeProfiles.HDR10 -> "HDR10"
- DynamicRangeProfiles.HDR10_PLUS -> "HDR10_PLUS"
- DynamicRangeProfiles.DOLBY_VISION_10B_HDR_REF -> "DOLBY_VISION_10B_HDR_REF"
- DynamicRangeProfiles.DOLBY_VISION_10B_HDR_REF_PO -> "DOLBY_VISION_10B_HDR_REF_PO"
- DynamicRangeProfiles.DOLBY_VISION_10B_HDR_OEM -> "DOLBY_VISION_10B_HDR_OEM"
- DynamicRangeProfiles.DOLBY_VISION_10B_HDR_OEM_PO -> "DOLBY_VISION_10B_HDR_OEM_PO"
- DynamicRangeProfiles.DOLBY_VISION_8B_HDR_REF -> "DOLBY_VISION_8B_HDR_REF"
- DynamicRangeProfiles.DOLBY_VISION_8B_HDR_REF_PO -> "DOLBY_VISION_8B_HDR_REF_PO"
- DynamicRangeProfiles.DOLBY_VISION_8B_HDR_OEM -> "DOLBY_VISION_8B_HDR_OEM"
- DynamicRangeProfiles.DOLBY_VISION_8B_HDR_OEM_PO -> "DOLBY_VISION_8B_HDR_OEM_PO"
- else -> "UNKNOWN"
- }
-
- @SuppressLint("InlinedApi")
- private fun enumerateDynamicRangeProfiles(cameraManager: CameraManager,
- cameraId: String): List {
- val dynamicRangeList: MutableList = mutableListOf()
-
- val characteristics = cameraManager.getCameraCharacteristics(cameraId)
- val dynamicRangeProfiles = characteristics.get(
- CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES)!!
- dynamicRangeProfiles.getSupportedProfiles().forEach { profile ->
- val profileName = dynamicRangeProfileString(profile)
- dynamicRangeList.add(DynamicRangeInfo(profileName, profile))
- }
-
- return dynamicRangeList
- }
- }
-}
diff --git a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/EncodeApiFragment.kt b/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/EncodeApiFragment.kt
deleted file mode 100644
index c4022776..00000000
--- a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/EncodeApiFragment.kt
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2023 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
- *
- * https://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.android.camera2.video.fragments
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.hardware.camera2.params.DynamicRangeProfiles
-import android.os.Bundle
-import android.util.Size
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.fragment.app.Fragment
-import androidx.navigation.Navigation
-import androidx.navigation.fragment.navArgs
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import com.example.android.camera.utils.GenericListAdapter
-import com.example.android.camera2.video.R
-
-/**
- * In this [Fragment] we let users pick the encoding API to use (MediaCodec or MediaRecorder)
- */
-class EncodeApiFragment : Fragment() {
-
- private val args: EncodeApiFragmentArgs by navArgs()
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? = RecyclerView(requireContext())
-
- @SuppressLint("MissingPermission")
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- view as RecyclerView
- view.apply {
- layoutManager = LinearLayoutManager(requireContext())
- val modeList = enumerateModes(args.dynamicRange)
- val layoutId = android.R.layout.simple_list_item_1
- adapter = GenericListAdapter(modeList, itemLayoutId = layoutId) { view, item, _ ->
- view.findViewById(android.R.id.text1).text = item.name
- view.setOnClickListener {
- navigate(item.value)
- }
- }
- }
- }
-
- private fun navigate(useMediaRecorder: Boolean) {
- val navController =
- Navigation.findNavController(requireActivity(), R.id.fragment_container)
-
- navController.navigate(
- EncodeApiFragmentDirections.actionEncodeApiToVideoCodec(
- args.cameraId, args.width, args.height, args.fps,
- args.dynamicRange, args.colorSpace, args.previewStabilization, useMediaRecorder))
- }
-
- companion object {
- private data class ApiInfo(
- val name: String,
- val value: Boolean)
-
- @SuppressLint("InlinedApi")
- private fun enumerateModes(dynamicRange: Long): List {
- val modeList: MutableList = mutableListOf()
-
- modeList.add(ApiInfo("MediaCodec", false))
- if (dynamicRange == DynamicRangeProfiles.STANDARD) {
- modeList.add(ApiInfo("MediaRecorder", true))
- }
-
- return modeList
- }
- }
-}
diff --git a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/FilterFragment.kt b/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/FilterFragment.kt
deleted file mode 100644
index 542895d3..00000000
--- a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/FilterFragment.kt
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2023 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
- *
- * https://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.android.camera2.video.fragments
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.hardware.camera2.CameraCharacteristics
-import android.hardware.camera2.CameraManager
-import android.hardware.camera2.params.DynamicRangeProfiles
-import android.os.Bundle
-import android.util.Size
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.fragment.app.Fragment
-import androidx.navigation.Navigation
-import androidx.navigation.fragment.navArgs
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import com.example.android.camera.utils.GenericListAdapter
-import com.example.android.camera2.video.R
-
-/**
- * In this [Fragment] we let users pick whether or not the portrait filter is on.
- */
-class FilterFragment : Fragment() {
-
- private val args: FilterFragmentArgs by navArgs()
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? = RecyclerView(requireContext())
-
- @SuppressLint("MissingPermission")
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- view as RecyclerView
- view.apply {
- layoutManager = LinearLayoutManager(requireContext())
- val modeList = enumerateModes()
- val layoutId = android.R.layout.simple_list_item_1
- adapter = GenericListAdapter(modeList, itemLayoutId = layoutId) { view, item, _ ->
- view.findViewById(android.R.id.text1).text = item.name
- view.setOnClickListener {
- val navController =
- Navigation.findNavController(requireActivity(), R.id.fragment_container)
- if (args.dynamicRange == DynamicRangeProfiles.STANDARD) {
- navController.navigate(
- FilterFragmentDirections.actionFilterToPreview(
- args.cameraId, args.width, args.height, args.fps,
- args.dynamicRange, args.colorSpace, args.previewStabilization,
- args.useMediaRecorder, args.videoCodec, item.value, true, 0))
- } else {
- navController.navigate(
- FilterFragmentDirections.actionFilterToTransfer(
- args.cameraId, args.width, args.height, args.fps,
- args.dynamicRange, args.colorSpace, args.previewStabilization,
- args.useMediaRecorder, args.videoCodec, item.value))
- }
- }
- }
- }
- }
-
- companion object {
- private data class ModeInfo(
- val name: String,
- val value: Boolean)
-
- @SuppressLint("InlinedApi")
- private fun enumerateModes(): List {
- val modeList: MutableList = mutableListOf()
-
- modeList.add(ModeInfo("Portrait Filter On", true))
- modeList.add(ModeInfo("Portrait Filter Off", false))
-
- return modeList
- }
- }
-}
diff --git a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/PermissionsFragment.kt b/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/PermissionsFragment.kt
deleted file mode 100644
index 3a38ef4f..00000000
--- a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/PermissionsFragment.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera2.video.fragments
-
-import android.Manifest
-import android.content.Context
-import android.content.pm.PackageManager
-import android.os.Bundle
-import android.widget.Toast
-import androidx.core.content.ContextCompat
-import androidx.fragment.app.Fragment
-import androidx.navigation.Navigation
-import com.example.android.camera2.video.R
-
-private const val PERMISSIONS_REQUEST_CODE = 10
-private val PERMISSIONS_REQUIRED = arrayOf(
- Manifest.permission.CAMERA,
- Manifest.permission.RECORD_AUDIO)
-
-/**
- * This [Fragment] requests permissions and, once granted, it will navigate to the next fragment
- */
-class PermissionsFragment : Fragment() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- if (hasPermissions(requireContext())) {
- // If permissions have already been granted, proceed
- Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(
- PermissionsFragmentDirections.actionPermissionsToSelector())
- } else {
- // Request camera-related permissions
- requestPermissions(PERMISSIONS_REQUIRED, PERMISSIONS_REQUEST_CODE)
- }
- }
-
- override fun onRequestPermissionsResult(
- requestCode: Int, permissions: Array, grantResults: IntArray) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults)
- if (requestCode == PERMISSIONS_REQUEST_CODE) {
- if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- // Takes the user to the success fragment when permission is granted
- Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(
- PermissionsFragmentDirections.actionPermissionsToSelector())
- } else {
- Toast.makeText(context, "Permission request denied", Toast.LENGTH_LONG).show()
- }
- }
- }
-
- companion object {
-
- /** Convenience method used to check if all permissions required by this app are granted */
- fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {
- ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
- }
- }
-}
diff --git a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/PreviewFragment.kt b/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/PreviewFragment.kt
deleted file mode 100644
index 333fb525..00000000
--- a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/PreviewFragment.kt
+++ /dev/null
@@ -1,546 +0,0 @@
-/*
- * Copyright 2023 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
- *
- * https://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.android.camera2.video.fragments
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.content.Intent
-import android.content.pm.ActivityInfo
-import android.graphics.ColorSpace
-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.TotalCaptureResult
-import android.hardware.camera2.params.ColorSpaceProfiles
-import android.hardware.camera2.params.DynamicRangeProfiles
-import android.hardware.camera2.params.OutputConfiguration
-import android.hardware.camera2.params.SessionConfiguration
-import android.media.MediaScannerConnection
-import android.os.Bundle
-import android.os.ConditionVariable
-import android.os.Handler
-import android.os.HandlerThread
-import android.os.Looper
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.MotionEvent
-import android.view.Surface
-import android.view.SurfaceHolder
-import android.view.View
-import android.view.ViewGroup
-import android.webkit.MimeTypeMap
-import android.widget.Toast
-import androidx.core.content.ContextCompat
-import androidx.core.content.FileProvider
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.lifecycleScope
-import androidx.navigation.NavController
-import androidx.navigation.Navigation
-import androidx.navigation.fragment.navArgs
-import com.example.android.camera.utils.getPreviewOutputSize
-import com.example.android.camera2.video.BuildConfig
-import com.example.android.camera2.video.CameraActivity
-import com.example.android.camera2.video.EncoderWrapper
-import com.example.android.camera2.video.R
-import com.example.android.camera2.video.databinding.FragmentPreviewBinding
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.suspendCancellableCoroutine
-import java.io.File
-import java.text.SimpleDateFormat
-import java.util.Date
-import java.util.Locale
-import java.util.concurrent.Executor
-import java.util.concurrent.RejectedExecutionException
-import kotlin.coroutines.resume
-import kotlin.coroutines.resumeWithException
-import kotlin.coroutines.suspendCoroutine
-
-class PreviewFragment : Fragment() {
-
- private class HandlerExecutor(handler: Handler) : Executor {
- private val mHandler = handler
-
- override fun execute(command: Runnable) {
- if (!mHandler.post(command)) {
- throw RejectedExecutionException("" + mHandler + " is shutting down");
- }
- }
- }
-
- /** Android ViewBinding */
- private var _fragmentBinding: FragmentPreviewBinding? = null
-
- private val fragmentBinding get() = _fragmentBinding!!
-
- private val pipeline: Pipeline by lazy {
- if (args.useHardware) {
- HardwarePipeline(args.width, args.height, args.fps, args.filterOn, args.transfer,
- args.dynamicRange, characteristics, encoder, fragmentBinding.viewFinder)
- } else {
- SoftwarePipeline(args.width, args.height, args.fps, args.filterOn,
- args.dynamicRange, characteristics, encoder, fragmentBinding.viewFinder)
- }
- }
-
- /** AndroidX navigation arguments */
- private val args: PreviewFragmentArgs by navArgs()
-
- /** Host's navigation controller */
- private val navController: NavController by lazy {
- Navigation.findNavController(requireActivity(), R.id.fragment_container)
- }
-
- /** Detects, characterizes, and connects to a CameraDevice (used for all camera operations) */
- private val cameraManager: CameraManager by lazy {
- val context = requireContext().applicationContext
- context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
- }
-
- /** [CameraCharacteristics] corresponding to the provided Camera ID */
- private val characteristics: CameraCharacteristics by lazy {
- cameraManager.getCameraCharacteristics(args.cameraId)
- }
-
- /** File where the recording will be saved */
- private val outputFile: File by lazy { createFile(requireContext(), "mp4") }
-
- /**
- * Setup a [Surface] for the encoder
- */
- private val encoderSurface: Surface by lazy {
- encoder.getInputSurface()
- }
-
- /** [EncoderWrapper] utility class */
- private val encoder: EncoderWrapper by lazy { createEncoder() }
-
- /** [HandlerThread] where all camera operations run */
- private val cameraThread = HandlerThread("CameraThread").apply { start() }
-
- /** [Handler] corresponding to [cameraThread] */
- private val cameraHandler = Handler(cameraThread.looper)
-
- /** Captures frames from a [CameraDevice] for our video recording */
- private lateinit var session: CameraCaptureSession
-
- /** The [CameraDevice] that will be opened in this fragment */
- private lateinit var camera: CameraDevice
-
- /** Requests used for preview only in the [CameraCaptureSession] */
- private val previewRequest: CaptureRequest? by lazy {
- pipeline.createPreviewRequest(session, args.previewStabilization)
- }
-
- /** Requests used for preview and recording in the [CameraCaptureSession] */
- private val recordRequest: CaptureRequest by lazy {
- pipeline.createRecordRequest(session, args.previewStabilization)
- }
-
- private var recordingStartMillis: Long = 0L
-
- /** Orientation of the camera as 0, 90, 180, or 270 degrees */
- private val orientation: Int by lazy {
- characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!
- }
-
- @Volatile
- private var recordingStarted = false
-
- @Volatile
- private var recordingComplete = false
-
- /** Condition variable for blocking until the recording completes */
- private val cvRecordingStarted = ConditionVariable(false)
- private val cvRecordingComplete = ConditionVariable(false)
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- _fragmentBinding = FragmentPreviewBinding.inflate(inflater, container, false)
-
- val window = requireActivity().getWindow()
- if (args.dynamicRange != DynamicRangeProfiles.STANDARD) {
- if (window.getColorMode() != ActivityInfo.COLOR_MODE_HDR) {
- window.setColorMode(ActivityInfo.COLOR_MODE_HDR)
- }
- }
-
- return fragmentBinding.root
- }
-
- @SuppressLint("MissingPermission")
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- fragmentBinding.viewFinder.holder.addCallback(object : SurfaceHolder.Callback {
- override fun surfaceDestroyed(holder: SurfaceHolder) {
- pipeline.destroyWindowSurface()
- }
-
- override fun surfaceChanged(
- holder: SurfaceHolder,
- format: Int,
- width: Int,
- height: Int) = Unit
-
- override fun surfaceCreated(holder: SurfaceHolder) {
-
- // Selects appropriate preview size and configures view finder
- val previewSize = getPreviewOutputSize(
- fragmentBinding.viewFinder.display, characteristics, SurfaceHolder::class.java)
- Log.d(TAG, "View finder size: ${fragmentBinding.viewFinder.width} x ${fragmentBinding.viewFinder.height}")
- Log.d(TAG, "Selected preview size: $previewSize")
- fragmentBinding.viewFinder.setAspectRatio(previewSize.width, previewSize.height)
-
- pipeline.setPreviewSize(previewSize)
-
- // To ensure that size is set, initialize camera in the view's thread
- fragmentBinding.viewFinder.post {
- pipeline.createResources(holder.surface)
- initializeCamera()
- }
- }
- })
- }
-
- private fun isCurrentlyRecording(): Boolean {
- return recordingStarted && !recordingComplete
- }
-
- private fun createEncoder(): EncoderWrapper {
- var width = args.width
- var height = args.height
- var orientationHint = orientation
-
- if (args.useHardware) {
- if (orientation == 90 || orientation == 270) {
- width = args.height
- height = args.width
- }
- orientationHint = 0
- }
-
- return EncoderWrapper(width, height, RECORDER_VIDEO_BITRATE, args.fps,
- args.dynamicRange, orientationHint, outputFile, args.useMediaRecorder,
- args.videoCodec)
- }
-
- /**
- * Begin all camera operations in a coroutine in the main thread. This function:
- * - Opens the camera
- * - Configures the camera session
- * - Starts the preview by dispatching a repeating request
- */
- @SuppressLint("ClickableViewAccessibility")
- private fun initializeCamera() = lifecycleScope.launch(Dispatchers.Main) {
-
- // Open the selected camera
- camera = openCamera(cameraManager, args.cameraId, cameraHandler)
-
- // Creates list of Surfaces where the camera will output frames
- val previewTargets = pipeline.getPreviewTargets()
-
- // Start a capture session using our open camera and list of Surfaces where frames will go
- session = createCaptureSession(camera, previewTargets, cameraHandler,
- recordingCompleteOnClose = (pipeline !is SoftwarePipeline))
-
- // Sends the capture request as frequently as possible until the session is torn down or
- // session.stopRepeating() is called
- if (previewRequest == null) {
- session.setRepeatingRequest(recordRequest, null, cameraHandler)
- } else {
- session.setRepeatingRequest(previewRequest!!, null, cameraHandler)
- }
-
- // React to user touching the capture button
- fragmentBinding.captureButton.setOnTouchListener { view, event ->
- when (event.action) {
-
- MotionEvent.ACTION_DOWN -> lifecycleScope.launch(Dispatchers.IO) {
- /* If the recording was already started in the past, do nothing. */
- if (!recordingStarted) {
- // Prevents screen rotation during the video recording
- requireActivity().requestedOrientation =
- ActivityInfo.SCREEN_ORIENTATION_LOCKED
-
- pipeline.actionDown(encoderSurface)
-
- // Finalizes encoder setup and starts recording
- recordingStarted = true
- encoder.start()
- cvRecordingStarted.open()
- pipeline.startRecording()
-
- // Start recording repeating requests, which will stop the ongoing preview
- // repeating requests without having to explicitly call
- // `session.stopRepeating`
- if (previewRequest != null) {
- val recordTargets = pipeline.getRecordTargets()
-
- session.close()
- session = createCaptureSession(camera, recordTargets, cameraHandler,
- recordingCompleteOnClose = true)
-
- session.setRepeatingRequest(recordRequest,
- object : CameraCaptureSession.CaptureCallback() {
- override fun onCaptureCompleted(session: CameraCaptureSession,
- request: CaptureRequest,
- result: TotalCaptureResult) {
- if (isCurrentlyRecording()) {
- encoder.frameAvailable()
- }
- }
- }, cameraHandler)
- }
-
- recordingStartMillis = System.currentTimeMillis()
- Log.d(TAG, "Recording started")
-
- // Set color to RED and show timer when recording begins
- fragmentBinding.captureButton.post {
- fragmentBinding.captureButton.background =
- context?.let {
- ContextCompat.getDrawable(it,
- R.drawable.ic_shutter_pressed)
- }
- fragmentBinding.captureTimer?.visibility = View.VISIBLE
- fragmentBinding.captureTimer?.start()
- }
- }
- }
-
- MotionEvent.ACTION_UP -> lifecycleScope.launch(Dispatchers.IO) {
- cvRecordingStarted.block()
-
- /* Wait for at least one frame to process so we don't have an empty video */
- encoder.waitForFirstFrame()
-
- session.stopRepeating()
- session.close()
-
- pipeline.clearFrameListener()
- fragmentBinding.captureButton.setOnTouchListener(null)
-
- // Set color to GRAY and hide timer when recording stops
- fragmentBinding.captureButton.post {
- fragmentBinding.captureButton.background =
- context?.let {
- ContextCompat.getDrawable(it,
- R.drawable.ic_shutter_normal)
- }
- fragmentBinding.captureTimer?.visibility = View.GONE
- fragmentBinding.captureTimer?.stop()
- }
-
- /* Wait until the session signals onReady */
- cvRecordingComplete.block()
-
- // Unlocks screen rotation after recording finished
- requireActivity().requestedOrientation =
- ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
-
- // Requires recording of at least MIN_REQUIRED_RECORDING_TIME_MILLIS
- val elapsedTimeMillis = System.currentTimeMillis() - recordingStartMillis
- if (elapsedTimeMillis < MIN_REQUIRED_RECORDING_TIME_MILLIS) {
- delay(MIN_REQUIRED_RECORDING_TIME_MILLIS - elapsedTimeMillis)
- }
-
- delay(CameraActivity.ANIMATION_SLOW_MILLIS)
-
- pipeline.cleanup()
-
- Log.d(TAG, "Recording stopped. Output file: $outputFile")
-
- if (encoder.shutdown()) {
- // Broadcasts the media file to the rest of the system
- MediaScannerConnection.scanFile(
- requireView().context, arrayOf(outputFile.absolutePath), null, null)
-
- if (outputFile.exists()) {
- // Launch external activity via intent to play video recorded using our provider
- startActivity(Intent().apply {
- action = Intent.ACTION_VIEW
- type = MimeTypeMap.getSingleton()
- .getMimeTypeFromExtension(outputFile.extension)
- val authority = "${BuildConfig.APPLICATION_ID}.provider"
- data = FileProvider.getUriForFile(view.context, authority, outputFile)
- flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
- Intent.FLAG_ACTIVITY_CLEAR_TOP
- })
- } else {
- // TODO:
- // 1. Move the callback to ACTION_DOWN, activating it on the second press
- // 2. Add an animation to the button before the user can press it again
- Handler(Looper.getMainLooper()).post {
- Toast.makeText(activity, R.string.error_file_not_found,
- Toast.LENGTH_LONG).show()
- }
- }
- } else {
- Handler(Looper.getMainLooper()).post {
- Toast.makeText(activity, R.string.recorder_shutdown_error,
- Toast.LENGTH_LONG).show()
- }
- }
- Handler(Looper.getMainLooper()).post {
- navController.popBackStack()
- }
- }
- }
-
- true
- }
- }
-
- /** Opens the camera and returns the opened device (as the result of the suspend coroutine) */
- @SuppressLint("MissingPermission")
- private suspend fun openCamera(
- manager: CameraManager,
- cameraId: String,
- handler: Handler? = null
- ): CameraDevice = suspendCancellableCoroutine { cont ->
- manager.openCamera(cameraId, object : CameraDevice.StateCallback() {
- override fun onOpened(device: CameraDevice) = cont.resume(device)
-
- override fun onDisconnected(device: CameraDevice) {
- Log.w(TAG, "Camera $cameraId has been disconnected")
- requireActivity().finish()
- }
-
- override fun onError(device: CameraDevice, error: Int) {
- val msg = when(error) {
- ERROR_CAMERA_DEVICE -> "Fatal (device)"
- ERROR_CAMERA_DISABLED -> "Device policy"
- ERROR_CAMERA_IN_USE -> "Camera in use"
- ERROR_CAMERA_SERVICE -> "Fatal (service)"
- ERROR_MAX_CAMERAS_IN_USE -> "Maximum cameras in use"
- else -> "Unknown"
- }
- val exc = RuntimeException("Camera $cameraId error: ($error) $msg")
- Log.e(TAG, exc.message, exc)
- if (cont.isActive) cont.resumeWithException(exc)
- }
- }, handler)
- }
-
- /**
- * Creates a [CameraCaptureSession] with the dynamic range profile set.
- */
- private fun setupSessionWithDynamicRangeProfile(
- device: CameraDevice,
- targets: List,
- handler: Handler,
- stateCallback: CameraCaptureSession.StateCallback
- ): Boolean {
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
- val outputConfigs = mutableListOf()
- for (target in targets) {
- val outputConfig = OutputConfiguration(target)
- outputConfig.setDynamicRangeProfile(args.dynamicRange)
- outputConfigs.add(outputConfig)
- }
-
- val sessionConfig = SessionConfiguration(SessionConfiguration.SESSION_REGULAR,
- outputConfigs, HandlerExecutor(handler), stateCallback)
- if (android.os.Build.VERSION.SDK_INT >=
- android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
- && args.colorSpace != ColorSpaceProfiles.UNSPECIFIED) {
- sessionConfig.setColorSpace(ColorSpace.Named.values()[args.colorSpace])
- }
- device.createCaptureSession(sessionConfig)
- return true
- } else {
- device.createCaptureSession(targets, stateCallback, handler)
- return false
- }
- }
-
- /**
- * Creates a [CameraCaptureSession] and returns the configured session (as the result of the
- * suspend coroutine)
- */
- private suspend fun createCaptureSession(
- device: CameraDevice,
- targets: List,
- handler: Handler,
- recordingCompleteOnClose: Boolean
- ): CameraCaptureSession = suspendCoroutine { cont ->
- val stateCallback = object: CameraCaptureSession.StateCallback() {
- override fun onConfigured(session: CameraCaptureSession) = cont.resume(session)
-
- override fun onConfigureFailed(session: CameraCaptureSession) {
- val exc = RuntimeException("Camera ${device.id} session configuration failed")
- Log.e(TAG, exc.message, exc)
- cont.resumeWithException(exc)
- }
-
- /** Called after all captures have completed - shut down the encoder */
- override fun onClosed(session: CameraCaptureSession) {
- if (!recordingCompleteOnClose or !isCurrentlyRecording()) {
- return
- }
-
- recordingComplete = true
- pipeline.stopRecording()
- cvRecordingComplete.open()
- }
- }
-
- setupSessionWithDynamicRangeProfile(device, targets, handler, stateCallback)
- }
-
- override fun onStop() {
- super.onStop()
- try {
- camera.close()
- } catch (exc: Throwable) {
- Log.e(TAG, "Error closing camera", exc)
- }
- }
-
- override fun onDestroy() {
- super.onDestroy()
- pipeline.clearFrameListener()
- pipeline.cleanup()
- cameraThread.quitSafely()
- encoderSurface.release()
- }
-
- override fun onDestroyView() {
- _fragmentBinding = null
- super.onDestroyView()
- }
-
- companion object {
- private val TAG = PreviewFragment::class.java.simpleName
-
- private const val RECORDER_VIDEO_BITRATE: Int = 10_000_000
- private const val MIN_REQUIRED_RECORDING_TIME_MILLIS: Long = 1000L
-
- /** Creates a [File] named with the current date and time */
- private fun createFile(context: Context, extension: String): File {
- val sdf = SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS", Locale.US)
- return File(context.filesDir, "VID_${sdf.format(Date())}.$extension")
- }
- }
-}
diff --git a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/PreviewStabilizationFragment.kt b/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/PreviewStabilizationFragment.kt
deleted file mode 100644
index 07a07e44..00000000
--- a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/PreviewStabilizationFragment.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2023 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
- *
- * https://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.android.camera2.video.fragments
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.hardware.camera2.params.DynamicRangeProfiles
-import android.os.Bundle
-import android.util.Size
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.fragment.app.Fragment
-import androidx.navigation.Navigation
-import androidx.navigation.fragment.navArgs
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import com.example.android.camera.utils.GenericListAdapter
-import com.example.android.camera2.video.R
-
-/**
- * In this [Fragment] we let users pick whether or not preview stabilization is on.
- */
-class PreviewStabilizationFragment : Fragment() {
-
- private val args: PreviewStabilizationFragmentArgs by navArgs()
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? = RecyclerView(requireContext())
-
- @SuppressLint("MissingPermission")
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- view as RecyclerView
- view.apply {
- layoutManager = LinearLayoutManager(requireContext())
- val modeList = enumerateModes()
- val layoutId = android.R.layout.simple_list_item_1
- adapter = GenericListAdapter(modeList, itemLayoutId = layoutId) { view, item, _ ->
- view.findViewById(android.R.id.text1).text = item.name
- view.setOnClickListener {
- navigate(item.value)
- }
- }
- }
- }
-
- private fun navigate(stabilizationOn: Boolean) {
- val navController =
- Navigation.findNavController(requireActivity(), R.id.fragment_container)
-
- navController.navigate(
- PreviewStabilizationFragmentDirections.actionPreviewStabilizationToEncodeApi(
- args.cameraId, args.width, args.height, args.fps,
- args.dynamicRange, args.colorSpace, stabilizationOn))
- }
-
- companion object {
- private data class ModeInfo(
- val name: String,
- val value: Boolean)
-
- @SuppressLint("InlinedApi")
- private fun enumerateModes(): List {
- val modeList: MutableList = mutableListOf()
-
- modeList.add(ModeInfo("Stabilization On", true))
- modeList.add(ModeInfo("Stabilization Off", false))
-
- return modeList
- }
- }
-}
diff --git a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/RecordModeFragment.kt b/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/RecordModeFragment.kt
deleted file mode 100644
index 9baf79f0..00000000
--- a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/RecordModeFragment.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2023 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
- *
- * https://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.android.camera2.video.fragments
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.hardware.camera2.CameraCharacteristics
-import android.hardware.camera2.CameraManager
-import android.hardware.camera2.params.DynamicRangeProfiles
-import android.os.Bundle
-import android.util.Size
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.fragment.app.Fragment
-import androidx.navigation.Navigation
-import androidx.navigation.fragment.navArgs
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import com.example.android.camera.utils.GenericListAdapter
-import com.example.android.camera2.video.R
-
-/**
- * In this [Fragment] we let users pick how recording is processed (via two separate camera streams
- * or one TEMPLATE_PREVIEW stream).
- */
-class RecordModeFragment : Fragment() {
-
- private val args: RecordModeFragmentArgs by navArgs()
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? = RecyclerView(requireContext())
-
- @SuppressLint("MissingPermission")
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- view as RecyclerView
- view.apply {
- layoutManager = LinearLayoutManager(requireContext())
- val recordModeList = enumerateRecordModes()
- val layoutId = android.R.layout.simple_list_item_1
- adapter = GenericListAdapter(recordModeList, itemLayoutId = layoutId) { view, item, _ ->
- view.findViewById(android.R.id.text1).text = item.name
- view.setOnClickListener {
- val navController =
- Navigation.findNavController(requireActivity(), R.id.fragment_container)
- if (item.value) {
- navController.navigate(
- RecordModeFragmentDirections.actionRecordModeToFilter(
- args.cameraId, args.width, args.height, args.fps,
- args.dynamicRange, args.colorSpace, args.previewStabilization,
- args.useMediaRecorder, args.videoCodec))
- } else {
- navController.navigate(
- RecordModeFragmentDirections.actionRecordModeToPreview(
- args.cameraId, args.width, args.height, args.fps,
- args.dynamicRange, args.colorSpace, args.previewStabilization,
- args.useMediaRecorder, args.videoCodec, false, false, 0))
- }
- }
- }
- }
- }
-
- companion object {
- private data class RecordModeInfo(
- val name: String,
- val value: Boolean)
-
- @SuppressLint("InlinedApi")
- private fun enumerateRecordModes(): List {
- val recordModeList: MutableList = mutableListOf()
-
- recordModeList.add(RecordModeInfo("Multi-stream", false))
- recordModeList.add(RecordModeInfo("Single-stream", true))
-
- return recordModeList
- }
- }
-}
diff --git a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/SelectorFragment.kt b/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/SelectorFragment.kt
deleted file mode 100644
index f4048987..00000000
--- a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/SelectorFragment.kt
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright 2023 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
- *
- * https://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.android.camera2.video.fragments
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.hardware.camera2.CameraCharacteristics
-import android.hardware.camera2.CameraManager
-import android.hardware.camera2.CameraMetadata
-import android.hardware.camera2.params.ColorSpaceProfiles
-import android.hardware.camera2.params.DynamicRangeProfiles
-import android.media.MediaRecorder
-import android.os.Bundle
-import android.util.Size
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.fragment.app.Fragment
-import androidx.navigation.Navigation
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import com.example.android.camera.utils.GenericListAdapter
-import com.example.android.camera2.video.R
-
-/**
- * In this [Fragment] we let users pick a camera, size and FPS to use for high
- * speed video recording
- */
-class SelectorFragment : Fragment() {
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? = RecyclerView(requireContext())
-
- @SuppressLint("MissingPermission")
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- view as RecyclerView
- view.apply {
- layoutManager = LinearLayoutManager(requireContext())
-
- val cameraManager =
- requireContext().getSystemService(Context.CAMERA_SERVICE) as CameraManager
-
- val cameraList = enumerateVideoCameras(cameraManager)
-
- val layoutId = android.R.layout.simple_list_item_1
- adapter = GenericListAdapter(cameraList, itemLayoutId = layoutId) { view, item, _ ->
- view.findViewById(android.R.id.text1).text = item.name
- view.setOnClickListener {
- var dynamicRangeProfiles: DynamicRangeProfiles? = null
- var colorSpaceProfiles: ColorSpaceProfiles? = null
- var supportsPreviewStabilization = false
- val characteristics = cameraManager.getCameraCharacteristics(item.cameraId)
-
- // DynamicRangeProfiles is introduced in android Tiramisu. If the SDK residing on
- // our device is older, do not call the non-existant paths.
- if (android.os.Build.VERSION.SDK_INT >=
- android.os.Build.VERSION_CODES.TIRAMISU) {
- val capabilities = characteristics.get(
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)!!
- if (capabilities.contains(CameraCharacteristics
- .REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT)) {
- dynamicRangeProfiles = characteristics.get(
- CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES)
- }
-
- val previewStabilizationModes = characteristics.get(
- CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES)!!
- supportsPreviewStabilization = previewStabilizationModes.contains(
- CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION)
- }
-
- if (android.os.Build.VERSION.SDK_INT >=
- android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
- colorSpaceProfiles = characteristics.get(
- CameraCharacteristics.REQUEST_AVAILABLE_COLOR_SPACE_PROFILES)
- }
-
- val navController =
- Navigation.findNavController(requireActivity(), R.id.fragment_container)
-
- // If possible, navigate to a second selector for picking a dynamic range.
- // Otherwise continue on to video recording.
- if (dynamicRangeProfiles != null) {
- navController.navigate(
- SelectorFragmentDirections.actionSelectorToDynamicRange(
- item.cameraId, item.size.width, item.size.height, item.fps))
- } else if (colorSpaceProfiles != null) {
- navController.navigate(
- SelectorFragmentDirections.actionSelectorToColorSpace(
- item.cameraId, item.size.width, item.size.height, item.fps,
- DynamicRangeProfiles.STANDARD))
- } else if (supportsPreviewStabilization) {
- navController.navigate(
- SelectorFragmentDirections.actionSelectorToPreviewStabilization(
- item.cameraId, item.size.width, item.size.height, item.fps,
- DynamicRangeProfiles.STANDARD, ColorSpaceProfiles.UNSPECIFIED))
- } else {
- navController.navigate(
- SelectorFragmentDirections.actionSelectorToEncodeApi(
- item.cameraId, item.size.width, item.size.height, item.fps,
- DynamicRangeProfiles.STANDARD, ColorSpaceProfiles.UNSPECIFIED,
- /*previewStabilization*/ false))
- }
- }
- }
- }
- }
-
- companion object {
-
- private data class CameraInfo(
- val name: String,
- val cameraId: String,
- val size: Size,
- val fps: Int)
-
- /** Converts a lens orientation enum into a human-readable string */
- private fun lensOrientationString(value: Int) = when (value) {
- CameraCharacteristics.LENS_FACING_BACK -> "Back"
- CameraCharacteristics.LENS_FACING_FRONT -> "Front"
- CameraCharacteristics.LENS_FACING_EXTERNAL -> "External"
- else -> "Unknown"
- }
-
- /** Lists all video-capable cameras and supported resolution and FPS combinations */
- @SuppressLint("InlinedApi")
- private fun enumerateVideoCameras(cameraManager: CameraManager): List {
- val availableCameras: MutableList = mutableListOf()
-
- // Iterate over the list of cameras and add those with high speed video recording
- // capability to our output. This function only returns those cameras that declare
- // constrained high speed video recording, but some cameras may be capable of doing
- // unconstrained video recording with high enough FPS for some use cases and they will
- // not necessarily declare constrained high speed video capability.
- cameraManager.cameraIdList.forEach { id ->
- val characteristics = cameraManager.getCameraCharacteristics(id)
- val orientation = lensOrientationString(
- characteristics.get(CameraCharacteristics.LENS_FACING)!!)
-
- // Query the available capabilities and output formats
- val capabilities = characteristics.get(
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)!!
- val cameraConfig = characteristics.get(
- CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
-
- // Return cameras that declare to be backward compatible
- if (capabilities.contains(CameraCharacteristics
- .REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE)) {
- // Recording should always be done in the most efficient format, which is
- // the format native to the camera framework
- val targetClass = MediaRecorder::class.java
-
- // For each size, list the expected FPS
- cameraConfig.getOutputSizes(targetClass).forEach { size ->
- // Get the number of seconds that each frame will take to process
- val secondsPerFrame =
- cameraConfig.getOutputMinFrameDuration(targetClass, size) /
- 1_000_000_000.0
- // Compute the frames per second to let user select a configuration
- val fps = if (secondsPerFrame > 0) (1.0 / secondsPerFrame).toInt() else 0
- val fpsLabel = if (fps > 0) "$fps" else "N/A"
- availableCameras.add(CameraInfo(
- "$orientation ($id) $size $fpsLabel FPS", id, size, fps))
- }
- }
- }
-
- return availableCameras
- }
- }
-}
diff --git a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/TransferFragment.kt b/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/TransferFragment.kt
deleted file mode 100644
index 7dc4950a..00000000
--- a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/TransferFragment.kt
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2023 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
- *
- * https://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.android.camera2.video.fragments
-
-import android.annotation.SuppressLint
-import android.os.Build
-import android.os.Bundle
-import android.util.Size
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.fragment.app.Fragment
-import androidx.navigation.Navigation
-import androidx.navigation.fragment.navArgs
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import com.example.android.camera.utils.GenericListAdapter
-import com.example.android.camera2.video.R
-
-/**
- * In this [Fragment] we let users pick a transfer curve for the preview output when using the
- * hardware pipeline with HDR.
- */
-class TransferFragment : Fragment() {
-
- private val args: TransferFragmentArgs by navArgs()
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? = RecyclerView(requireContext())
-
- @SuppressLint("MissingPermission")
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- view as RecyclerView
- view.apply {
- layoutManager = LinearLayoutManager(requireContext())
- val transferList = enumerateTransferCharacteristics()
-
- val layoutId = android.R.layout.simple_list_item_1
- adapter = GenericListAdapter(transferList, itemLayoutId = layoutId) { view, item, _ ->
- view.findViewById(android.R.id.text1).text =
- "Preview Transfer " + item.name
- view.setOnClickListener {
- val navController =
- Navigation.findNavController(requireActivity(), R.id.fragment_container)
- val direction = TransferFragmentDirections.actionTransferToPreview(
- args.cameraId, args.width, args.height, args.fps,
- args.dynamicRange, args.colorSpace, args.previewStabilization,
- args.useMediaRecorder, args.videoCodec, args.filterOn, true, item.id)
- navController.navigate(direction)
- }
- }
- }
- }
-
- companion object {
-
- private data class TransferInfo(
- val name: String,
- val id: Int)
-
- public val PQ_STR = "PQ"
- public val LINEAR_STR = "LINEAR"
- public val HLG_STR = "HLG (Android 14 or above)"
- public val HLG_WORKAROUND_STR = "HLG (Android 13)"
- public val PQ_ID: Int = 0
- public val LINEAR_ID: Int = 1
- public val HLG_ID: Int = 2
- public val HLG_WORKAROUND_ID: Int = 3
-
- public fun idToStr(transferId: Int): String = when (transferId) {
- PQ_ID -> PQ_STR
- LINEAR_ID -> LINEAR_STR
- HLG_ID -> HLG_STR
- HLG_WORKAROUND_ID -> HLG_WORKAROUND_STR
- else -> throw RuntimeException("Unexpected transferId " + transferId)
- }
-
- /** Lists all video-capable cameras and supported resolution and FPS combinations */
- @SuppressLint("InlinedApi")
- private fun enumerateTransferCharacteristics(): List {
- val transferCharacteristics: MutableList = mutableListOf()
- transferCharacteristics.add(TransferInfo(PQ_STR, PQ_ID))
- transferCharacteristics.add(TransferInfo(LINEAR_STR, LINEAR_ID))
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.TIRAMISU) {
- transferCharacteristics.add(TransferInfo(HLG_STR, HLG_ID))
- } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.TIRAMISU) {
- transferCharacteristics.add(TransferInfo(HLG_WORKAROUND_STR, HLG_WORKAROUND_ID))
- }
- return transferCharacteristics
- }
- }
-}
diff --git a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/VideoCodecFragment.kt b/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/VideoCodecFragment.kt
deleted file mode 100644
index 936c350a..00000000
--- a/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/VideoCodecFragment.kt
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright 2023 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
- *
- * https://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.android.camera2.video.fragments
-
-import android.annotation.SuppressLint
-import android.hardware.camera2.params.DynamicRangeProfiles
-import android.media.MediaCodecList
-import android.media.MediaFormat
-import android.os.Bundle
-import android.util.Size
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.fragment.app.Fragment
-import androidx.navigation.Navigation
-import androidx.navigation.fragment.navArgs
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import com.example.android.camera.utils.GenericListAdapter
-import com.example.android.camera2.video.R
-
-/**
- * In this [Fragment] we let users pick a video codec to use for the encoded video.
- */
-class VideoCodecFragment : Fragment() {
-
- private val args: VideoCodecFragmentArgs by navArgs()
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? = RecyclerView(requireContext())
-
- @SuppressLint("MissingPermission")
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- view as RecyclerView
- view.apply {
- layoutManager = LinearLayoutManager(requireContext())
- val videoCodecList = enumerateVideoCodecs(args.dynamicRange)
-
- val layoutId = android.R.layout.simple_list_item_1
- adapter = GenericListAdapter(videoCodecList, itemLayoutId = layoutId) { view, item, _ ->
- view.findViewById(android.R.id.text1).text = item.name
- view.setOnClickListener {
- val navController =
- Navigation.findNavController(requireActivity(), R.id.fragment_container)
- val direction = VideoCodecFragmentDirections.actionVideoCodecToRecordMode(
- args.cameraId, args.width, args.height, args.fps,
- args.dynamicRange, args.colorSpace, args.previewStabilization,
- args.useMediaRecorder, item.id)
- navController.navigate(direction)
- }
- }
- }
- }
-
- companion object {
-
- private data class VideoCodecInfo(
- val name: String,
- val id: Int)
-
- public const val VIDEO_CODEC_ID_HEVC: Int = 0
- public const val VIDEO_CODEC_ID_H264: Int = 1
- public const val VIDEO_CODEC_ID_AV1: Int = 2
-
- public fun idToStr(videoCodecId: Int): String = when (videoCodecId) {
- VIDEO_CODEC_ID_HEVC -> "HEVC"
- VIDEO_CODEC_ID_H264 -> "H264"
- VIDEO_CODEC_ID_AV1 -> "AV1"
- else -> throw RuntimeException("Unexpected video codec id " + videoCodecId)
- }
-
- public fun idToType(videoCodecId: Int): String = when (videoCodecId) {
- VIDEO_CODEC_ID_H264 -> MediaFormat.MIMETYPE_VIDEO_AVC
- VIDEO_CODEC_ID_HEVC -> MediaFormat.MIMETYPE_VIDEO_HEVC
- VIDEO_CODEC_ID_AV1 -> MediaFormat.MIMETYPE_VIDEO_AV1
- else -> throw RuntimeException("Unexpected video codec id " + videoCodecId)
- }
-
- /** Lists all video-capable cameras and supported resolution and FPS combinations */
- @SuppressLint("InlinedApi")
- private fun enumerateVideoCodecs(dynamicRange: Long): List {
- val videoCodecIdList = when {
- dynamicRange == DynamicRangeProfiles.STANDARD -> listOf(VIDEO_CODEC_ID_H264)
- dynamicRange < DynamicRangeProfiles.PUBLIC_MAX ->
- listOf(VIDEO_CODEC_ID_HEVC, VIDEO_CODEC_ID_AV1)
- else -> throw RuntimeException("Unrecognized dynamic range $dynamicRange")
- }
-
- val supportedVideoCodecIdSet = mutableSetOf()
- val mediaCodecList = MediaCodecList(MediaCodecList.REGULAR_CODECS)
- for (codecInfo in mediaCodecList.getCodecInfos()) {
- if (!codecInfo.isEncoder()) {
- continue
- }
-
- val types = codecInfo.getSupportedTypes()
- for (type in types) {
- for (videoCodecId in videoCodecIdList) {
- if (type.equals(idToType(videoCodecId), ignoreCase = true)) {
- supportedVideoCodecIdSet.add(videoCodecId)
- }
- }
- }
- }
-
- val videoCodecList: MutableList = mutableListOf()
- for (id in supportedVideoCodecIdSet) {
- videoCodecList.add(VideoCodecInfo(idToStr(id), id))
- }
- return videoCodecList
- }
- }
-}
diff --git a/Camera2Video/app/src/main/res/drawable-hdpi/ic_launcher.png b/Camera2Video/app/src/main/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index bba1165c..00000000
Binary files a/Camera2Video/app/src/main/res/drawable-hdpi/ic_launcher.png and /dev/null differ
diff --git a/Camera2Video/app/src/main/res/drawable-mdpi/ic_launcher.png b/Camera2Video/app/src/main/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 43045910..00000000
Binary files a/Camera2Video/app/src/main/res/drawable-mdpi/ic_launcher.png and /dev/null differ
diff --git a/Camera2Video/app/src/main/res/drawable-xhdpi/ic_launcher.png b/Camera2Video/app/src/main/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 80c5ebaa..00000000
Binary files a/Camera2Video/app/src/main/res/drawable-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/Camera2Video/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/Camera2Video/app/src/main/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index 9baac9b6..00000000
Binary files a/Camera2Video/app/src/main/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/Camera2Video/app/src/main/res/layout-land/fragment_preview.xml b/Camera2Video/app/src/main/res/layout-land/fragment_preview.xml
deleted file mode 100644
index 1656daf7..00000000
--- a/Camera2Video/app/src/main/res/layout-land/fragment_preview.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Camera2Video/app/src/main/res/layout/activity_camera.xml b/Camera2Video/app/src/main/res/layout/activity_camera.xml
deleted file mode 100644
index 6c62bcf7..00000000
--- a/Camera2Video/app/src/main/res/layout/activity_camera.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
diff --git a/Camera2Video/app/src/main/res/layout/fragment_preview.xml b/Camera2Video/app/src/main/res/layout/fragment_preview.xml
deleted file mode 100644
index be24397f..00000000
--- a/Camera2Video/app/src/main/res/layout/fragment_preview.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Camera2Video/app/src/main/res/navigation/nav_graph.xml b/Camera2Video/app/src/main/res/navigation/nav_graph.xml
deleted file mode 100644
index 17099bbf..00000000
--- a/Camera2Video/app/src/main/res/navigation/nav_graph.xml
+++ /dev/null
@@ -1,483 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Camera2Video/app/src/main/res/values/strings.xml b/Camera2Video/app/src/main/res/values/strings.xml
deleted file mode 100644
index 0496f05c..00000000
--- a/Camera2Video/app/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
- Camera2Video
- Capture
- Recorded file not found
- Recording stops too fast, please press longer
-
diff --git a/Camera2Video/app/src/main/res/values/styles.xml b/Camera2Video/app/src/main/res/values/styles.xml
deleted file mode 100644
index b9b5162e..00000000
--- a/Camera2Video/app/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/Camera2Video/app/src/main/res/xml/file_paths.xml b/Camera2Video/app/src/main/res/xml/file_paths.xml
deleted file mode 100644
index 3b4bcebd..00000000
--- a/Camera2Video/app/src/main/res/xml/file_paths.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/Camera2Video/build.gradle b/Camera2Video/build.gradle
deleted file mode 100644
index 1843a459..00000000
--- a/Camera2Video/build.gradle
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.
- */
-
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
-
-buildscript {
- // Top-level variables used for versioning
- ext.kotlin_version = '1.8.0'
- ext.java_version = JavaVersion.VERSION_1_8
-
- repositories {
- google()
- mavenCentral()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:7.4.1'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5"
-
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
- }
-}
-
-allprojects {
- repositories {
- mavenLocal()
- google()
- mavenCentral()
- }
-}
-
-task clean(type: Delete) {
- delete rootProject.buildDir
-}
diff --git a/Camera2Video/gradle.properties b/Camera2Video/gradle.properties
deleted file mode 100644
index b448ae14..00000000
--- a/Camera2Video/gradle.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-
-# Project-wide Gradle settings.
-
-# IDE (e.g. Android Studio) users:
-# Settings specified in this file will override any Gradle settings
-# configured through the IDE.
-
-# For more details on how to configure your build environment visit
-# http://www.gradle.org/docs/current/userguide/build_environment.html
-
-# Specifies the JVM arguments used for the daemon process.
-# The setting is particularly useful for tweaking memory settings.
-# Default value: -Xmx10248m -XX:MaxPermSize=256m
-# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
-org.gradle.jvmargs=-Xmx1536m
-
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
-
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
-
-android.enableJetifier=true
-android.useAndroidX=true
diff --git a/Camera2Video/gradle/wrapper/gradle-wrapper.jar b/Camera2Video/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 94336fca..00000000
Binary files a/Camera2Video/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/Camera2Video/gradle/wrapper/gradle-wrapper.properties b/Camera2Video/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index e8be3262..00000000
--- a/Camera2Video/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-#Wed Mar 31 21:04:32 PDT 2021
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
diff --git a/Camera2Video/gradlew b/Camera2Video/gradlew
deleted file mode 100755
index cccdd3d5..00000000
--- a/Camera2Video/gradlew
+++ /dev/null
@@ -1,172 +0,0 @@
-#!/usr/bin/env sh
-
-##############################################################################
-##
-## Gradle start up script for UN*X
-##
-##############################################################################
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn () {
- echo "$*"
-}
-
-die () {
- echo
- echo "$*"
- echo
- exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
- else
- JAVACMD="$JAVA_HOME/bin/java"
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
- fi
- i=$((i+1))
- done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
-fi
-
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=$(save "$@")
-
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-
-# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
-if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
- cd "$(dirname "$0")"
-fi
-
-exec "$JAVACMD" "$@"
diff --git a/Camera2Video/gradlew.bat b/Camera2Video/gradlew.bat
deleted file mode 100644
index e95643d6..00000000
--- a/Camera2Video/gradlew.bat
+++ /dev/null
@@ -1,84 +0,0 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/Camera2Video/screenshots/icon-web.png b/Camera2Video/screenshots/icon-web.png
deleted file mode 100644
index d9bd4c48..00000000
Binary files a/Camera2Video/screenshots/icon-web.png and /dev/null differ
diff --git a/Camera2Video/screenshots/main.png b/Camera2Video/screenshots/main.png
deleted file mode 100644
index 074b3036..00000000
Binary files a/Camera2Video/screenshots/main.png and /dev/null differ
diff --git a/Camera2Video/settings.gradle b/Camera2Video/settings.gradle
deleted file mode 100644
index 663c0482..00000000
--- a/Camera2Video/settings.gradle
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.
- */
-
-include 'app'
-include 'utils'
diff --git a/Camera2Video/utils/.gitignore b/Camera2Video/utils/.gitignore
deleted file mode 100644
index 38c58757..00000000
--- a/Camera2Video/utils/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/build
-.idea
\ No newline at end of file
diff --git a/Camera2Video/utils/README.md b/Camera2Video/utils/README.md
deleted file mode 100644
index 7a10722f..00000000
--- a/Camera2Video/utils/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-Do not modify code under this folder outside of `CameraUtils`, it is copied
-automatically by `.github/scripts/copy_utils.sh`.
\ No newline at end of file
diff --git a/Camera2Video/utils/build.gradle b/Camera2Video/utils/build.gradle
deleted file mode 100644
index 71724623..00000000
--- a/Camera2Video/utils/build.gradle
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.
- */
-
-apply plugin: 'com.android.library'
-apply plugin: 'kotlin-android'
-
-android {
- compileSdkVersion 33
-
- defaultConfig {
- minSdkVersion 21
- targetSdkVersion 33
- versionCode 1
- versionName "1.0"
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles 'consumer-rules.pro'
- }
-
- compileOptions {
- sourceCompatibility rootProject.ext.java_version
- targetCompatibility rootProject.ext.java_version
- }
-
- kotlinOptions {
- jvmTarget = "$rootProject.ext.java_version"
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
-}
-
-dependencies {
-
- // Kotlin lang
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.4'
-
- // App compat and UI things
- implementation 'androidx.appcompat:appcompat:1.1.0'
- implementation 'androidx.recyclerview:recyclerview:1.1.0'
-
- // EXIF Interface
- implementation 'androidx.exifinterface:exifinterface:1.2.0'
-}
diff --git a/Camera2Video/utils/src/main/AndroidManifest.xml b/Camera2Video/utils/src/main/AndroidManifest.xml
deleted file mode 100644
index 2e13c37a..00000000
--- a/Camera2Video/utils/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
diff --git a/Camera2Video/utils/src/main/java/com/example/android/camera/utils/AutoFitSurfaceView.kt b/Camera2Video/utils/src/main/java/com/example/android/camera/utils/AutoFitSurfaceView.kt
deleted file mode 100644
index 3d900d19..00000000
--- a/Camera2Video/utils/src/main/java/com/example/android/camera/utils/AutoFitSurfaceView.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.content.Context
-import android.util.AttributeSet
-import android.util.Log
-import android.view.SurfaceView
-import kotlin.math.roundToInt
-
-/**
- * A [SurfaceView] that can be adjusted to a specified aspect ratio and
- * performs center-crop transformation of input frames.
- */
-class AutoFitSurfaceView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyle: Int = 0
-) : SurfaceView(context, attrs, defStyle) {
-
- private var aspectRatio = 0f
-
- /**
- * Sets the aspect ratio for this view. The size of the view will be
- * measured based on the ratio calculated from the parameters.
- *
- * @param width Camera resolution horizontal size
- * @param height Camera resolution vertical size
- */
- fun setAspectRatio(width: Int, height: Int) {
- require(width > 0 && height > 0) { "Size cannot be negative" }
- aspectRatio = width.toFloat() / height.toFloat()
- holder.setFixedSize(width, height)
- requestLayout()
- }
-
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec)
- val width = MeasureSpec.getSize(widthMeasureSpec)
- val height = MeasureSpec.getSize(heightMeasureSpec)
- if (aspectRatio == 0f) {
- setMeasuredDimension(width, height)
- } else {
-
- // Performs center-crop transformation of the camera frames
- val newWidth: Int
- val newHeight: Int
- val actualRatio = if (width > height) aspectRatio else 1f / aspectRatio
- if (width < height * actualRatio) {
- newHeight = height
- newWidth = (height * actualRatio).roundToInt()
- } else {
- newWidth = width
- newHeight = (width / actualRatio).roundToInt()
- }
-
- Log.d(TAG, "Measured dimensions set: $newWidth x $newHeight")
- setMeasuredDimension(newWidth, newHeight)
- }
- }
-
- companion object {
- private val TAG = AutoFitSurfaceView::class.java.simpleName
- }
-}
diff --git a/Camera2Video/utils/src/main/java/com/example/android/camera/utils/CameraSizes.kt b/Camera2Video/utils/src/main/java/com/example/android/camera/utils/CameraSizes.kt
deleted file mode 100644
index 6db01d31..00000000
--- a/Camera2Video/utils/src/main/java/com/example/android/camera/utils/CameraSizes.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.graphics.Point
-import android.hardware.camera2.CameraCharacteristics
-import android.hardware.camera2.params.StreamConfigurationMap
-import android.util.Size
-import android.view.Display
-import kotlin.math.max
-import kotlin.math.min
-
-/** Helper class used to pre-compute shortest and longest sides of a [Size] */
-class SmartSize(width: Int, height: Int) {
- var size = Size(width, height)
- var long = max(size.width, size.height)
- var short = min(size.width, size.height)
- override fun toString() = "SmartSize(${long}x${short})"
-}
-
-/** Standard High Definition size for pictures and video */
-val SIZE_1080P: SmartSize = SmartSize(1920, 1080)
-
-/** Returns a [SmartSize] object for the given [Display] */
-fun getDisplaySmartSize(display: Display): SmartSize {
- val outPoint = Point()
- display.getRealSize(outPoint)
- return SmartSize(outPoint.x, outPoint.y)
-}
-
-/**
- * Returns the largest available PREVIEW size. For more information, see:
- * https://d.android.com/reference/android/hardware/camera2/CameraDevice and
- * https://developer.android.com/reference/android/hardware/camera2/params/StreamConfigurationMap
- */
-fun getPreviewOutputSize(
- display: Display,
- characteristics: CameraCharacteristics,
- targetClass: Class,
- format: Int? = null
-): Size {
-
- // Find which is smaller: screen or 1080p
- val screenSize = getDisplaySmartSize(display)
- val hdScreen = screenSize.long >= SIZE_1080P.long || screenSize.short >= SIZE_1080P.short
- val maxSize = if (hdScreen) SIZE_1080P else screenSize
-
- // If image format is provided, use it to determine supported sizes; else use target class
- val config = characteristics.get(
- CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
- if (format == null)
- assert(StreamConfigurationMap.isOutputSupportedFor(targetClass))
- else
- assert(config.isOutputSupportedFor(format))
- val allSizes = if (format == null)
- config.getOutputSizes(targetClass) else config.getOutputSizes(format)
-
- // Get available sizes and sort them by area from largest to smallest
- val validSizes = allSizes
- .sortedWith(compareBy { it.height * it.width })
- .map { SmartSize(it.width, it.height) }.reversed()
-
- // Then, get the largest output size that is smaller or equal than our max size
- return validSizes.first { it.long <= maxSize.long && it.short <= maxSize.short }.size
-}
\ No newline at end of file
diff --git a/Camera2Video/utils/src/main/java/com/example/android/camera/utils/ExifUtils.kt b/Camera2Video/utils/src/main/java/com/example/android/camera/utils/ExifUtils.kt
deleted file mode 100644
index 561c14b3..00000000
--- a/Camera2Video/utils/src/main/java/com/example/android/camera/utils/ExifUtils.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.graphics.Bitmap
-import android.graphics.Matrix
-import android.util.Log
-import androidx.exifinterface.media.ExifInterface
-
-private const val TAG: String = "ExifUtils"
-
-/** Transforms rotation and mirroring information into one of the [ExifInterface] constants */
-fun computeExifOrientation(rotationDegrees: Int, mirrored: Boolean) = when {
- rotationDegrees == 0 && !mirrored -> ExifInterface.ORIENTATION_NORMAL
- rotationDegrees == 0 && mirrored -> ExifInterface.ORIENTATION_FLIP_HORIZONTAL
- rotationDegrees == 180 && !mirrored -> ExifInterface.ORIENTATION_ROTATE_180
- rotationDegrees == 180 && mirrored -> ExifInterface.ORIENTATION_FLIP_VERTICAL
- rotationDegrees == 270 && mirrored -> ExifInterface.ORIENTATION_TRANSVERSE
- rotationDegrees == 90 && !mirrored -> ExifInterface.ORIENTATION_ROTATE_90
- rotationDegrees == 90 && mirrored -> ExifInterface.ORIENTATION_TRANSPOSE
- rotationDegrees == 270 && mirrored -> ExifInterface.ORIENTATION_ROTATE_270
- rotationDegrees == 270 && !mirrored -> ExifInterface.ORIENTATION_TRANSVERSE
- else -> ExifInterface.ORIENTATION_UNDEFINED
-}
-
-/**
- * Helper function used to convert an EXIF orientation enum into a transformation matrix
- * that can be applied to a bitmap.
- *
- * @return matrix - Transformation required to properly display [Bitmap]
- */
-fun decodeExifOrientation(exifOrientation: Int): Matrix {
- val matrix = Matrix()
-
- // Apply transformation corresponding to declared EXIF orientation
- when (exifOrientation) {
- ExifInterface.ORIENTATION_NORMAL -> Unit
- ExifInterface.ORIENTATION_UNDEFINED -> Unit
- ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90F)
- ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180F)
- ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270F)
- ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.postScale(-1F, 1F)
- ExifInterface.ORIENTATION_FLIP_VERTICAL -> matrix.postScale(1F, -1F)
- ExifInterface.ORIENTATION_TRANSPOSE -> {
- matrix.postScale(-1F, 1F)
- matrix.postRotate(270F)
- }
- ExifInterface.ORIENTATION_TRANSVERSE -> {
- matrix.postScale(-1F, 1F)
- matrix.postRotate(90F)
- }
-
- // Error out if the EXIF orientation is invalid
- else -> Log.e(TAG, "Invalid orientation: $exifOrientation")
- }
-
- // Return the resulting matrix
- return matrix
-}
diff --git a/Camera2Video/utils/src/main/java/com/example/android/camera/utils/GenericListAdapter.kt b/Camera2Video/utils/src/main/java/com/example/android/camera/utils/GenericListAdapter.kt
deleted file mode 100644
index a55af278..00000000
--- a/Camera2Video/utils/src/main/java/com/example/android/camera/utils/GenericListAdapter.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.recyclerview.widget.RecyclerView
-
-/** Type helper used for the callback triggered once our view has been bound */
-typealias BindCallback = (view: View, data: T, position: Int) -> Unit
-
-/** List adapter for generic types, intended used for small-medium lists of data */
-class GenericListAdapter(
- private val dataset: List,
- private val itemLayoutId: Int? = null,
- private val itemViewFactory: (() -> View)? = null,
- private val onBind: BindCallback
-) : RecyclerView.Adapter() {
-
- class GenericListViewHolder(val view: View) : RecyclerView.ViewHolder(view)
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = GenericListViewHolder(when {
- itemViewFactory != null -> itemViewFactory.invoke()
- itemLayoutId != null -> {
- LayoutInflater.from(parent.context)
- .inflate(itemLayoutId, parent, false)
- }
- else -> {
- throw IllegalStateException(
- "Either the layout ID or the view factory need to be non-null")
- }
- })
-
- override fun onBindViewHolder(holder: GenericListViewHolder, position: Int) {
- if (position < 0 || position > dataset.size) return
- onBind(holder.view, dataset[position], position)
- }
-
- override fun getItemCount() = dataset.size
-}
\ No newline at end of file
diff --git a/Camera2Video/utils/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt b/Camera2Video/utils/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt
deleted file mode 100644
index f9d9a470..00000000
--- a/Camera2Video/utils/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.content.Context
-import android.hardware.camera2.CameraCharacteristics
-import android.view.OrientationEventListener
-import android.view.Surface
-import androidx.lifecycle.LiveData
-
-
-/**
- * Calculates closest 90-degree orientation to compensate for the device
- * rotation relative to sensor orientation, i.e., allows user to see camera
- * frames with the expected orientation.
- */
-class OrientationLiveData(
- context: Context,
- characteristics: CameraCharacteristics
-): LiveData() {
-
- private val listener = object : OrientationEventListener(context.applicationContext) {
- override fun onOrientationChanged(orientation: Int) {
- val rotation = when {
- orientation <= 45 -> Surface.ROTATION_0
- orientation <= 135 -> Surface.ROTATION_90
- orientation <= 225 -> Surface.ROTATION_180
- orientation <= 315 -> Surface.ROTATION_270
- else -> Surface.ROTATION_0
- }
- val relative = computeRelativeRotation(characteristics, rotation)
- if (relative != value) postValue(relative)
- }
- }
-
- override fun onActive() {
- super.onActive()
- listener.enable()
- }
-
- override fun onInactive() {
- super.onInactive()
- listener.disable()
- }
-
- companion object {
-
- /**
- * Computes rotation required to transform from the camera sensor orientation to the
- * device's current orientation in degrees.
- *
- * @param characteristics the [CameraCharacteristics] to query for the sensor orientation.
- * @param surfaceRotation the current device orientation as a Surface constant
- * @return the relative rotation from the camera sensor to the current device orientation.
- */
- @JvmStatic
- private fun computeRelativeRotation(
- characteristics: CameraCharacteristics,
- surfaceRotation: Int
- ): Int {
- val sensorOrientationDegrees =
- characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!
-
- val deviceOrientationDegrees = when (surfaceRotation) {
- Surface.ROTATION_0 -> 0
- Surface.ROTATION_90 -> 90
- Surface.ROTATION_180 -> 180
- Surface.ROTATION_270 -> 270
- else -> 0
- }
-
- // Reverse device orientation for front-facing cameras
- val sign = if (characteristics.get(CameraCharacteristics.LENS_FACING) ==
- CameraCharacteristics.LENS_FACING_FRONT) 1 else -1
-
- // Calculate desired JPEG orientation relative to camera orientation to make
- // the image upright relative to the device orientation
- return (sensorOrientationDegrees - (deviceOrientationDegrees * sign) + 360) % 360
- }
- }
-}
diff --git a/Camera2Video/utils/src/main/java/com/example/android/camera/utils/Yuv.kt b/Camera2Video/utils/src/main/java/com/example/android/camera/utils/Yuv.kt
deleted file mode 100644
index c476ad0e..00000000
--- a/Camera2Video/utils/src/main/java/com/example/android/camera/utils/Yuv.kt
+++ /dev/null
@@ -1,191 +0,0 @@
-package com.example.android.camera.utils
-
-import android.graphics.ImageFormat
-import android.media.Image
-import androidx.annotation.IntDef
-import java.nio.ByteBuffer
-
-/*
-This file is converted from part of https://github.com/gordinmitya/yuv2buf.
-Follow the link to find demo app, performance benchmarks and unit tests.
-
-Intro to YUV image formats:
-YUV_420_888 - is a generic format that can be represented as I420, YV12, NV21, and NV12.
-420 means that for each 4 luminosity pixels we have 2 chroma pixels: U and V.
-
-* I420 format represents an image as Y plane followed by U then followed by V plane
- without chroma channels interleaving.
- For example:
- Y Y Y Y
- Y Y Y Y
- U U V V
-
-* NV21 format represents an image as Y plane followed by V and U interleaved. First V then U.
- For example:
- Y Y Y Y
- Y Y Y Y
- V U V U
-
-* YV12 and NV12 are the same as previous formats but with swapped order of V and U. (U then V)
-
-Visualization of these 4 formats:
-https://user-images.githubusercontent.com/9286092/89119601-4f6f8100-d4b8-11ea-9a51-2765f7e513c2.jpg
-
-It's guaranteed that image.getPlanes() always returns planes in order Y U V for YUV_420_888.
-https://developer.android.com/reference/android/graphics/ImageFormat#YUV_420_888
-
-Because I420 and NV21 are more widely supported (RenderScript, OpenCV, MNN)
-the conversion is done into these formats.
-
-More about each format: https://www.fourcc.org/yuv.php
-*/
-
-@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
-@IntDef(ImageFormat.NV21, ImageFormat.YUV_420_888)
-annotation class YuvType
-
-class YuvByteBuffer(image: Image, dstBuffer: ByteBuffer? = null) {
- @YuvType
- val type: Int
- val buffer: ByteBuffer
-
- init {
- val wrappedImage = ImageWrapper(image)
-
- type = if (wrappedImage.u.pixelStride == 1) {
- ImageFormat.YUV_420_888
- } else {
- ImageFormat.NV21
- }
- val size = image.width * image.height * 3 / 2
- buffer = if (
- dstBuffer == null || dstBuffer.capacity() < size ||
- dstBuffer.isReadOnly || !dstBuffer.isDirect
- ) {
- ByteBuffer.allocateDirect(size) }
- else {
- dstBuffer
- }
- buffer.rewind()
-
- removePadding(wrappedImage)
- }
-
- // Input buffers are always direct as described in
- // https://developer.android.com/reference/android/media/Image.Plane#getBuffer()
- private fun removePadding(image: ImageWrapper) {
- val sizeLuma = image.y.width * image.y.height
- val sizeChroma = image.u.width * image.u.height
- if (image.y.rowStride > image.y.width) {
- removePaddingCompact(image.y, buffer, 0)
- } else {
- buffer.position(0)
- buffer.put(image.y.buffer)
- }
- if (type == ImageFormat.YUV_420_888) {
- if (image.u.rowStride > image.u.width) {
- removePaddingCompact(image.u, buffer, sizeLuma)
- removePaddingCompact(image.v, buffer, sizeLuma + sizeChroma)
- } else {
- buffer.position(sizeLuma)
- buffer.put(image.u.buffer)
- buffer.position(sizeLuma + sizeChroma)
- buffer.put(image.v.buffer)
- }
- } else {
- if (image.u.rowStride > image.u.width * 2) {
- removePaddingNotCompact(image, buffer, sizeLuma)
- } else {
- buffer.position(sizeLuma)
- var uv = image.v.buffer
- val properUVSize = image.v.height * image.v.rowStride - 1
- if (uv.capacity() > properUVSize) {
- uv = clipBuffer(image.v.buffer, 0, properUVSize)
- }
- buffer.put(uv)
- val lastOne = image.u.buffer[image.u.buffer.capacity() - 1]
- buffer.put(buffer.capacity() - 1, lastOne)
- }
- }
- buffer.rewind()
- }
-
- private fun removePaddingCompact(
- plane: PlaneWrapper,
- dst: ByteBuffer,
- offset: Int
- ) {
- require(plane.pixelStride == 1) {
- "use removePaddingCompact with pixelStride == 1"
- }
-
- val src = plane.buffer
- val rowStride = plane.rowStride
- var row: ByteBuffer
- dst.position(offset)
- for (i in 0 until plane.height) {
- row = clipBuffer(src, i * rowStride, plane.width)
- dst.put(row)
- }
- }
-
- private fun removePaddingNotCompact(
- image: ImageWrapper,
- dst: ByteBuffer,
- offset: Int
- ) {
- require(image.u.pixelStride == 2) {
- "use removePaddingNotCompact pixelStride == 2"
- }
- val width = image.u.width
- val height = image.u.height
- val rowStride = image.u.rowStride
- var row: ByteBuffer
- dst.position(offset)
- for (i in 0 until height - 1) {
- row = clipBuffer(image.v.buffer, i * rowStride, width * 2)
- dst.put(row)
- }
- row = clipBuffer(image.u.buffer, (height - 1) * rowStride - 1, width * 2)
- dst.put(row)
- }
-
- private fun clipBuffer(buffer: ByteBuffer, start: Int, size: Int): ByteBuffer {
- val duplicate = buffer.duplicate()
- duplicate.position(start)
- duplicate.limit(start + size)
- return duplicate.slice()
- }
-
- private class ImageWrapper(image:Image) {
- val width= image.width
- val height = image.height
- val y = PlaneWrapper(width, height, image.planes[0])
- val u = PlaneWrapper(width / 2, height / 2, image.planes[1])
- val v = PlaneWrapper(width / 2, height / 2, image.planes[2])
-
- // Check this is a supported image format
- // https://developer.android.com/reference/android/graphics/ImageFormat#YUV_420_888
- init {
- require(y.pixelStride == 1) {
- "Pixel stride for Y plane must be 1 but got ${y.pixelStride} instead."
- }
- require(u.pixelStride == v.pixelStride && u.rowStride == v.rowStride) {
- "U and V planes must have the same pixel and row strides " +
- "but got pixel=${u.pixelStride} row=${u.rowStride} for U " +
- "and pixel=${v.pixelStride} and row=${v.rowStride} for V"
- }
- require(u.pixelStride == 1 || u.pixelStride == 2) {
- "Supported" + " pixel strides for U and V planes are 1 and 2"
- }
- }
- }
-
- private class PlaneWrapper(width: Int, height: Int, plane: Image.Plane) {
- val width = width
- val height = height
- val buffer: ByteBuffer = plane.buffer
- val rowStride = plane.rowStride
- val pixelStride = plane.pixelStride
- }
-}
\ No newline at end of file
diff --git a/Camera2Video/utils/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt b/Camera2Video/utils/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt
deleted file mode 100644
index 8dcd5596..00000000
--- a/Camera2Video/utils/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.ImageFormat
-import android.media.Image
-import android.renderscript.Allocation
-import android.renderscript.Element
-import android.renderscript.RenderScript
-import android.renderscript.ScriptIntrinsicYuvToRGB
-import android.renderscript.Type
-import java.nio.ByteBuffer
-
-/**
- * Helper class used to convert a [Image] object from
- * [ImageFormat.YUV_420_888] format to an RGB [Bitmap] object, it has equivalent
- * functionality to https://github
- * .com/androidx/androidx/blob/androidx-main/camera/camera-core/src/main/java/androidx/camera/core/ImageYuvToRgbConverter.java
- *
- * NOTE: This has been tested in a limited number of devices and is not
- * considered production-ready code. It was created for illustration purposes,
- * since this is not an efficient camera pipeline due to the multiple copies
- * required to convert each frame. For example, this
- * implementation
- * (https://stackoverflow.com/questions/52726002/camera2-captured-picture-conversion-from-yuv-420-888-to-nv21/52740776#52740776)
- * might have better performance.
- */
-class YuvToRgbConverter(context: Context) {
- private val rs = RenderScript.create(context)
- private val scriptYuvToRgb =
- ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs))
-
- // Do not add getters/setters functions to these private variables
- // because yuvToRgb() assume they won't be modified elsewhere
- private var yuvBits: ByteBuffer? = null
- private var bytes: ByteArray = ByteArray(0)
- private var inputAllocation: Allocation? = null
- private var outputAllocation: Allocation? = null
-
- @Synchronized
- fun yuvToRgb(image: Image, output: Bitmap) {
- val yuvBuffer = YuvByteBuffer(image, yuvBits)
- yuvBits = yuvBuffer.buffer
-
- if (needCreateAllocations(image, yuvBuffer)) {
- val yuvType = Type.Builder(rs, Element.U8(rs))
- .setX(image.width)
- .setY(image.height)
- .setYuvFormat(yuvBuffer.type)
- inputAllocation = Allocation.createTyped(
- rs,
- yuvType.create(),
- Allocation.USAGE_SCRIPT
- )
- bytes = ByteArray(yuvBuffer.buffer.capacity())
- val rgbaType = Type.Builder(rs, Element.RGBA_8888(rs))
- .setX(image.width)
- .setY(image.height)
- outputAllocation = Allocation.createTyped(
- rs,
- rgbaType.create(),
- Allocation.USAGE_SCRIPT
- )
- }
-
- yuvBuffer.buffer.get(bytes)
- inputAllocation!!.copyFrom(bytes)
-
- // Convert NV21 or YUV_420_888 format to RGB
- inputAllocation!!.copyFrom(bytes)
- scriptYuvToRgb.setInput(inputAllocation)
- scriptYuvToRgb.forEach(outputAllocation)
- outputAllocation!!.copyTo(output)
- }
-
- private fun needCreateAllocations(image: Image, yuvBuffer: YuvByteBuffer): Boolean {
- return (inputAllocation == null || // the very 1st call
- inputAllocation!!.type.x != image.width || // image size changed
- inputAllocation!!.type.y != image.height ||
- inputAllocation!!.type.yuv != yuvBuffer.type || // image format changed
- bytes.size == yuvBuffer.buffer.capacity())
- }
-}
diff --git a/Camera2Video/utils/src/main/res/drawable/ic_shutter.xml b/Camera2Video/utils/src/main/res/drawable/ic_shutter.xml
deleted file mode 100644
index 9bb91ab8..00000000
--- a/Camera2Video/utils/src/main/res/drawable/ic_shutter.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Camera2Video/utils/src/main/res/drawable/ic_shutter_focused.xml b/Camera2Video/utils/src/main/res/drawable/ic_shutter_focused.xml
deleted file mode 100644
index d92ffdfa..00000000
--- a/Camera2Video/utils/src/main/res/drawable/ic_shutter_focused.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/Camera2Video/utils/src/main/res/drawable/ic_shutter_normal.xml b/Camera2Video/utils/src/main/res/drawable/ic_shutter_normal.xml
deleted file mode 100644
index cb50026e..00000000
--- a/Camera2Video/utils/src/main/res/drawable/ic_shutter_normal.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/Camera2Video/utils/src/main/res/drawable/ic_shutter_pressed.xml b/Camera2Video/utils/src/main/res/drawable/ic_shutter_pressed.xml
deleted file mode 100644
index d92ffdfa..00000000
--- a/Camera2Video/utils/src/main/res/drawable/ic_shutter_pressed.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraUtils/.gitignore b/CameraUtils/.gitignore
deleted file mode 100644
index 24bddc82..00000000
--- a/CameraUtils/.gitignore
+++ /dev/null
@@ -1,60 +0,0 @@
-# Built application files
-app/release
-*.apk
-*.ap_
-
-# Files for the ART/Dalvik VM
-*.dex
-
-# Java class files
-*.class
-
-# Generated files
-bin/
-gen/
-out/
-
-# Gradle files
-.gradle/
-build/
-
-# Local configuration file (sdk path, etc)
-local.properties
-
-# Proguard folder generated by Eclipse
-proguard/
-
-# Log Files
-*.log
-
-# Android Studio Navigation editor temp files
-.navigation/
-
-# Android Studio captures folder
-captures/
-
-# IntelliJ
-*.iml
-.idea/*
-!.idea/runConfigurations
-
-# Keystore files
-*.jks
-
-# External native build folder generated in Android Studio 2.2 and later
-.externalNativeBuild
-
-# Google Services (e.g. APIs or Firebase)
-google-services.json
-
-# Freeline
-freeline.py
-freeline/
-freeline_project_description.json
-
-# fastlane
-fastlane/report.xml
-fastlane/Preview.html
-fastlane/screenshots
-fastlane/test_output
-fastlane/readme.md
diff --git a/CameraUtils/.java-version b/CameraUtils/.java-version
deleted file mode 100644
index 9d607966..00000000
--- a/CameraUtils/.java-version
+++ /dev/null
@@ -1 +0,0 @@
-11
\ No newline at end of file
diff --git a/CameraUtils/README.md b/CameraUtils/README.md
deleted file mode 100644
index 9e5886eb..00000000
--- a/CameraUtils/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-Android Camera Utilities
-========================
-
-This Android Library contains a collection of utilities used across different
-samples in this repo, it is not intended to be run as a standalone application.
\ No newline at end of file
diff --git a/CameraUtils/build.gradle b/CameraUtils/build.gradle
deleted file mode 100644
index e5b67a79..00000000
--- a/CameraUtils/build.gradle
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.
- */
-
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
-
-buildscript {
- // Top-level variables used for versioning
- ext.kotlin_version = '1.5.21'
- ext.java_version = JavaVersion.VERSION_1_8
-
- repositories {
- google()
- mavenCentral()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:4.2.2'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
-
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
- }
-}
-
-allprojects {
- repositories {
- mavenLocal()
- google()
- mavenCentral()
- }
-}
-
-task clean(type: Delete) {
- delete rootProject.buildDir
-}
diff --git a/CameraUtils/gradle.properties b/CameraUtils/gradle.properties
deleted file mode 100644
index c512ddd3..00000000
--- a/CameraUtils/gradle.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# Project-wide Gradle settings.
-# IDE (e.g. Android Studio) users:
-# Gradle settings configured through the IDE *will override*
-# any settings specified in this file.
-# For more details on how to configure your build environment visit
-# http://www.gradle.org/docs/current/userguide/build_environment.html
-# Specifies the JVM arguments used for the daemon process.
-# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx1536m
-android.enableJetifier=true
-android.useAndroidX=true
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
\ No newline at end of file
diff --git a/CameraUtils/gradle/wrapper/gradle-wrapper.jar b/CameraUtils/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 94336fca..00000000
Binary files a/CameraUtils/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/CameraUtils/gradle/wrapper/gradle-wrapper.properties b/CameraUtils/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 1b106427..00000000
--- a/CameraUtils/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-#Wed Mar 31 21:07:00 PDT 2021
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
diff --git a/CameraUtils/gradlew b/CameraUtils/gradlew
deleted file mode 100755
index cccdd3d5..00000000
--- a/CameraUtils/gradlew
+++ /dev/null
@@ -1,172 +0,0 @@
-#!/usr/bin/env sh
-
-##############################################################################
-##
-## Gradle start up script for UN*X
-##
-##############################################################################
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn () {
- echo "$*"
-}
-
-die () {
- echo
- echo "$*"
- echo
- exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
- else
- JAVACMD="$JAVA_HOME/bin/java"
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
- fi
- i=$((i+1))
- done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
-fi
-
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=$(save "$@")
-
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-
-# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
-if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
- cd "$(dirname "$0")"
-fi
-
-exec "$JAVACMD" "$@"
diff --git a/CameraUtils/gradlew.bat b/CameraUtils/gradlew.bat
deleted file mode 100644
index e95643d6..00000000
--- a/CameraUtils/gradlew.bat
+++ /dev/null
@@ -1,84 +0,0 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/CameraUtils/lib/.gitignore b/CameraUtils/lib/.gitignore
deleted file mode 100644
index 38c58757..00000000
--- a/CameraUtils/lib/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/build
-.idea
\ No newline at end of file
diff --git a/CameraUtils/lib/README.md b/CameraUtils/lib/README.md
deleted file mode 100644
index 7a10722f..00000000
--- a/CameraUtils/lib/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-Do not modify code under this folder outside of `CameraUtils`, it is copied
-automatically by `.github/scripts/copy_utils.sh`.
\ No newline at end of file
diff --git a/CameraUtils/lib/build.gradle b/CameraUtils/lib/build.gradle
deleted file mode 100644
index 1ef65fa3..00000000
--- a/CameraUtils/lib/build.gradle
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.
- */
-
-apply plugin: 'com.android.library'
-apply plugin: 'kotlin-android'
-
-android {
- compileSdkVersion 29
-
- defaultConfig {
- minSdkVersion 21
- targetSdkVersion 29
- versionCode 1
- versionName "1.0"
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles 'consumer-rules.pro'
- }
-
- compileOptions {
- sourceCompatibility rootProject.ext.java_version
- targetCompatibility rootProject.ext.java_version
- }
-
- kotlinOptions {
- jvmTarget = "$rootProject.ext.java_version"
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
-}
-
-dependencies {
-
- // Kotlin lang
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.4'
-
- // App compat and UI things
- implementation 'androidx.appcompat:appcompat:1.1.0'
- implementation 'androidx.recyclerview:recyclerview:1.1.0'
-
- // EXIF Interface
- implementation 'androidx.exifinterface:exifinterface:1.2.0'
-
- // Unit testing
- testImplementation 'androidx.test.ext:junit:1.1.1'
- testImplementation 'androidx.test:rules:1.2.0'
- testImplementation 'androidx.test:runner:1.2.0'
- testImplementation 'androidx.test.espresso:espresso-core:3.2.0'
- testImplementation 'org.robolectric:robolectric:4.3.1'
-
- // Instrumented testing
- androidTestImplementation 'androidx.test.ext:junit:1.1.1'
- androidTestImplementation 'androidx.test:rules:1.2.0'
- androidTestImplementation 'androidx.test:runner:1.2.0'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
-}
diff --git a/CameraUtils/lib/src/main/AndroidManifest.xml b/CameraUtils/lib/src/main/AndroidManifest.xml
deleted file mode 100644
index 2e13c37a..00000000
--- a/CameraUtils/lib/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
diff --git a/CameraUtils/lib/src/main/java/com/example/android/camera/utils/AutoFitSurfaceView.kt b/CameraUtils/lib/src/main/java/com/example/android/camera/utils/AutoFitSurfaceView.kt
deleted file mode 100644
index 3d900d19..00000000
--- a/CameraUtils/lib/src/main/java/com/example/android/camera/utils/AutoFitSurfaceView.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.content.Context
-import android.util.AttributeSet
-import android.util.Log
-import android.view.SurfaceView
-import kotlin.math.roundToInt
-
-/**
- * A [SurfaceView] that can be adjusted to a specified aspect ratio and
- * performs center-crop transformation of input frames.
- */
-class AutoFitSurfaceView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyle: Int = 0
-) : SurfaceView(context, attrs, defStyle) {
-
- private var aspectRatio = 0f
-
- /**
- * Sets the aspect ratio for this view. The size of the view will be
- * measured based on the ratio calculated from the parameters.
- *
- * @param width Camera resolution horizontal size
- * @param height Camera resolution vertical size
- */
- fun setAspectRatio(width: Int, height: Int) {
- require(width > 0 && height > 0) { "Size cannot be negative" }
- aspectRatio = width.toFloat() / height.toFloat()
- holder.setFixedSize(width, height)
- requestLayout()
- }
-
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec)
- val width = MeasureSpec.getSize(widthMeasureSpec)
- val height = MeasureSpec.getSize(heightMeasureSpec)
- if (aspectRatio == 0f) {
- setMeasuredDimension(width, height)
- } else {
-
- // Performs center-crop transformation of the camera frames
- val newWidth: Int
- val newHeight: Int
- val actualRatio = if (width > height) aspectRatio else 1f / aspectRatio
- if (width < height * actualRatio) {
- newHeight = height
- newWidth = (height * actualRatio).roundToInt()
- } else {
- newWidth = width
- newHeight = (width / actualRatio).roundToInt()
- }
-
- Log.d(TAG, "Measured dimensions set: $newWidth x $newHeight")
- setMeasuredDimension(newWidth, newHeight)
- }
- }
-
- companion object {
- private val TAG = AutoFitSurfaceView::class.java.simpleName
- }
-}
diff --git a/CameraUtils/lib/src/main/java/com/example/android/camera/utils/CameraSizes.kt b/CameraUtils/lib/src/main/java/com/example/android/camera/utils/CameraSizes.kt
deleted file mode 100644
index 6db01d31..00000000
--- a/CameraUtils/lib/src/main/java/com/example/android/camera/utils/CameraSizes.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.graphics.Point
-import android.hardware.camera2.CameraCharacteristics
-import android.hardware.camera2.params.StreamConfigurationMap
-import android.util.Size
-import android.view.Display
-import kotlin.math.max
-import kotlin.math.min
-
-/** Helper class used to pre-compute shortest and longest sides of a [Size] */
-class SmartSize(width: Int, height: Int) {
- var size = Size(width, height)
- var long = max(size.width, size.height)
- var short = min(size.width, size.height)
- override fun toString() = "SmartSize(${long}x${short})"
-}
-
-/** Standard High Definition size for pictures and video */
-val SIZE_1080P: SmartSize = SmartSize(1920, 1080)
-
-/** Returns a [SmartSize] object for the given [Display] */
-fun getDisplaySmartSize(display: Display): SmartSize {
- val outPoint = Point()
- display.getRealSize(outPoint)
- return SmartSize(outPoint.x, outPoint.y)
-}
-
-/**
- * Returns the largest available PREVIEW size. For more information, see:
- * https://d.android.com/reference/android/hardware/camera2/CameraDevice and
- * https://developer.android.com/reference/android/hardware/camera2/params/StreamConfigurationMap
- */
-fun getPreviewOutputSize(
- display: Display,
- characteristics: CameraCharacteristics,
- targetClass: Class,
- format: Int? = null
-): Size {
-
- // Find which is smaller: screen or 1080p
- val screenSize = getDisplaySmartSize(display)
- val hdScreen = screenSize.long >= SIZE_1080P.long || screenSize.short >= SIZE_1080P.short
- val maxSize = if (hdScreen) SIZE_1080P else screenSize
-
- // If image format is provided, use it to determine supported sizes; else use target class
- val config = characteristics.get(
- CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
- if (format == null)
- assert(StreamConfigurationMap.isOutputSupportedFor(targetClass))
- else
- assert(config.isOutputSupportedFor(format))
- val allSizes = if (format == null)
- config.getOutputSizes(targetClass) else config.getOutputSizes(format)
-
- // Get available sizes and sort them by area from largest to smallest
- val validSizes = allSizes
- .sortedWith(compareBy { it.height * it.width })
- .map { SmartSize(it.width, it.height) }.reversed()
-
- // Then, get the largest output size that is smaller or equal than our max size
- return validSizes.first { it.long <= maxSize.long && it.short <= maxSize.short }.size
-}
\ No newline at end of file
diff --git a/CameraUtils/lib/src/main/java/com/example/android/camera/utils/ExifUtils.kt b/CameraUtils/lib/src/main/java/com/example/android/camera/utils/ExifUtils.kt
deleted file mode 100644
index 561c14b3..00000000
--- a/CameraUtils/lib/src/main/java/com/example/android/camera/utils/ExifUtils.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.graphics.Bitmap
-import android.graphics.Matrix
-import android.util.Log
-import androidx.exifinterface.media.ExifInterface
-
-private const val TAG: String = "ExifUtils"
-
-/** Transforms rotation and mirroring information into one of the [ExifInterface] constants */
-fun computeExifOrientation(rotationDegrees: Int, mirrored: Boolean) = when {
- rotationDegrees == 0 && !mirrored -> ExifInterface.ORIENTATION_NORMAL
- rotationDegrees == 0 && mirrored -> ExifInterface.ORIENTATION_FLIP_HORIZONTAL
- rotationDegrees == 180 && !mirrored -> ExifInterface.ORIENTATION_ROTATE_180
- rotationDegrees == 180 && mirrored -> ExifInterface.ORIENTATION_FLIP_VERTICAL
- rotationDegrees == 270 && mirrored -> ExifInterface.ORIENTATION_TRANSVERSE
- rotationDegrees == 90 && !mirrored -> ExifInterface.ORIENTATION_ROTATE_90
- rotationDegrees == 90 && mirrored -> ExifInterface.ORIENTATION_TRANSPOSE
- rotationDegrees == 270 && mirrored -> ExifInterface.ORIENTATION_ROTATE_270
- rotationDegrees == 270 && !mirrored -> ExifInterface.ORIENTATION_TRANSVERSE
- else -> ExifInterface.ORIENTATION_UNDEFINED
-}
-
-/**
- * Helper function used to convert an EXIF orientation enum into a transformation matrix
- * that can be applied to a bitmap.
- *
- * @return matrix - Transformation required to properly display [Bitmap]
- */
-fun decodeExifOrientation(exifOrientation: Int): Matrix {
- val matrix = Matrix()
-
- // Apply transformation corresponding to declared EXIF orientation
- when (exifOrientation) {
- ExifInterface.ORIENTATION_NORMAL -> Unit
- ExifInterface.ORIENTATION_UNDEFINED -> Unit
- ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90F)
- ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180F)
- ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270F)
- ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.postScale(-1F, 1F)
- ExifInterface.ORIENTATION_FLIP_VERTICAL -> matrix.postScale(1F, -1F)
- ExifInterface.ORIENTATION_TRANSPOSE -> {
- matrix.postScale(-1F, 1F)
- matrix.postRotate(270F)
- }
- ExifInterface.ORIENTATION_TRANSVERSE -> {
- matrix.postScale(-1F, 1F)
- matrix.postRotate(90F)
- }
-
- // Error out if the EXIF orientation is invalid
- else -> Log.e(TAG, "Invalid orientation: $exifOrientation")
- }
-
- // Return the resulting matrix
- return matrix
-}
diff --git a/CameraUtils/lib/src/main/java/com/example/android/camera/utils/GenericListAdapter.kt b/CameraUtils/lib/src/main/java/com/example/android/camera/utils/GenericListAdapter.kt
deleted file mode 100644
index a55af278..00000000
--- a/CameraUtils/lib/src/main/java/com/example/android/camera/utils/GenericListAdapter.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.recyclerview.widget.RecyclerView
-
-/** Type helper used for the callback triggered once our view has been bound */
-typealias BindCallback = (view: View, data: T, position: Int) -> Unit
-
-/** List adapter for generic types, intended used for small-medium lists of data */
-class GenericListAdapter(
- private val dataset: List,
- private val itemLayoutId: Int? = null,
- private val itemViewFactory: (() -> View)? = null,
- private val onBind: BindCallback
-) : RecyclerView.Adapter() {
-
- class GenericListViewHolder(val view: View) : RecyclerView.ViewHolder(view)
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = GenericListViewHolder(when {
- itemViewFactory != null -> itemViewFactory.invoke()
- itemLayoutId != null -> {
- LayoutInflater.from(parent.context)
- .inflate(itemLayoutId, parent, false)
- }
- else -> {
- throw IllegalStateException(
- "Either the layout ID or the view factory need to be non-null")
- }
- })
-
- override fun onBindViewHolder(holder: GenericListViewHolder, position: Int) {
- if (position < 0 || position > dataset.size) return
- onBind(holder.view, dataset[position], position)
- }
-
- override fun getItemCount() = dataset.size
-}
\ No newline at end of file
diff --git a/CameraUtils/lib/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt b/CameraUtils/lib/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt
deleted file mode 100644
index f9d9a470..00000000
--- a/CameraUtils/lib/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.content.Context
-import android.hardware.camera2.CameraCharacteristics
-import android.view.OrientationEventListener
-import android.view.Surface
-import androidx.lifecycle.LiveData
-
-
-/**
- * Calculates closest 90-degree orientation to compensate for the device
- * rotation relative to sensor orientation, i.e., allows user to see camera
- * frames with the expected orientation.
- */
-class OrientationLiveData(
- context: Context,
- characteristics: CameraCharacteristics
-): LiveData() {
-
- private val listener = object : OrientationEventListener(context.applicationContext) {
- override fun onOrientationChanged(orientation: Int) {
- val rotation = when {
- orientation <= 45 -> Surface.ROTATION_0
- orientation <= 135 -> Surface.ROTATION_90
- orientation <= 225 -> Surface.ROTATION_180
- orientation <= 315 -> Surface.ROTATION_270
- else -> Surface.ROTATION_0
- }
- val relative = computeRelativeRotation(characteristics, rotation)
- if (relative != value) postValue(relative)
- }
- }
-
- override fun onActive() {
- super.onActive()
- listener.enable()
- }
-
- override fun onInactive() {
- super.onInactive()
- listener.disable()
- }
-
- companion object {
-
- /**
- * Computes rotation required to transform from the camera sensor orientation to the
- * device's current orientation in degrees.
- *
- * @param characteristics the [CameraCharacteristics] to query for the sensor orientation.
- * @param surfaceRotation the current device orientation as a Surface constant
- * @return the relative rotation from the camera sensor to the current device orientation.
- */
- @JvmStatic
- private fun computeRelativeRotation(
- characteristics: CameraCharacteristics,
- surfaceRotation: Int
- ): Int {
- val sensorOrientationDegrees =
- characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!
-
- val deviceOrientationDegrees = when (surfaceRotation) {
- Surface.ROTATION_0 -> 0
- Surface.ROTATION_90 -> 90
- Surface.ROTATION_180 -> 180
- Surface.ROTATION_270 -> 270
- else -> 0
- }
-
- // Reverse device orientation for front-facing cameras
- val sign = if (characteristics.get(CameraCharacteristics.LENS_FACING) ==
- CameraCharacteristics.LENS_FACING_FRONT) 1 else -1
-
- // Calculate desired JPEG orientation relative to camera orientation to make
- // the image upright relative to the device orientation
- return (sensorOrientationDegrees - (deviceOrientationDegrees * sign) + 360) % 360
- }
- }
-}
diff --git a/CameraUtils/lib/src/main/java/com/example/android/camera/utils/Yuv.kt b/CameraUtils/lib/src/main/java/com/example/android/camera/utils/Yuv.kt
deleted file mode 100644
index c476ad0e..00000000
--- a/CameraUtils/lib/src/main/java/com/example/android/camera/utils/Yuv.kt
+++ /dev/null
@@ -1,191 +0,0 @@
-package com.example.android.camera.utils
-
-import android.graphics.ImageFormat
-import android.media.Image
-import androidx.annotation.IntDef
-import java.nio.ByteBuffer
-
-/*
-This file is converted from part of https://github.com/gordinmitya/yuv2buf.
-Follow the link to find demo app, performance benchmarks and unit tests.
-
-Intro to YUV image formats:
-YUV_420_888 - is a generic format that can be represented as I420, YV12, NV21, and NV12.
-420 means that for each 4 luminosity pixels we have 2 chroma pixels: U and V.
-
-* I420 format represents an image as Y plane followed by U then followed by V plane
- without chroma channels interleaving.
- For example:
- Y Y Y Y
- Y Y Y Y
- U U V V
-
-* NV21 format represents an image as Y plane followed by V and U interleaved. First V then U.
- For example:
- Y Y Y Y
- Y Y Y Y
- V U V U
-
-* YV12 and NV12 are the same as previous formats but with swapped order of V and U. (U then V)
-
-Visualization of these 4 formats:
-https://user-images.githubusercontent.com/9286092/89119601-4f6f8100-d4b8-11ea-9a51-2765f7e513c2.jpg
-
-It's guaranteed that image.getPlanes() always returns planes in order Y U V for YUV_420_888.
-https://developer.android.com/reference/android/graphics/ImageFormat#YUV_420_888
-
-Because I420 and NV21 are more widely supported (RenderScript, OpenCV, MNN)
-the conversion is done into these formats.
-
-More about each format: https://www.fourcc.org/yuv.php
-*/
-
-@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
-@IntDef(ImageFormat.NV21, ImageFormat.YUV_420_888)
-annotation class YuvType
-
-class YuvByteBuffer(image: Image, dstBuffer: ByteBuffer? = null) {
- @YuvType
- val type: Int
- val buffer: ByteBuffer
-
- init {
- val wrappedImage = ImageWrapper(image)
-
- type = if (wrappedImage.u.pixelStride == 1) {
- ImageFormat.YUV_420_888
- } else {
- ImageFormat.NV21
- }
- val size = image.width * image.height * 3 / 2
- buffer = if (
- dstBuffer == null || dstBuffer.capacity() < size ||
- dstBuffer.isReadOnly || !dstBuffer.isDirect
- ) {
- ByteBuffer.allocateDirect(size) }
- else {
- dstBuffer
- }
- buffer.rewind()
-
- removePadding(wrappedImage)
- }
-
- // Input buffers are always direct as described in
- // https://developer.android.com/reference/android/media/Image.Plane#getBuffer()
- private fun removePadding(image: ImageWrapper) {
- val sizeLuma = image.y.width * image.y.height
- val sizeChroma = image.u.width * image.u.height
- if (image.y.rowStride > image.y.width) {
- removePaddingCompact(image.y, buffer, 0)
- } else {
- buffer.position(0)
- buffer.put(image.y.buffer)
- }
- if (type == ImageFormat.YUV_420_888) {
- if (image.u.rowStride > image.u.width) {
- removePaddingCompact(image.u, buffer, sizeLuma)
- removePaddingCompact(image.v, buffer, sizeLuma + sizeChroma)
- } else {
- buffer.position(sizeLuma)
- buffer.put(image.u.buffer)
- buffer.position(sizeLuma + sizeChroma)
- buffer.put(image.v.buffer)
- }
- } else {
- if (image.u.rowStride > image.u.width * 2) {
- removePaddingNotCompact(image, buffer, sizeLuma)
- } else {
- buffer.position(sizeLuma)
- var uv = image.v.buffer
- val properUVSize = image.v.height * image.v.rowStride - 1
- if (uv.capacity() > properUVSize) {
- uv = clipBuffer(image.v.buffer, 0, properUVSize)
- }
- buffer.put(uv)
- val lastOne = image.u.buffer[image.u.buffer.capacity() - 1]
- buffer.put(buffer.capacity() - 1, lastOne)
- }
- }
- buffer.rewind()
- }
-
- private fun removePaddingCompact(
- plane: PlaneWrapper,
- dst: ByteBuffer,
- offset: Int
- ) {
- require(plane.pixelStride == 1) {
- "use removePaddingCompact with pixelStride == 1"
- }
-
- val src = plane.buffer
- val rowStride = plane.rowStride
- var row: ByteBuffer
- dst.position(offset)
- for (i in 0 until plane.height) {
- row = clipBuffer(src, i * rowStride, plane.width)
- dst.put(row)
- }
- }
-
- private fun removePaddingNotCompact(
- image: ImageWrapper,
- dst: ByteBuffer,
- offset: Int
- ) {
- require(image.u.pixelStride == 2) {
- "use removePaddingNotCompact pixelStride == 2"
- }
- val width = image.u.width
- val height = image.u.height
- val rowStride = image.u.rowStride
- var row: ByteBuffer
- dst.position(offset)
- for (i in 0 until height - 1) {
- row = clipBuffer(image.v.buffer, i * rowStride, width * 2)
- dst.put(row)
- }
- row = clipBuffer(image.u.buffer, (height - 1) * rowStride - 1, width * 2)
- dst.put(row)
- }
-
- private fun clipBuffer(buffer: ByteBuffer, start: Int, size: Int): ByteBuffer {
- val duplicate = buffer.duplicate()
- duplicate.position(start)
- duplicate.limit(start + size)
- return duplicate.slice()
- }
-
- private class ImageWrapper(image:Image) {
- val width= image.width
- val height = image.height
- val y = PlaneWrapper(width, height, image.planes[0])
- val u = PlaneWrapper(width / 2, height / 2, image.planes[1])
- val v = PlaneWrapper(width / 2, height / 2, image.planes[2])
-
- // Check this is a supported image format
- // https://developer.android.com/reference/android/graphics/ImageFormat#YUV_420_888
- init {
- require(y.pixelStride == 1) {
- "Pixel stride for Y plane must be 1 but got ${y.pixelStride} instead."
- }
- require(u.pixelStride == v.pixelStride && u.rowStride == v.rowStride) {
- "U and V planes must have the same pixel and row strides " +
- "but got pixel=${u.pixelStride} row=${u.rowStride} for U " +
- "and pixel=${v.pixelStride} and row=${v.rowStride} for V"
- }
- require(u.pixelStride == 1 || u.pixelStride == 2) {
- "Supported" + " pixel strides for U and V planes are 1 and 2"
- }
- }
- }
-
- private class PlaneWrapper(width: Int, height: Int, plane: Image.Plane) {
- val width = width
- val height = height
- val buffer: ByteBuffer = plane.buffer
- val rowStride = plane.rowStride
- val pixelStride = plane.pixelStride
- }
-}
\ No newline at end of file
diff --git a/CameraUtils/lib/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt b/CameraUtils/lib/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt
deleted file mode 100644
index 8dcd5596..00000000
--- a/CameraUtils/lib/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.ImageFormat
-import android.media.Image
-import android.renderscript.Allocation
-import android.renderscript.Element
-import android.renderscript.RenderScript
-import android.renderscript.ScriptIntrinsicYuvToRGB
-import android.renderscript.Type
-import java.nio.ByteBuffer
-
-/**
- * Helper class used to convert a [Image] object from
- * [ImageFormat.YUV_420_888] format to an RGB [Bitmap] object, it has equivalent
- * functionality to https://github
- * .com/androidx/androidx/blob/androidx-main/camera/camera-core/src/main/java/androidx/camera/core/ImageYuvToRgbConverter.java
- *
- * NOTE: This has been tested in a limited number of devices and is not
- * considered production-ready code. It was created for illustration purposes,
- * since this is not an efficient camera pipeline due to the multiple copies
- * required to convert each frame. For example, this
- * implementation
- * (https://stackoverflow.com/questions/52726002/camera2-captured-picture-conversion-from-yuv-420-888-to-nv21/52740776#52740776)
- * might have better performance.
- */
-class YuvToRgbConverter(context: Context) {
- private val rs = RenderScript.create(context)
- private val scriptYuvToRgb =
- ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs))
-
- // Do not add getters/setters functions to these private variables
- // because yuvToRgb() assume they won't be modified elsewhere
- private var yuvBits: ByteBuffer? = null
- private var bytes: ByteArray = ByteArray(0)
- private var inputAllocation: Allocation? = null
- private var outputAllocation: Allocation? = null
-
- @Synchronized
- fun yuvToRgb(image: Image, output: Bitmap) {
- val yuvBuffer = YuvByteBuffer(image, yuvBits)
- yuvBits = yuvBuffer.buffer
-
- if (needCreateAllocations(image, yuvBuffer)) {
- val yuvType = Type.Builder(rs, Element.U8(rs))
- .setX(image.width)
- .setY(image.height)
- .setYuvFormat(yuvBuffer.type)
- inputAllocation = Allocation.createTyped(
- rs,
- yuvType.create(),
- Allocation.USAGE_SCRIPT
- )
- bytes = ByteArray(yuvBuffer.buffer.capacity())
- val rgbaType = Type.Builder(rs, Element.RGBA_8888(rs))
- .setX(image.width)
- .setY(image.height)
- outputAllocation = Allocation.createTyped(
- rs,
- rgbaType.create(),
- Allocation.USAGE_SCRIPT
- )
- }
-
- yuvBuffer.buffer.get(bytes)
- inputAllocation!!.copyFrom(bytes)
-
- // Convert NV21 or YUV_420_888 format to RGB
- inputAllocation!!.copyFrom(bytes)
- scriptYuvToRgb.setInput(inputAllocation)
- scriptYuvToRgb.forEach(outputAllocation)
- outputAllocation!!.copyTo(output)
- }
-
- private fun needCreateAllocations(image: Image, yuvBuffer: YuvByteBuffer): Boolean {
- return (inputAllocation == null || // the very 1st call
- inputAllocation!!.type.x != image.width || // image size changed
- inputAllocation!!.type.y != image.height ||
- inputAllocation!!.type.yuv != yuvBuffer.type || // image format changed
- bytes.size == yuvBuffer.buffer.capacity())
- }
-}
diff --git a/CameraUtils/lib/src/main/res/drawable/ic_shutter.xml b/CameraUtils/lib/src/main/res/drawable/ic_shutter.xml
deleted file mode 100644
index 9bb91ab8..00000000
--- a/CameraUtils/lib/src/main/res/drawable/ic_shutter.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraUtils/lib/src/main/res/drawable/ic_shutter_focused.xml b/CameraUtils/lib/src/main/res/drawable/ic_shutter_focused.xml
deleted file mode 100644
index 9bf521da..00000000
--- a/CameraUtils/lib/src/main/res/drawable/ic_shutter_focused.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraUtils/lib/src/main/res/drawable/ic_shutter_normal.xml b/CameraUtils/lib/src/main/res/drawable/ic_shutter_normal.xml
deleted file mode 100644
index cb50026e..00000000
--- a/CameraUtils/lib/src/main/res/drawable/ic_shutter_normal.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraUtils/lib/src/main/res/drawable/ic_shutter_pressed.xml b/CameraUtils/lib/src/main/res/drawable/ic_shutter_pressed.xml
deleted file mode 100644
index 9bf521da..00000000
--- a/CameraUtils/lib/src/main/res/drawable/ic_shutter_pressed.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraUtils/settings.gradle b/CameraUtils/settings.gradle
deleted file mode 100644
index 5c26f719..00000000
--- a/CameraUtils/settings.gradle
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.
- */
-
-include ':lib'
diff --git a/CameraX-MLKit/.gitignore b/CameraX-MLKit/.gitignore
deleted file mode 100644
index aa724b77..00000000
--- a/CameraX-MLKit/.gitignore
+++ /dev/null
@@ -1,15 +0,0 @@
-*.iml
-.gradle
-/local.properties
-/.idea/caches
-/.idea/libraries
-/.idea/modules.xml
-/.idea/workspace.xml
-/.idea/navEditor.xml
-/.idea/assetWizardSettings.xml
-.DS_Store
-/build
-/captures
-.externalNativeBuild
-.cxx
-local.properties
diff --git a/CameraX-MLKit/README.md b/CameraX-MLKit/README.md
deleted file mode 100644
index 4df2e048..00000000
--- a/CameraX-MLKit/README.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# CameraX-MLKit
-
-This example uses CameraX's MlKitAnalyzer to perform QR Code scanning. For QR Codes that encode Urls, this app will prompt the user to open the Url in a broswer. This app can be adapted to handle other types of QR Code data.
-
-The interesting part of the code is in `MainActivity.kt` in the `startCamera()` function. There, we set up BarcodeScannerOptions to match on QR Codes. Then we call `cameraController.setImageAnalysisAnalyzer` with an `MlKitAnalyzer` (available as of CameraX 1.2). We also pass in `COORDINATE_SYSTEM_VIEW_REFERENCED` so that CameraX will handle the cordinates coming off of the camera sensor, making it easy to draw a box around the QR Code. Finally, we create a QrCodeDrawable, which is a class defined in this sample, extending View, for displaying an overlay on the QR Code and handling tap events on the QR Code.
-
-You can open this project in Android Studio to explore the code further, and to build and run the application on a test device.
-
-## Screenshots
-
-
-
-## Command line options
-
-### Build
-
-To build the app directly from the command line, run:
-```sh
-./gradlew assembleDebug
-```
-
-### Test
-
-Unit testing and instrumented device testing share the same code. To test the app using Robolectric, no device required, run:
-```sh
-./gradlew test
-```
-
-To run the same tests in an Android device connected via ADB, run:
-```sh
-./gradlew connectedAndroidTest
-```
-
-Alternatively, test running configurations can be added to Android Studio for convenience (and a nice UI). To do that:
-1. Go to: `Run` > `Edit Configurations` > `Add New Configuration`.
-1. For Robolectric select `Android JUnit`, for connected device select `Android Instrumented Tests`.
-1. Select `app` module and `com.android.example.cameraxbasic.MainInstrumentedTest` class.
-1. Optional: Give the run configuration a name, like `test robolectric` or `test device`
diff --git a/CameraX-MLKit/app/.gitignore b/CameraX-MLKit/app/.gitignore
deleted file mode 100644
index 42afabfd..00000000
--- a/CameraX-MLKit/app/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
\ No newline at end of file
diff --git a/CameraX-MLKit/app/build.gradle b/CameraX-MLKit/app/build.gradle
deleted file mode 100644
index ff5a4f5c..00000000
--- a/CameraX-MLKit/app/build.gradle
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.
- */
-
-plugins {
- id 'com.android.application'
- id 'org.jetbrains.kotlin.android'
-}
-
-android {
- namespace 'com.example.camerax_mlkit'
- compileSdk 33
-
- defaultConfig {
- applicationId "com.example.camerax_mlkit"
- minSdk 21
- targetSdk 33
- versionCode 1
- versionName "1.0"
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
- kotlinOptions {
- jvmTarget = '1.8'
- }
- buildFeatures {
- viewBinding true
- }
-}
-
-dependencies {
-
- implementation 'androidx.core:core-ktx:1.7.0'
- implementation 'androidx.appcompat:appcompat:1.5.1'
- implementation 'com.google.android.material:material:1.7.0'
- implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
- implementation 'androidx.camera:camera-mlkit-vision:1.2.0-beta02'
- testImplementation 'junit:junit:4.13.2'
- androidTestImplementation 'androidx.test.ext:junit:1.1.3'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
-
- def camerax_version = "1.2.0-rc01"
- implementation "androidx.camera:camera-core:${camerax_version}"
- implementation "androidx.camera:camera-camera2:${camerax_version}"
- implementation "androidx.camera:camera-lifecycle:${camerax_version}"
- implementation "androidx.camera:camera-view:${camerax_version}"
-
- implementation 'com.google.mlkit:barcode-scanning:17.0.2'
-}
\ No newline at end of file
diff --git a/CameraX-MLKit/app/src/androidTest/java/com/example/camerax_mlkit/ExampleInstrumentedTest.kt b/CameraX-MLKit/app/src/androidTest/java/com/example/camerax_mlkit/ExampleInstrumentedTest.kt
deleted file mode 100644
index 32fe7e66..00000000
--- a/CameraX-MLKit/app/src/androidTest/java/com/example/camerax_mlkit/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.camerax_mlkit
-
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.ext.junit.runners.AndroidJUnit4
-
-import org.junit.Test
-import org.junit.runner.RunWith
-
-import org.junit.Assert.*
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-@RunWith(AndroidJUnit4::class)
-class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.example.camerax_mlkit", appContext.packageName)
- }
-}
\ No newline at end of file
diff --git a/CameraX-MLKit/app/src/main/AndroidManifest.xml b/CameraX-MLKit/app/src/main/AndroidManifest.xml
deleted file mode 100644
index 86f8fda8..00000000
--- a/CameraX-MLKit/app/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraX-MLKit/app/src/main/java/com/example/camerax_mlkit/MainActivity.kt b/CameraX-MLKit/app/src/main/java/com/example/camerax_mlkit/MainActivity.kt
deleted file mode 100644
index e47a5e7b..00000000
--- a/CameraX-MLKit/app/src/main/java/com/example/camerax_mlkit/MainActivity.kt
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.camerax_mlkit
-
-import android.Manifest
-import android.content.pm.PackageManager
-import android.os.Bundle
-import android.view.View
-import android.widget.Toast
-import androidx.appcompat.app.AppCompatActivity
-import androidx.camera.mlkit.vision.MlKitAnalyzer
-import androidx.camera.view.CameraController.COORDINATE_SYSTEM_VIEW_REFERENCED
-import androidx.camera.view.LifecycleCameraController
-import androidx.camera.view.PreviewView
-import androidx.core.app.ActivityCompat
-import androidx.core.content.ContextCompat
-import com.example.camerax_mlkit.databinding.ActivityMainBinding
-import com.google.mlkit.vision.barcode.BarcodeScanner
-import com.google.mlkit.vision.barcode.BarcodeScannerOptions
-import com.google.mlkit.vision.barcode.BarcodeScanning
-import com.google.mlkit.vision.barcode.common.Barcode
-import java.util.concurrent.ExecutorService
-import java.util.concurrent.Executors
-
-class MainActivity : AppCompatActivity() {
- private lateinit var viewBinding: ActivityMainBinding
- private lateinit var cameraExecutor: ExecutorService
- private lateinit var barcodeScanner: BarcodeScanner
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- viewBinding = ActivityMainBinding.inflate(layoutInflater)
- setContentView(viewBinding.root)
-
- // Request camera permissions
- if (allPermissionsGranted()) {
- startCamera()
- } else {
- ActivityCompat.requestPermissions(
- this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS
- )
- }
-
- cameraExecutor = Executors.newSingleThreadExecutor()
- }
-
- private fun startCamera() {
- var cameraController = LifecycleCameraController(baseContext)
- val previewView: PreviewView = viewBinding.viewFinder
-
- val options = BarcodeScannerOptions.Builder()
- .setBarcodeFormats(Barcode.FORMAT_QR_CODE)
- .build()
- barcodeScanner = BarcodeScanning.getClient(options)
-
- cameraController.setImageAnalysisAnalyzer(
- ContextCompat.getMainExecutor(this),
- MlKitAnalyzer(
- listOf(barcodeScanner),
- COORDINATE_SYSTEM_VIEW_REFERENCED,
- ContextCompat.getMainExecutor(this)
- ) { result: MlKitAnalyzer.Result? ->
- val barcodeResults = result?.getValue(barcodeScanner)
- if ((barcodeResults == null) ||
- (barcodeResults.size == 0) ||
- (barcodeResults.first() == null)
- ) {
- previewView.overlay.clear()
- previewView.setOnTouchListener { _, _ -> false } //no-op
- return@MlKitAnalyzer
- }
-
- val qrCodeViewModel = QrCodeViewModel(barcodeResults[0])
- val qrCodeDrawable = QrCodeDrawable(qrCodeViewModel)
-
- previewView.setOnTouchListener(qrCodeViewModel.qrCodeTouchCallback)
- previewView.overlay.clear()
- previewView.overlay.add(qrCodeDrawable)
- }
- )
-
- cameraController.bindToLifecycle(this)
- previewView.controller = cameraController
- }
-
- private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
- ContextCompat.checkSelfPermission(
- baseContext, it) == PackageManager.PERMISSION_GRANTED
- }
-
- override fun onDestroy() {
- super.onDestroy()
- cameraExecutor.shutdown()
- barcodeScanner.close()
- }
-
- companion object {
- private const val TAG = "CameraX-MLKit"
- private const val REQUEST_CODE_PERMISSIONS = 10
- private val REQUIRED_PERMISSIONS =
- mutableListOf (
- Manifest.permission.CAMERA
- ).toTypedArray()
- }
-
- override fun onRequestPermissionsResult(
- requestCode: Int, permissions: Array, grantResults:
- IntArray) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults)
- if (requestCode == REQUEST_CODE_PERMISSIONS) {
- if (allPermissionsGranted()) {
- startCamera()
- } else {
- Toast.makeText(this,
- "Permissions not granted by the user.",
- Toast.LENGTH_SHORT).show()
- finish()
- }
- }
- }
-}
\ No newline at end of file
diff --git a/CameraX-MLKit/app/src/main/java/com/example/camerax_mlkit/QrCodeDrawable.kt b/CameraX-MLKit/app/src/main/java/com/example/camerax_mlkit/QrCodeDrawable.kt
deleted file mode 100644
index d2c5a4d6..00000000
--- a/CameraX-MLKit/app/src/main/java/com/example/camerax_mlkit/QrCodeDrawable.kt
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.camerax_mlkit
-
-import android.content.Intent
-import android.graphics.*
-import android.graphics.drawable.Drawable
-import android.net.Uri
-import android.view.MotionEvent
-import android.view.View
-import com.google.mlkit.vision.barcode.common.Barcode
-
-/**
- * A Drawable that handles displaying a QR Code's data and a bounding box around the QR code.
- */
-class QrCodeDrawable(qrCodeViewModel: QrCodeViewModel) : Drawable() {
- private val boundingRectPaint = Paint().apply {
- style = Paint.Style.STROKE
- color = Color.YELLOW
- strokeWidth = 5F
- alpha = 200
- }
-
- private val contentRectPaint = Paint().apply {
- style = Paint.Style.FILL
- color = Color.YELLOW
- alpha = 255
- }
-
- private val contentTextPaint = Paint().apply {
- color = Color.DKGRAY
- alpha = 255
- textSize = 36F
- }
-
- private val qrCodeViewModel = qrCodeViewModel
- private val contentPadding = 25
- private var textWidth = contentTextPaint.measureText(qrCodeViewModel.qrContent).toInt()
-
- override fun draw(canvas: Canvas) {
- canvas.drawRect(qrCodeViewModel.boundingRect, boundingRectPaint)
- canvas.drawRect(
- Rect(
- qrCodeViewModel.boundingRect.left,
- qrCodeViewModel.boundingRect.bottom + contentPadding/2,
- qrCodeViewModel.boundingRect.left + textWidth + contentPadding*2,
- qrCodeViewModel.boundingRect.bottom + contentTextPaint.textSize.toInt() + contentPadding),
- contentRectPaint
- )
- canvas.drawText(
- qrCodeViewModel.qrContent,
- (qrCodeViewModel.boundingRect.left + contentPadding).toFloat(),
- (qrCodeViewModel.boundingRect.bottom + contentPadding*2).toFloat(),
- contentTextPaint
- )
- }
-
- override fun setAlpha(alpha: Int) {
- boundingRectPaint.alpha = alpha
- contentRectPaint.alpha = alpha
- contentTextPaint.alpha = alpha
- }
-
- override fun setColorFilter(colorFiter: ColorFilter?) {
- boundingRectPaint.colorFilter = colorFilter
- contentRectPaint.colorFilter = colorFilter
- contentTextPaint.colorFilter = colorFilter
- }
-
- @Deprecated("Deprecated in Java")
- override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
-}
\ No newline at end of file
diff --git a/CameraX-MLKit/app/src/main/java/com/example/camerax_mlkit/QrCodeViewModel.kt b/CameraX-MLKit/app/src/main/java/com/example/camerax_mlkit/QrCodeViewModel.kt
deleted file mode 100644
index 8f4d0529..00000000
--- a/CameraX-MLKit/app/src/main/java/com/example/camerax_mlkit/QrCodeViewModel.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.camerax_mlkit
-
-import android.content.Intent
-import android.graphics.Rect
-import android.net.Uri
-import android.view.MotionEvent
-import android.view.View
-import com.google.mlkit.vision.barcode.common.Barcode
-
-/**
- * A ViewModel for encapsulating the data for a QR Code, including the encoded data, the bounding
- * box, and the touch behavior on the QR Code.
- *
- * As is, this class only handles displaying the QR Code data if it's a URL. Other data types
- * can be handled by adding more cases of Barcode.TYPE_URL in the init block.
- */
-class QrCodeViewModel(barcode: Barcode) {
- var boundingRect: Rect = barcode.boundingBox!!
- var qrContent: String = ""
- var qrCodeTouchCallback = { v: View, e: MotionEvent -> false} //no-op
-
- init {
- when (barcode.valueType) {
- Barcode.TYPE_URL -> {
- qrContent = barcode.url!!.url!!
- qrCodeTouchCallback = { v: View, e: MotionEvent ->
- if (e.action == MotionEvent.ACTION_DOWN && boundingRect.contains(e.getX().toInt(), e.getY().toInt())) {
- val openBrowserIntent = Intent(Intent.ACTION_VIEW)
- openBrowserIntent.data = Uri.parse(qrContent)
- v.context.startActivity(openBrowserIntent)
- }
- true // return true from the callback to signify the event was handled
- }
- }
- // Add other QR Code types here to handle other types of data,
- // like Wifi credentials.
- else -> {
- qrContent = "Unsupported data type: ${barcode.rawValue.toString()}"
- }
- }
- }
-}
\ No newline at end of file
diff --git a/CameraX-MLKit/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/CameraX-MLKit/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index cfd913d4..00000000
--- a/CameraX-MLKit/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraX-MLKit/app/src/main/res/drawable/ic_launcher_background.xml b/CameraX-MLKit/app/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 9d7d0f81..00000000
--- a/CameraX-MLKit/app/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,186 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/CameraX-MLKit/app/src/main/res/layout/activity_main.xml b/CameraX-MLKit/app/src/main/res/layout/activity_main.xml
deleted file mode 100644
index 90d64126..00000000
--- a/CameraX-MLKit/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraX-MLKit/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/CameraX-MLKit/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 62222e32..00000000
--- a/CameraX-MLKit/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraX-MLKit/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/CameraX-MLKit/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 62222e32..00000000
--- a/CameraX-MLKit/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraX-MLKit/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/CameraX-MLKit/app/src/main/res/mipmap-hdpi/ic_launcher.webp
deleted file mode 100644
index c209e78e..00000000
Binary files a/CameraX-MLKit/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ
diff --git a/CameraX-MLKit/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/CameraX-MLKit/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
deleted file mode 100644
index b2dfe3d1..00000000
Binary files a/CameraX-MLKit/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/CameraX-MLKit/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/CameraX-MLKit/app/src/main/res/mipmap-mdpi/ic_launcher.webp
deleted file mode 100644
index 4f0f1d64..00000000
Binary files a/CameraX-MLKit/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ
diff --git a/CameraX-MLKit/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/CameraX-MLKit/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
deleted file mode 100644
index 62b611da..00000000
Binary files a/CameraX-MLKit/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/CameraX-MLKit/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/CameraX-MLKit/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
deleted file mode 100644
index 948a3070..00000000
Binary files a/CameraX-MLKit/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ
diff --git a/CameraX-MLKit/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/CameraX-MLKit/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
deleted file mode 100644
index 1b9a6956..00000000
Binary files a/CameraX-MLKit/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/CameraX-MLKit/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/CameraX-MLKit/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
deleted file mode 100644
index 28d4b77f..00000000
Binary files a/CameraX-MLKit/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ
diff --git a/CameraX-MLKit/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/CameraX-MLKit/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
deleted file mode 100644
index 9287f508..00000000
Binary files a/CameraX-MLKit/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/CameraX-MLKit/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/CameraX-MLKit/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
deleted file mode 100644
index aa7d6427..00000000
Binary files a/CameraX-MLKit/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ
diff --git a/CameraX-MLKit/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/CameraX-MLKit/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
deleted file mode 100644
index 9126ae37..00000000
Binary files a/CameraX-MLKit/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/CameraX-MLKit/app/src/main/res/values-night/themes.xml b/CameraX-MLKit/app/src/main/res/values-night/themes.xml
deleted file mode 100644
index 0469b37a..00000000
--- a/CameraX-MLKit/app/src/main/res/values-night/themes.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraX-MLKit/app/src/main/res/values/colors.xml b/CameraX-MLKit/app/src/main/res/values/colors.xml
deleted file mode 100644
index 40ea8717..00000000
--- a/CameraX-MLKit/app/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
- #FFBB86FC
- #FF6200EE
- #FF3700B3
- #FF03DAC5
- #FF018786
- #FF000000
- #FFFFFFFF
-
\ No newline at end of file
diff --git a/CameraX-MLKit/app/src/main/res/values/strings.xml b/CameraX-MLKit/app/src/main/res/values/strings.xml
deleted file mode 100644
index 6da27864..00000000
--- a/CameraX-MLKit/app/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
- Camerax-MLKit
-
\ No newline at end of file
diff --git a/CameraX-MLKit/app/src/main/res/values/themes.xml b/CameraX-MLKit/app/src/main/res/values/themes.xml
deleted file mode 100644
index ad352288..00000000
--- a/CameraX-MLKit/app/src/main/res/values/themes.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraX-MLKit/app/src/main/res/xml/backup_rules.xml b/CameraX-MLKit/app/src/main/res/xml/backup_rules.xml
deleted file mode 100644
index 2b095999..00000000
--- a/CameraX-MLKit/app/src/main/res/xml/backup_rules.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraX-MLKit/app/src/main/res/xml/data_extraction_rules.xml b/CameraX-MLKit/app/src/main/res/xml/data_extraction_rules.xml
deleted file mode 100644
index 84d45477..00000000
--- a/CameraX-MLKit/app/src/main/res/xml/data_extraction_rules.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraX-MLKit/app/src/test/java/com/example/camerax_mlkit/ExampleUnitTest.kt b/CameraX-MLKit/app/src/test/java/com/example/camerax_mlkit/ExampleUnitTest.kt
deleted file mode 100644
index 23514c04..00000000
--- a/CameraX-MLKit/app/src/test/java/com/example/camerax_mlkit/ExampleUnitTest.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.camerax_mlkit
-
-import org.junit.Test
-
-import org.junit.Assert.*
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
\ No newline at end of file
diff --git a/CameraX-MLKit/build.gradle b/CameraX-MLKit/build.gradle
deleted file mode 100644
index 1b6e7f1f..00000000
--- a/CameraX-MLKit/build.gradle
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.
- */
-
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
-plugins {
- id 'com.android.application' version '7.3.1' apply false
- id 'com.android.library' version '7.3.1' apply false
- id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
-}
\ No newline at end of file
diff --git a/CameraX-MLKit/gradle.properties b/CameraX-MLKit/gradle.properties
deleted file mode 100644
index ae849f63..00000000
--- a/CameraX-MLKit/gradle.properties
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# Copyright 2022 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
-#
-# https://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.
-#
-
-# Project-wide Gradle settings.
-# IDE (e.g. Android Studio) users:
-# Gradle settings configured through the IDE *will override*
-# any settings specified in this file.
-# For more details on how to configure your build environment visit
-# http://www.gradle.org/docs/current/userguide/build_environment.html
-# Specifies the JVM arguments used for the daemon process.
-# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
-# AndroidX package structure to make it clearer which packages are bundled with the
-# Android operating system, and which are packaged with your app's APK
-# https://developer.android.com/topic/libraries/support-library/androidx-rn
-android.useAndroidX=true
-# Kotlin code style for this project: "official" or "obsolete":
-kotlin.code.style=official
-# Enables namespacing of each library's R class so that its R class includes only the
-# resources declared in the library itself and none from the library's dependencies,
-# thereby reducing the size of the R class for that library
-android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/CameraX-MLKit/gradle/wrapper/gradle-wrapper.jar b/CameraX-MLKit/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index e708b1c0..00000000
Binary files a/CameraX-MLKit/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/CameraX-MLKit/gradle/wrapper/gradle-wrapper.properties b/CameraX-MLKit/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 1da035b3..00000000
--- a/CameraX-MLKit/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# Copyright 2022 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
-#
-# https://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.
-#
-
-#Mon Nov 07 12:22:22 EST 2022
-distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
-distributionPath=wrapper/dists
-zipStorePath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
diff --git a/CameraX-MLKit/gradlew b/CameraX-MLKit/gradlew
deleted file mode 100755
index 4f906e0c..00000000
--- a/CameraX-MLKit/gradlew
+++ /dev/null
@@ -1,185 +0,0 @@
-#!/usr/bin/env sh
-
-#
-# Copyright 2015 the original author or authors.
-#
-# 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
-#
-# https://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.
-#
-
-##############################################################################
-##
-## Gradle start up script for UN*X
-##
-##############################################################################
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn () {
- echo "$*"
-}
-
-die () {
- echo
- echo "$*"
- echo
- exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
- else
- JAVACMD="$JAVA_HOME/bin/java"
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin or MSYS, switch paths to Windows format before running java
-if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
- fi
- i=`expr $i + 1`
- done
- case $i in
- 0) set -- ;;
- 1) set -- "$args0" ;;
- 2) set -- "$args0" "$args1" ;;
- 3) set -- "$args0" "$args1" "$args2" ;;
- 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
-fi
-
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=`save "$@"`
-
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-
-exec "$JAVACMD" "$@"
diff --git a/CameraX-MLKit/gradlew.bat b/CameraX-MLKit/gradlew.bat
deleted file mode 100644
index ac1b06f9..00000000
--- a/CameraX-MLKit/gradlew.bat
+++ /dev/null
@@ -1,89 +0,0 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/CameraX-MLKit/screenshots/camerax-mlkit.png b/CameraX-MLKit/screenshots/camerax-mlkit.png
deleted file mode 100644
index d97657be..00000000
Binary files a/CameraX-MLKit/screenshots/camerax-mlkit.png and /dev/null differ
diff --git a/CameraX-MLKit/settings.gradle b/CameraX-MLKit/settings.gradle
deleted file mode 100644
index 71ce265f..00000000
--- a/CameraX-MLKit/settings.gradle
+++ /dev/null
@@ -1,16 +0,0 @@
-pluginManagement {
- repositories {
- gradlePluginPortal()
- google()
- mavenCentral()
- }
-}
-dependencyResolutionManagement {
- repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
- repositories {
- google()
- mavenCentral()
- }
-}
-rootProject.name = "Camerax-MLKit"
-include ':app'
diff --git a/CameraXAdvanced/.gitignore b/CameraXAdvanced/.gitignore
deleted file mode 100644
index 24bddc82..00000000
--- a/CameraXAdvanced/.gitignore
+++ /dev/null
@@ -1,60 +0,0 @@
-# Built application files
-app/release
-*.apk
-*.ap_
-
-# Files for the ART/Dalvik VM
-*.dex
-
-# Java class files
-*.class
-
-# Generated files
-bin/
-gen/
-out/
-
-# Gradle files
-.gradle/
-build/
-
-# Local configuration file (sdk path, etc)
-local.properties
-
-# Proguard folder generated by Eclipse
-proguard/
-
-# Log Files
-*.log
-
-# Android Studio Navigation editor temp files
-.navigation/
-
-# Android Studio captures folder
-captures/
-
-# IntelliJ
-*.iml
-.idea/*
-!.idea/runConfigurations
-
-# Keystore files
-*.jks
-
-# External native build folder generated in Android Studio 2.2 and later
-.externalNativeBuild
-
-# Google Services (e.g. APIs or Firebase)
-google-services.json
-
-# Freeline
-freeline.py
-freeline/
-freeline_project_description.json
-
-# fastlane
-fastlane/report.xml
-fastlane/Preview.html
-fastlane/screenshots
-fastlane/test_output
-fastlane/readme.md
diff --git a/CameraXAdvanced/README.md b/CameraXAdvanced/README.md
deleted file mode 100644
index 53d678b7..00000000
--- a/CameraXAdvanced/README.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# CameraX + Tensorflow Lite
-This is a collection of examples to demonstrate CameraX usage in different areas such as TFLite,
-and the list will grow along with CameraX advancing footprints.
-
-## TensorFlow Lite(TFLite)
-This example implements an Activity that performs real-time object detection on
-the live camera frames. It performs the following operations:
-1. Initializes camera preview and image analysis frame streams using CameraX
-2. Loads a mobilenet quantized model using Tensorflow Lite
-4. Performs inference on the transformed frames and reports the object predicted on the screen
-
-The whole pipeline is able to maintain 30 FPS on a Pixel 3 XL with:
-- the default image size from Camera (640x480)
-- the default tensor size (300 x 300)
-
-## Screenshots
-
-
diff --git a/CameraXAdvanced/build.gradle b/CameraXAdvanced/build.gradle
deleted file mode 100644
index 78f510b9..00000000
--- a/CameraXAdvanced/build.gradle
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2020 Google LLC
- *
- * 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
- *
- * https://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.
- */
-
-buildscript {
-
- ext {
- // Top-level variables used for versioning
- ext.kotlin_version = '1.5.21'
- ext.java_version = JavaVersion.VERSION_1_8
- }
-
- repositories {
- google()
- mavenCentral()
- }
-
- dependencies {
- classpath 'com.android.tools.build:gradle:7.2.2'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- classpath 'com.diffplug.spotless:spotless-plugin-gradle:5.11.1'
- }
-}
-
-allprojects {
- repositories {
- google()
- mavenCentral()
- maven { // repo for TFLite snapshot
- name 'ossrh-snapshot'
- url 'https://s01.oss.sonatype.org/content/repositories/snapshots'
- }
- }
-}
-
-subprojects {
- apply plugin: 'com.diffplug.spotless'
- spotless {
- java {
- target "**/*.java"
- trimTrailingWhitespace()
- removeUnusedImports()
- googleJavaFormat()
- endWithNewline()
- }
- kotlin {
- target "**/*.kt"
- trimTrailingWhitespace()
- endWithNewline()
- }
- }
-}
diff --git a/CameraXAdvanced/gradle.properties b/CameraXAdvanced/gradle.properties
deleted file mode 100644
index 23339e0d..00000000
--- a/CameraXAdvanced/gradle.properties
+++ /dev/null
@@ -1,21 +0,0 @@
-# Project-wide Gradle settings.
-# IDE (e.g. Android Studio) users:
-# Gradle settings configured through the IDE *will override*
-# any settings specified in this file.
-# For more details on how to configure your build environment visit
-# http://www.gradle.org/docs/current/userguide/build_environment.html
-# Specifies the JVM arguments used for the daemon process.
-# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx1536m
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
-# AndroidX package structure to make it clearer which packages are bundled with the
-# Android operating system, and which are packaged with your app's APK
-# https://developer.android.com/topic/libraries/support-library/androidx-rn
-android.useAndroidX=true
-# Automatically convert third-party libraries to use AndroidX
-android.enableJetifier=true
-# Kotlin code style for this project: "official" or "obsolete":
-kotlin.code.style=official
diff --git a/CameraXAdvanced/gradle/wrapper/gradle-wrapper.jar b/CameraXAdvanced/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index f6b961fd..00000000
Binary files a/CameraXAdvanced/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/CameraXAdvanced/gradle/wrapper/gradle-wrapper.properties b/CameraXAdvanced/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 2be8ad4d..00000000
--- a/CameraXAdvanced/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-#Wed Dec 16 12:00:46 WET 2020
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
diff --git a/CameraXAdvanced/gradlew b/CameraXAdvanced/gradlew
deleted file mode 100755
index cccdd3d5..00000000
--- a/CameraXAdvanced/gradlew
+++ /dev/null
@@ -1,172 +0,0 @@
-#!/usr/bin/env sh
-
-##############################################################################
-##
-## Gradle start up script for UN*X
-##
-##############################################################################
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn () {
- echo "$*"
-}
-
-die () {
- echo
- echo "$*"
- echo
- exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
- else
- JAVACMD="$JAVA_HOME/bin/java"
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
- fi
- i=$((i+1))
- done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
-fi
-
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=$(save "$@")
-
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-
-# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
-if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
- cd "$(dirname "$0")"
-fi
-
-exec "$JAVACMD" "$@"
diff --git a/CameraXAdvanced/gradlew.bat b/CameraXAdvanced/gradlew.bat
deleted file mode 100644
index f9553162..00000000
--- a/CameraXAdvanced/gradlew.bat
+++ /dev/null
@@ -1,84 +0,0 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/CameraXAdvanced/settings.gradle b/CameraXAdvanced/settings.gradle
deleted file mode 100644
index 844db508..00000000
--- a/CameraXAdvanced/settings.gradle
+++ /dev/null
@@ -1 +0,0 @@
-include 'tflite'
diff --git a/CameraXAdvanced/tflite/.gitignore b/CameraXAdvanced/tflite/.gitignore
deleted file mode 100644
index 796b96d1..00000000
--- a/CameraXAdvanced/tflite/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/CameraXAdvanced/tflite/build.gradle b/CameraXAdvanced/tflite/build.gradle
deleted file mode 100644
index b221bf77..00000000
--- a/CameraXAdvanced/tflite/build.gradle
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2020 Google LLC
- *
- * 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
- *
- * https://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.
- */
-
-apply plugin: "com.android.application"
-apply plugin: "kotlin-android"
-
-android {
- compileSdkVersion 31
- ndkVersion "21.3.6528147"
-
- defaultConfig {
- applicationId 'com.android.example.camerax.tflite'
- minSdkVersion 21
- targetSdkVersion 30
- versionCode 1
- versionName "0.0.1"
- }
-
- compileOptions {
- sourceCompatibility rootProject.ext.java_version
- targetCompatibility rootProject.ext.java_version
- }
-
- kotlinOptions {
- jvmTarget = rootProject.ext.java_version
- }
-
-
- buildFeatures {
- viewBinding true
- }
- androidResources {
- noCompress 'lite'
- }
-}
-
-dependencies {
- implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
-
- // App compat and UI things
- implementation 'androidx.appcompat:appcompat:1.3.1'
- implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
- implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
-
- // CameraX
- def camerax_version = "1.1.0-beta01"
- implementation "androidx.camera:camera-core:${camerax_version}"
- implementation "androidx.camera:camera-camera2:${camerax_version}"
- implementation "androidx.camera:camera-lifecycle:${camerax_version}"
- implementation "androidx.camera:camera-view:${camerax_version}"
- implementation "androidx.camera:camera-video:${camerax_version}"
-
-
- // Tensorflow lite dependencies
- implementation 'org.tensorflow:tensorflow-lite:2.9.0'
- implementation 'org.tensorflow:tensorflow-lite-gpu:2.9.0'
- implementation 'org.tensorflow:tensorflow-lite-support:0.4.2'
-
- testImplementation 'org.robolectric:robolectric:4.4'
- testImplementation 'junit:junit:4.13.2'
-}
diff --git a/CameraXAdvanced/tflite/screenshots/demo.gif b/CameraXAdvanced/tflite/screenshots/demo.gif
deleted file mode 100644
index 4d957bde..00000000
Binary files a/CameraXAdvanced/tflite/screenshots/demo.gif and /dev/null differ
diff --git a/CameraXAdvanced/tflite/screenshots/screenshot-1.jpg b/CameraXAdvanced/tflite/screenshots/screenshot-1.jpg
deleted file mode 100644
index 039874e3..00000000
Binary files a/CameraXAdvanced/tflite/screenshots/screenshot-1.jpg and /dev/null differ
diff --git a/CameraXAdvanced/tflite/src/main/AndroidManifest.xml b/CameraXAdvanced/tflite/src/main/AndroidManifest.xml
deleted file mode 100644
index 965fd839..00000000
--- a/CameraXAdvanced/tflite/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/CameraXAdvanced/tflite/src/main/assets/coco_ssd_mobilenet_v1_1.0_labels.txt b/CameraXAdvanced/tflite/src/main/assets/coco_ssd_mobilenet_v1_1.0_labels.txt
deleted file mode 100644
index 5a70ff82..00000000
--- a/CameraXAdvanced/tflite/src/main/assets/coco_ssd_mobilenet_v1_1.0_labels.txt
+++ /dev/null
@@ -1,91 +0,0 @@
-???
-person
-bicycle
-car
-motorcycle
-airplane
-bus
-train
-truck
-boat
-traffic light
-fire hydrant
-???
-stop sign
-parking meter
-bench
-bird
-cat
-dog
-horse
-sheep
-cow
-elephant
-bear
-zebra
-giraffe
-???
-backpack
-umbrella
-???
-???
-handbag
-tie
-suitcase
-frisbee
-skis
-snowboard
-sports ball
-kite
-baseball bat
-baseball glove
-skateboard
-surfboard
-tennis racket
-bottle
-???
-wine glass
-cup
-fork
-knife
-spoon
-bowl
-banana
-apple
-sandwich
-orange
-broccoli
-carrot
-hot dog
-pizza
-donut
-cake
-chair
-couch
-potted plant
-bed
-???
-dining table
-???
-???
-toilet
-???
-tv
-laptop
-mouse
-remote
-keyboard
-cell phone
-microwave
-oven
-toaster
-sink
-refrigerator
-???
-book
-clock
-vase
-scissors
-teddy bear
-hair drier
-toothbrush
diff --git a/CameraXAdvanced/tflite/src/main/assets/coco_ssd_mobilenet_v1_1.0_quant.tflite b/CameraXAdvanced/tflite/src/main/assets/coco_ssd_mobilenet_v1_1.0_quant.tflite
deleted file mode 100644
index 8015ee5d..00000000
Binary files a/CameraXAdvanced/tflite/src/main/assets/coco_ssd_mobilenet_v1_1.0_quant.tflite and /dev/null differ
diff --git a/CameraXAdvanced/tflite/src/main/java/com/example/android/camerax/tflite/CameraActivity.kt b/CameraXAdvanced/tflite/src/main/java/com/example/android/camerax/tflite/CameraActivity.kt
deleted file mode 100644
index 4f94dc84..00000000
--- a/CameraXAdvanced/tflite/src/main/java/com/example/android/camerax/tflite/CameraActivity.kt
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * Copyright 2020 Google LLC
- *
- * 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
- *
- * https://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.android.camerax.tflite
-
-import android.Manifest
-import android.annotation.SuppressLint
-import android.content.Context
-import android.content.pm.PackageManager
-import android.graphics.Bitmap
-import android.graphics.Matrix
-import android.graphics.RectF
-import android.os.Bundle
-import android.util.Log
-import android.util.Size
-import android.view.View
-import android.view.ViewGroup
-import androidx.appcompat.app.AppCompatActivity
-import androidx.camera.core.AspectRatio
-import androidx.camera.core.CameraSelector
-import androidx.camera.core.ImageAnalysis
-import androidx.camera.core.Preview
-import androidx.camera.lifecycle.ProcessCameraProvider
-import androidx.core.app.ActivityCompat
-import androidx.core.content.ContextCompat
-import androidx.lifecycle.LifecycleOwner
-import com.android.example.camerax.tflite.databinding.ActivityCameraBinding
-import org.tensorflow.lite.DataType
-import org.tensorflow.lite.Interpreter
-import org.tensorflow.lite.nnapi.NnApiDelegate
-import org.tensorflow.lite.support.common.FileUtil
-import org.tensorflow.lite.support.common.ops.NormalizeOp
-import org.tensorflow.lite.support.image.ImageProcessor
-import org.tensorflow.lite.support.image.TensorImage
-import org.tensorflow.lite.support.image.ops.ResizeOp
-import org.tensorflow.lite.support.image.ops.ResizeWithCropOrPadOp
-import org.tensorflow.lite.support.image.ops.Rot90Op
-import java.util.concurrent.Executors
-import java.util.concurrent.TimeUnit
-import kotlin.math.min
-import kotlin.random.Random
-
-
-/** Activity that displays the camera and performs object detection on the incoming frames */
-class CameraActivity : AppCompatActivity() {
-
- private lateinit var activityCameraBinding: ActivityCameraBinding
-
- private lateinit var bitmapBuffer: Bitmap
-
- private val executor = Executors.newSingleThreadExecutor()
- private val permissions = listOf(Manifest.permission.CAMERA)
- private val permissionsRequestCode = Random.nextInt(0, 10000)
-
- private var lensFacing: Int = CameraSelector.LENS_FACING_BACK
- private val isFrontFacing get() = lensFacing == CameraSelector.LENS_FACING_FRONT
-
- private var pauseAnalysis = false
- private var imageRotationDegrees: Int = 0
- private val tfImageBuffer = TensorImage(DataType.UINT8)
-
- private val tfImageProcessor by lazy {
- val cropSize = minOf(bitmapBuffer.width, bitmapBuffer.height)
- ImageProcessor.Builder()
- .add(ResizeWithCropOrPadOp(cropSize, cropSize))
- .add(ResizeOp(
- tfInputSize.height, tfInputSize.width, ResizeOp.ResizeMethod.NEAREST_NEIGHBOR))
- .add(Rot90Op(-imageRotationDegrees / 90))
- .add(NormalizeOp(0f, 1f))
- .build()
- }
-
- private val nnApiDelegate by lazy {
- NnApiDelegate()
- }
-
- private val tflite by lazy {
- Interpreter(
- FileUtil.loadMappedFile(this, MODEL_PATH),
- Interpreter.Options().addDelegate(nnApiDelegate))
- }
- private val detector by lazy {
- ObjectDetectionHelper(
- tflite,
- FileUtil.loadLabels(this, LABELS_PATH)
- )
- }
-
- private val tfInputSize by lazy {
- val inputIndex = 0
- val inputShape = tflite.getInputTensor(inputIndex).shape()
- Size(inputShape[2], inputShape[1]) // Order of axis is: {1, height, width, 3}
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- activityCameraBinding = ActivityCameraBinding.inflate(layoutInflater)
- setContentView(activityCameraBinding.root)
-
- activityCameraBinding.cameraCaptureButton.setOnClickListener {
-
- // Disable all camera controls
- it.isEnabled = false
-
- if (pauseAnalysis) {
- // If image analysis is in paused state, resume it
- pauseAnalysis = false
- activityCameraBinding.imagePredicted.visibility = View.GONE
-
- } else {
- // Otherwise, pause image analysis and freeze image
- pauseAnalysis = true
- val matrix = Matrix().apply {
- postRotate(imageRotationDegrees.toFloat())
- if (isFrontFacing) postScale(-1f, 1f)
- }
- val uprightImage = Bitmap.createBitmap(
- bitmapBuffer, 0, 0, bitmapBuffer.width, bitmapBuffer.height, matrix, true)
- activityCameraBinding.imagePredicted.setImageBitmap(uprightImage)
- activityCameraBinding.imagePredicted.visibility = View.VISIBLE
- }
-
- // Re-enable camera controls
- it.isEnabled = true
- }
- }
-
- override fun onDestroy() {
-
- // Terminate all outstanding analyzing jobs (if there is any).
- executor.apply {
- shutdown()
- awaitTermination(1000, TimeUnit.MILLISECONDS)
- }
-
- // Release TFLite resources.
- tflite.close()
- nnApiDelegate.close()
-
- super.onDestroy()
- }
-
- /** Declare and bind preview and analysis use cases */
- @SuppressLint("UnsafeExperimentalUsageError")
- private fun bindCameraUseCases() = activityCameraBinding.viewFinder.post {
-
- val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
- cameraProviderFuture.addListener ({
-
- // Camera provider is now guaranteed to be available
- val cameraProvider = cameraProviderFuture.get()
-
- // Set up the view finder use case to display camera preview
- val preview = Preview.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_4_3)
- .setTargetRotation(activityCameraBinding.viewFinder.display.rotation)
- .build()
-
- // Set up the image analysis use case which will process frames in real time
- val imageAnalysis = ImageAnalysis.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_4_3)
- .setTargetRotation(activityCameraBinding.viewFinder.display.rotation)
- .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
- .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
- .build()
-
- var frameCounter = 0
- var lastFpsTimestamp = System.currentTimeMillis()
-
- imageAnalysis.setAnalyzer(executor, ImageAnalysis.Analyzer { image ->
- if (!::bitmapBuffer.isInitialized) {
- // The image rotation and RGB image buffer are initialized only once
- // the analyzer has started running
- imageRotationDegrees = image.imageInfo.rotationDegrees
- bitmapBuffer = Bitmap.createBitmap(
- image.width, image.height, Bitmap.Config.ARGB_8888)
- }
-
- // Early exit: image analysis is in paused state
- if (pauseAnalysis) {
- image.close()
- return@Analyzer
- }
-
- // Copy out RGB bits to our shared buffer
- image.use { bitmapBuffer.copyPixelsFromBuffer(image.planes[0].buffer) }
-
- // Process the image in Tensorflow
- val tfImage = tfImageProcessor.process(tfImageBuffer.apply { load(bitmapBuffer) })
-
- // Perform the object detection for the current frame
- val predictions = detector.predict(tfImage)
-
- // Report only the top prediction
- reportPrediction(predictions.maxByOrNull { it.score })
-
- // Compute the FPS of the entire pipeline
- val frameCount = 10
- if (++frameCounter % frameCount == 0) {
- frameCounter = 0
- val now = System.currentTimeMillis()
- val delta = now - lastFpsTimestamp
- val fps = 1000 * frameCount.toFloat() / delta
- Log.d(TAG, "FPS: ${"%.02f".format(fps)} with tensorSize: ${tfImage.width} x ${tfImage.height}")
- lastFpsTimestamp = now
- }
- })
-
- // Create a new camera selector each time, enforcing lens facing
- val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
-
- // Apply declared configs to CameraX using the same lifecycle owner
- cameraProvider.unbindAll()
- cameraProvider.bindToLifecycle(
- this as LifecycleOwner, cameraSelector, preview, imageAnalysis)
-
- // Use the camera object to link our preview use case with the view
- preview.setSurfaceProvider(activityCameraBinding.viewFinder.surfaceProvider)
-
- }, ContextCompat.getMainExecutor(this))
- }
-
- private fun reportPrediction(
- prediction: ObjectDetectionHelper.ObjectPrediction?
- ) = activityCameraBinding.viewFinder.post {
-
- // Early exit: if prediction is not good enough, don't report it
- if (prediction == null || prediction.score < ACCURACY_THRESHOLD) {
- activityCameraBinding.boxPrediction.visibility = View.GONE
- activityCameraBinding.textPrediction.visibility = View.GONE
- return@post
- }
-
- // Location has to be mapped to our local coordinates
- val location = mapOutputCoordinates(prediction.location)
-
- // Update the text and UI
- activityCameraBinding.textPrediction.text = "${"%.2f".format(prediction.score)} ${prediction.label}"
- (activityCameraBinding.boxPrediction.layoutParams as ViewGroup.MarginLayoutParams).apply {
- topMargin = location.top.toInt()
- leftMargin = location.left.toInt()
- width = min(activityCameraBinding.viewFinder.width, location.right.toInt() - location.left.toInt())
- height = min(activityCameraBinding.viewFinder.height, location.bottom.toInt() - location.top.toInt())
- }
-
- // Make sure all UI elements are visible
- activityCameraBinding.boxPrediction.visibility = View.VISIBLE
- activityCameraBinding.textPrediction.visibility = View.VISIBLE
- }
-
- /**
- * Helper function used to map the coordinates for objects coming out of
- * the model into the coordinates that the user sees on the screen.
- */
- private fun mapOutputCoordinates(location: RectF): RectF {
-
- // Step 1: map location to the preview coordinates
- val previewLocation = RectF(
- location.left * activityCameraBinding.viewFinder.width,
- location.top * activityCameraBinding.viewFinder.height,
- location.right * activityCameraBinding.viewFinder.width,
- location.bottom * activityCameraBinding.viewFinder.height
- )
-
- // Step 2: compensate for camera sensor orientation and mirroring
- val isFrontFacing = lensFacing == CameraSelector.LENS_FACING_FRONT
- val correctedLocation = if (isFrontFacing) {
- RectF(
- activityCameraBinding.viewFinder.width - previewLocation.right,
- previewLocation.top,
- activityCameraBinding.viewFinder.width - previewLocation.left,
- previewLocation.bottom)
- } else {
- previewLocation
- }
-
- // Step 3: compensate for 1:1 to 4:3 aspect ratio conversion + small margin
- val margin = 0.1f
- val requestedRatio = 4f / 3f
- val midX = (correctedLocation.left + correctedLocation.right) / 2f
- val midY = (correctedLocation.top + correctedLocation.bottom) / 2f
- return if (activityCameraBinding.viewFinder.width < activityCameraBinding.viewFinder.height) {
- RectF(
- midX - (1f + margin) * requestedRatio * correctedLocation.width() / 2f,
- midY - (1f - margin) * correctedLocation.height() / 2f,
- midX + (1f + margin) * requestedRatio * correctedLocation.width() / 2f,
- midY + (1f - margin) * correctedLocation.height() / 2f
- )
- } else {
- RectF(
- midX - (1f - margin) * correctedLocation.width() / 2f,
- midY - (1f + margin) * requestedRatio * correctedLocation.height() / 2f,
- midX + (1f - margin) * correctedLocation.width() / 2f,
- midY + (1f + margin) * requestedRatio * correctedLocation.height() / 2f
- )
- }
- }
-
- override fun onResume() {
- super.onResume()
-
- // Request permissions each time the app resumes, since they can be revoked at any time
- if (!hasPermissions(this)) {
- ActivityCompat.requestPermissions(
- this, permissions.toTypedArray(), permissionsRequestCode)
- } else {
- bindCameraUseCases()
- }
- }
-
- override fun onRequestPermissionsResult(
- requestCode: Int,
- permissions: Array,
- grantResults: IntArray
- ) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults)
- if (requestCode == permissionsRequestCode && hasPermissions(this)) {
- bindCameraUseCases()
- } else {
- finish() // If we don't have the required permissions, we can't run
- }
- }
-
- /** Convenience method used to check if all permissions required by this app are granted */
- private fun hasPermissions(context: Context) = permissions.all {
- ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
- }
-
- companion object {
- private val TAG = CameraActivity::class.java.simpleName
-
- private const val ACCURACY_THRESHOLD = 0.5f
- private const val MODEL_PATH = "coco_ssd_mobilenet_v1_1.0_quant.tflite"
- private const val LABELS_PATH = "coco_ssd_mobilenet_v1_1.0_labels.txt"
- }
-}
diff --git a/CameraXAdvanced/tflite/src/main/java/com/example/android/camerax/tflite/ObjectDetectionHelper.kt b/CameraXAdvanced/tflite/src/main/java/com/example/android/camerax/tflite/ObjectDetectionHelper.kt
deleted file mode 100644
index a22025d8..00000000
--- a/CameraXAdvanced/tflite/src/main/java/com/example/android/camerax/tflite/ObjectDetectionHelper.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2020 Google LLC
- *
- * 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
- *
- * https://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.android.camerax.tflite
-
-import android.graphics.RectF
-import org.tensorflow.lite.Interpreter
-import org.tensorflow.lite.support.image.TensorImage
-
-/**
- * Helper class used to communicate between our app and the TF object detection model
- */
-class ObjectDetectionHelper(private val tflite: Interpreter, private val labels: List) {
-
- /** Abstraction object that wraps a prediction output in an easy to parse way */
- data class ObjectPrediction(val location: RectF, val label: String, val score: Float)
-
- private val locations = arrayOf(Array(OBJECT_COUNT) { FloatArray(4) })
- private val labelIndices = arrayOf(FloatArray(OBJECT_COUNT))
- private val scores = arrayOf(FloatArray(OBJECT_COUNT))
-
- private val outputBuffer = mapOf(
- 0 to locations,
- 1 to labelIndices,
- 2 to scores,
- 3 to FloatArray(1)
- )
-
- val predictions get() = (0 until OBJECT_COUNT).map {
- ObjectPrediction(
-
- // The locations are an array of [0, 1] floats for [top, left, bottom, right]
- location = locations[0][it].let {
- RectF(it[1], it[0], it[3], it[2])
- },
-
- // SSD Mobilenet V1 Model assumes class 0 is background class
- // in label file and class labels start from 1 to number_of_classes + 1,
- // while outputClasses correspond to class index from 0 to number_of_classes
- label = labels[1 + labelIndices[0][it].toInt()],
-
- // Score is a single value of [0, 1]
- score = scores[0][it]
- )
- }
-
- fun predict(image: TensorImage): List {
- tflite.runForMultipleInputsOutputs(arrayOf(image.buffer), outputBuffer)
- return predictions
- }
-
- companion object {
- const val OBJECT_COUNT = 10
- }
-}
\ No newline at end of file
diff --git a/CameraXAdvanced/tflite/src/main/res/drawable/ic_launcher_background.xml b/CameraXAdvanced/tflite/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 268681bd..00000000
--- a/CameraXAdvanced/tflite/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,89 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/CameraXAdvanced/tflite/src/main/res/drawable/ic_shutter.xml b/CameraXAdvanced/tflite/src/main/res/drawable/ic_shutter.xml
deleted file mode 100644
index ab352baa..00000000
--- a/CameraXAdvanced/tflite/src/main/res/drawable/ic_shutter.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXAdvanced/tflite/src/main/res/drawable/ic_shutter_focused.xml b/CameraXAdvanced/tflite/src/main/res/drawable/ic_shutter_focused.xml
deleted file mode 100644
index fa852acd..00000000
--- a/CameraXAdvanced/tflite/src/main/res/drawable/ic_shutter_focused.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraXAdvanced/tflite/src/main/res/drawable/ic_shutter_normal.xml b/CameraXAdvanced/tflite/src/main/res/drawable/ic_shutter_normal.xml
deleted file mode 100644
index 25a10e15..00000000
--- a/CameraXAdvanced/tflite/src/main/res/drawable/ic_shutter_normal.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraXAdvanced/tflite/src/main/res/drawable/ic_shutter_pressed.xml b/CameraXAdvanced/tflite/src/main/res/drawable/ic_shutter_pressed.xml
deleted file mode 100644
index fa852acd..00000000
--- a/CameraXAdvanced/tflite/src/main/res/drawable/ic_shutter_pressed.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraXAdvanced/tflite/src/main/res/drawable/shape_rectangle.xml b/CameraXAdvanced/tflite/src/main/res/drawable/shape_rectangle.xml
deleted file mode 100644
index 5365e4c3..00000000
--- a/CameraXAdvanced/tflite/src/main/res/drawable/shape_rectangle.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXAdvanced/tflite/src/main/res/layout-land/activity_camera.xml b/CameraXAdvanced/tflite/src/main/res/layout-land/activity_camera.xml
deleted file mode 100644
index 1cc66f16..00000000
--- a/CameraXAdvanced/tflite/src/main/res/layout-land/activity_camera.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXAdvanced/tflite/src/main/res/layout/activity_camera.xml b/CameraXAdvanced/tflite/src/main/res/layout/activity_camera.xml
deleted file mode 100644
index c094f944..00000000
--- a/CameraXAdvanced/tflite/src/main/res/layout/activity_camera.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXAdvanced/tflite/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/CameraXAdvanced/tflite/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index fe90d56c..00000000
--- a/CameraXAdvanced/tflite/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXAdvanced/tflite/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/CameraXAdvanced/tflite/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index fe90d56c..00000000
--- a/CameraXAdvanced/tflite/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXAdvanced/tflite/src/main/res/mipmap-hdpi/ic_launcher.png b/CameraXAdvanced/tflite/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index edd7b363..00000000
Binary files a/CameraXAdvanced/tflite/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/CameraXAdvanced/tflite/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/CameraXAdvanced/tflite/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
deleted file mode 100644
index a050bf23..00000000
Binary files a/CameraXAdvanced/tflite/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/CameraXAdvanced/tflite/src/main/res/mipmap-hdpi/ic_launcher_round.png b/CameraXAdvanced/tflite/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index ec13a435..00000000
Binary files a/CameraXAdvanced/tflite/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/CameraXAdvanced/tflite/src/main/res/mipmap-mdpi/ic_launcher.png b/CameraXAdvanced/tflite/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 6fd4f910..00000000
Binary files a/CameraXAdvanced/tflite/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/CameraXAdvanced/tflite/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/CameraXAdvanced/tflite/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
deleted file mode 100644
index df81982e..00000000
Binary files a/CameraXAdvanced/tflite/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/CameraXAdvanced/tflite/src/main/res/mipmap-mdpi/ic_launcher_round.png b/CameraXAdvanced/tflite/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 26160487..00000000
Binary files a/CameraXAdvanced/tflite/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/CameraXAdvanced/tflite/src/main/res/mipmap-xhdpi/ic_launcher.png b/CameraXAdvanced/tflite/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index fdae11da..00000000
Binary files a/CameraXAdvanced/tflite/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/CameraXAdvanced/tflite/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/CameraXAdvanced/tflite/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
deleted file mode 100644
index 667d5669..00000000
Binary files a/CameraXAdvanced/tflite/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/CameraXAdvanced/tflite/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/CameraXAdvanced/tflite/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index cb11f725..00000000
Binary files a/CameraXAdvanced/tflite/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/CameraXAdvanced/tflite/src/main/res/mipmap-xxhdpi/ic_launcher.png b/CameraXAdvanced/tflite/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 5d4fa36c..00000000
Binary files a/CameraXAdvanced/tflite/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/CameraXAdvanced/tflite/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/CameraXAdvanced/tflite/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
deleted file mode 100644
index 33dc1c6e..00000000
Binary files a/CameraXAdvanced/tflite/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/CameraXAdvanced/tflite/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/CameraXAdvanced/tflite/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 07d13934..00000000
Binary files a/CameraXAdvanced/tflite/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/CameraXAdvanced/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/CameraXAdvanced/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 675e3138..00000000
Binary files a/CameraXAdvanced/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/CameraXAdvanced/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/CameraXAdvanced/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
deleted file mode 100644
index 9f9808a0..00000000
Binary files a/CameraXAdvanced/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/CameraXAdvanced/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/CameraXAdvanced/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 54e07e05..00000000
Binary files a/CameraXAdvanced/tflite/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/CameraXAdvanced/tflite/src/main/res/values/dimens.xml b/CameraXAdvanced/tflite/src/main/res/values/dimens.xml
deleted file mode 100644
index 53948b64..00000000
--- a/CameraXAdvanced/tflite/src/main/res/values/dimens.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
- 16dp
- 32dp
- 48dp
- 64dp
- 92dp
-
- 4dp
- 8dp
- 16dp
-
- 32dp
- 64dp
- 92dp
-
- 80dp
-
\ No newline at end of file
diff --git a/CameraXAdvanced/tflite/src/main/res/values/strings.xml b/CameraXAdvanced/tflite/src/main/res/values/strings.xml
deleted file mode 100644
index dbfb4c04..00000000
--- a/CameraXAdvanced/tflite/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- Camera Object Detector
- Capture
- UNKNOWN
-
diff --git a/CameraXAdvanced/tflite/src/main/res/values/styles.xml b/CameraXAdvanced/tflite/src/main/res/values/styles.xml
deleted file mode 100644
index 8bfac621..00000000
--- a/CameraXAdvanced/tflite/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/CameraXAdvanced/utils/.gitignore b/CameraXAdvanced/utils/.gitignore
deleted file mode 100644
index 38c58757..00000000
--- a/CameraXAdvanced/utils/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/build
-.idea
\ No newline at end of file
diff --git a/CameraXAdvanced/utils/README.md b/CameraXAdvanced/utils/README.md
deleted file mode 100644
index 7a10722f..00000000
--- a/CameraXAdvanced/utils/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-Do not modify code under this folder outside of `CameraUtils`, it is copied
-automatically by `.github/scripts/copy_utils.sh`.
\ No newline at end of file
diff --git a/CameraXAdvanced/utils/build.gradle b/CameraXAdvanced/utils/build.gradle
deleted file mode 100644
index 1ef65fa3..00000000
--- a/CameraXAdvanced/utils/build.gradle
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.
- */
-
-apply plugin: 'com.android.library'
-apply plugin: 'kotlin-android'
-
-android {
- compileSdkVersion 29
-
- defaultConfig {
- minSdkVersion 21
- targetSdkVersion 29
- versionCode 1
- versionName "1.0"
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles 'consumer-rules.pro'
- }
-
- compileOptions {
- sourceCompatibility rootProject.ext.java_version
- targetCompatibility rootProject.ext.java_version
- }
-
- kotlinOptions {
- jvmTarget = "$rootProject.ext.java_version"
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
-}
-
-dependencies {
-
- // Kotlin lang
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.4'
-
- // App compat and UI things
- implementation 'androidx.appcompat:appcompat:1.1.0'
- implementation 'androidx.recyclerview:recyclerview:1.1.0'
-
- // EXIF Interface
- implementation 'androidx.exifinterface:exifinterface:1.2.0'
-
- // Unit testing
- testImplementation 'androidx.test.ext:junit:1.1.1'
- testImplementation 'androidx.test:rules:1.2.0'
- testImplementation 'androidx.test:runner:1.2.0'
- testImplementation 'androidx.test.espresso:espresso-core:3.2.0'
- testImplementation 'org.robolectric:robolectric:4.3.1'
-
- // Instrumented testing
- androidTestImplementation 'androidx.test.ext:junit:1.1.1'
- androidTestImplementation 'androidx.test:rules:1.2.0'
- androidTestImplementation 'androidx.test:runner:1.2.0'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
-}
diff --git a/CameraXAdvanced/utils/src/main/AndroidManifest.xml b/CameraXAdvanced/utils/src/main/AndroidManifest.xml
deleted file mode 100644
index 2e13c37a..00000000
--- a/CameraXAdvanced/utils/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
diff --git a/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/AutoFitSurfaceView.kt b/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/AutoFitSurfaceView.kt
deleted file mode 100644
index 3d900d19..00000000
--- a/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/AutoFitSurfaceView.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.content.Context
-import android.util.AttributeSet
-import android.util.Log
-import android.view.SurfaceView
-import kotlin.math.roundToInt
-
-/**
- * A [SurfaceView] that can be adjusted to a specified aspect ratio and
- * performs center-crop transformation of input frames.
- */
-class AutoFitSurfaceView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyle: Int = 0
-) : SurfaceView(context, attrs, defStyle) {
-
- private var aspectRatio = 0f
-
- /**
- * Sets the aspect ratio for this view. The size of the view will be
- * measured based on the ratio calculated from the parameters.
- *
- * @param width Camera resolution horizontal size
- * @param height Camera resolution vertical size
- */
- fun setAspectRatio(width: Int, height: Int) {
- require(width > 0 && height > 0) { "Size cannot be negative" }
- aspectRatio = width.toFloat() / height.toFloat()
- holder.setFixedSize(width, height)
- requestLayout()
- }
-
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec)
- val width = MeasureSpec.getSize(widthMeasureSpec)
- val height = MeasureSpec.getSize(heightMeasureSpec)
- if (aspectRatio == 0f) {
- setMeasuredDimension(width, height)
- } else {
-
- // Performs center-crop transformation of the camera frames
- val newWidth: Int
- val newHeight: Int
- val actualRatio = if (width > height) aspectRatio else 1f / aspectRatio
- if (width < height * actualRatio) {
- newHeight = height
- newWidth = (height * actualRatio).roundToInt()
- } else {
- newWidth = width
- newHeight = (width / actualRatio).roundToInt()
- }
-
- Log.d(TAG, "Measured dimensions set: $newWidth x $newHeight")
- setMeasuredDimension(newWidth, newHeight)
- }
- }
-
- companion object {
- private val TAG = AutoFitSurfaceView::class.java.simpleName
- }
-}
diff --git a/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/CameraSizes.kt b/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/CameraSizes.kt
deleted file mode 100644
index 6db01d31..00000000
--- a/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/CameraSizes.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.graphics.Point
-import android.hardware.camera2.CameraCharacteristics
-import android.hardware.camera2.params.StreamConfigurationMap
-import android.util.Size
-import android.view.Display
-import kotlin.math.max
-import kotlin.math.min
-
-/** Helper class used to pre-compute shortest and longest sides of a [Size] */
-class SmartSize(width: Int, height: Int) {
- var size = Size(width, height)
- var long = max(size.width, size.height)
- var short = min(size.width, size.height)
- override fun toString() = "SmartSize(${long}x${short})"
-}
-
-/** Standard High Definition size for pictures and video */
-val SIZE_1080P: SmartSize = SmartSize(1920, 1080)
-
-/** Returns a [SmartSize] object for the given [Display] */
-fun getDisplaySmartSize(display: Display): SmartSize {
- val outPoint = Point()
- display.getRealSize(outPoint)
- return SmartSize(outPoint.x, outPoint.y)
-}
-
-/**
- * Returns the largest available PREVIEW size. For more information, see:
- * https://d.android.com/reference/android/hardware/camera2/CameraDevice and
- * https://developer.android.com/reference/android/hardware/camera2/params/StreamConfigurationMap
- */
-fun getPreviewOutputSize(
- display: Display,
- characteristics: CameraCharacteristics,
- targetClass: Class,
- format: Int? = null
-): Size {
-
- // Find which is smaller: screen or 1080p
- val screenSize = getDisplaySmartSize(display)
- val hdScreen = screenSize.long >= SIZE_1080P.long || screenSize.short >= SIZE_1080P.short
- val maxSize = if (hdScreen) SIZE_1080P else screenSize
-
- // If image format is provided, use it to determine supported sizes; else use target class
- val config = characteristics.get(
- CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
- if (format == null)
- assert(StreamConfigurationMap.isOutputSupportedFor(targetClass))
- else
- assert(config.isOutputSupportedFor(format))
- val allSizes = if (format == null)
- config.getOutputSizes(targetClass) else config.getOutputSizes(format)
-
- // Get available sizes and sort them by area from largest to smallest
- val validSizes = allSizes
- .sortedWith(compareBy { it.height * it.width })
- .map { SmartSize(it.width, it.height) }.reversed()
-
- // Then, get the largest output size that is smaller or equal than our max size
- return validSizes.first { it.long <= maxSize.long && it.short <= maxSize.short }.size
-}
\ No newline at end of file
diff --git a/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/ExifUtils.kt b/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/ExifUtils.kt
deleted file mode 100644
index 561c14b3..00000000
--- a/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/ExifUtils.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.graphics.Bitmap
-import android.graphics.Matrix
-import android.util.Log
-import androidx.exifinterface.media.ExifInterface
-
-private const val TAG: String = "ExifUtils"
-
-/** Transforms rotation and mirroring information into one of the [ExifInterface] constants */
-fun computeExifOrientation(rotationDegrees: Int, mirrored: Boolean) = when {
- rotationDegrees == 0 && !mirrored -> ExifInterface.ORIENTATION_NORMAL
- rotationDegrees == 0 && mirrored -> ExifInterface.ORIENTATION_FLIP_HORIZONTAL
- rotationDegrees == 180 && !mirrored -> ExifInterface.ORIENTATION_ROTATE_180
- rotationDegrees == 180 && mirrored -> ExifInterface.ORIENTATION_FLIP_VERTICAL
- rotationDegrees == 270 && mirrored -> ExifInterface.ORIENTATION_TRANSVERSE
- rotationDegrees == 90 && !mirrored -> ExifInterface.ORIENTATION_ROTATE_90
- rotationDegrees == 90 && mirrored -> ExifInterface.ORIENTATION_TRANSPOSE
- rotationDegrees == 270 && mirrored -> ExifInterface.ORIENTATION_ROTATE_270
- rotationDegrees == 270 && !mirrored -> ExifInterface.ORIENTATION_TRANSVERSE
- else -> ExifInterface.ORIENTATION_UNDEFINED
-}
-
-/**
- * Helper function used to convert an EXIF orientation enum into a transformation matrix
- * that can be applied to a bitmap.
- *
- * @return matrix - Transformation required to properly display [Bitmap]
- */
-fun decodeExifOrientation(exifOrientation: Int): Matrix {
- val matrix = Matrix()
-
- // Apply transformation corresponding to declared EXIF orientation
- when (exifOrientation) {
- ExifInterface.ORIENTATION_NORMAL -> Unit
- ExifInterface.ORIENTATION_UNDEFINED -> Unit
- ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90F)
- ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180F)
- ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270F)
- ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.postScale(-1F, 1F)
- ExifInterface.ORIENTATION_FLIP_VERTICAL -> matrix.postScale(1F, -1F)
- ExifInterface.ORIENTATION_TRANSPOSE -> {
- matrix.postScale(-1F, 1F)
- matrix.postRotate(270F)
- }
- ExifInterface.ORIENTATION_TRANSVERSE -> {
- matrix.postScale(-1F, 1F)
- matrix.postRotate(90F)
- }
-
- // Error out if the EXIF orientation is invalid
- else -> Log.e(TAG, "Invalid orientation: $exifOrientation")
- }
-
- // Return the resulting matrix
- return matrix
-}
diff --git a/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/GenericListAdapter.kt b/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/GenericListAdapter.kt
deleted file mode 100644
index a55af278..00000000
--- a/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/GenericListAdapter.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.recyclerview.widget.RecyclerView
-
-/** Type helper used for the callback triggered once our view has been bound */
-typealias BindCallback = (view: View, data: T, position: Int) -> Unit
-
-/** List adapter for generic types, intended used for small-medium lists of data */
-class GenericListAdapter(
- private val dataset: List,
- private val itemLayoutId: Int? = null,
- private val itemViewFactory: (() -> View)? = null,
- private val onBind: BindCallback
-) : RecyclerView.Adapter() {
-
- class GenericListViewHolder(val view: View) : RecyclerView.ViewHolder(view)
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = GenericListViewHolder(when {
- itemViewFactory != null -> itemViewFactory.invoke()
- itemLayoutId != null -> {
- LayoutInflater.from(parent.context)
- .inflate(itemLayoutId, parent, false)
- }
- else -> {
- throw IllegalStateException(
- "Either the layout ID or the view factory need to be non-null")
- }
- })
-
- override fun onBindViewHolder(holder: GenericListViewHolder, position: Int) {
- if (position < 0 || position > dataset.size) return
- onBind(holder.view, dataset[position], position)
- }
-
- override fun getItemCount() = dataset.size
-}
\ No newline at end of file
diff --git a/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt b/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt
deleted file mode 100644
index f9d9a470..00000000
--- a/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.content.Context
-import android.hardware.camera2.CameraCharacteristics
-import android.view.OrientationEventListener
-import android.view.Surface
-import androidx.lifecycle.LiveData
-
-
-/**
- * Calculates closest 90-degree orientation to compensate for the device
- * rotation relative to sensor orientation, i.e., allows user to see camera
- * frames with the expected orientation.
- */
-class OrientationLiveData(
- context: Context,
- characteristics: CameraCharacteristics
-): LiveData() {
-
- private val listener = object : OrientationEventListener(context.applicationContext) {
- override fun onOrientationChanged(orientation: Int) {
- val rotation = when {
- orientation <= 45 -> Surface.ROTATION_0
- orientation <= 135 -> Surface.ROTATION_90
- orientation <= 225 -> Surface.ROTATION_180
- orientation <= 315 -> Surface.ROTATION_270
- else -> Surface.ROTATION_0
- }
- val relative = computeRelativeRotation(characteristics, rotation)
- if (relative != value) postValue(relative)
- }
- }
-
- override fun onActive() {
- super.onActive()
- listener.enable()
- }
-
- override fun onInactive() {
- super.onInactive()
- listener.disable()
- }
-
- companion object {
-
- /**
- * Computes rotation required to transform from the camera sensor orientation to the
- * device's current orientation in degrees.
- *
- * @param characteristics the [CameraCharacteristics] to query for the sensor orientation.
- * @param surfaceRotation the current device orientation as a Surface constant
- * @return the relative rotation from the camera sensor to the current device orientation.
- */
- @JvmStatic
- private fun computeRelativeRotation(
- characteristics: CameraCharacteristics,
- surfaceRotation: Int
- ): Int {
- val sensorOrientationDegrees =
- characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!
-
- val deviceOrientationDegrees = when (surfaceRotation) {
- Surface.ROTATION_0 -> 0
- Surface.ROTATION_90 -> 90
- Surface.ROTATION_180 -> 180
- Surface.ROTATION_270 -> 270
- else -> 0
- }
-
- // Reverse device orientation for front-facing cameras
- val sign = if (characteristics.get(CameraCharacteristics.LENS_FACING) ==
- CameraCharacteristics.LENS_FACING_FRONT) 1 else -1
-
- // Calculate desired JPEG orientation relative to camera orientation to make
- // the image upright relative to the device orientation
- return (sensorOrientationDegrees - (deviceOrientationDegrees * sign) + 360) % 360
- }
- }
-}
diff --git a/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/Yuv.kt b/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/Yuv.kt
deleted file mode 100644
index c476ad0e..00000000
--- a/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/Yuv.kt
+++ /dev/null
@@ -1,191 +0,0 @@
-package com.example.android.camera.utils
-
-import android.graphics.ImageFormat
-import android.media.Image
-import androidx.annotation.IntDef
-import java.nio.ByteBuffer
-
-/*
-This file is converted from part of https://github.com/gordinmitya/yuv2buf.
-Follow the link to find demo app, performance benchmarks and unit tests.
-
-Intro to YUV image formats:
-YUV_420_888 - is a generic format that can be represented as I420, YV12, NV21, and NV12.
-420 means that for each 4 luminosity pixels we have 2 chroma pixels: U and V.
-
-* I420 format represents an image as Y plane followed by U then followed by V plane
- without chroma channels interleaving.
- For example:
- Y Y Y Y
- Y Y Y Y
- U U V V
-
-* NV21 format represents an image as Y plane followed by V and U interleaved. First V then U.
- For example:
- Y Y Y Y
- Y Y Y Y
- V U V U
-
-* YV12 and NV12 are the same as previous formats but with swapped order of V and U. (U then V)
-
-Visualization of these 4 formats:
-https://user-images.githubusercontent.com/9286092/89119601-4f6f8100-d4b8-11ea-9a51-2765f7e513c2.jpg
-
-It's guaranteed that image.getPlanes() always returns planes in order Y U V for YUV_420_888.
-https://developer.android.com/reference/android/graphics/ImageFormat#YUV_420_888
-
-Because I420 and NV21 are more widely supported (RenderScript, OpenCV, MNN)
-the conversion is done into these formats.
-
-More about each format: https://www.fourcc.org/yuv.php
-*/
-
-@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
-@IntDef(ImageFormat.NV21, ImageFormat.YUV_420_888)
-annotation class YuvType
-
-class YuvByteBuffer(image: Image, dstBuffer: ByteBuffer? = null) {
- @YuvType
- val type: Int
- val buffer: ByteBuffer
-
- init {
- val wrappedImage = ImageWrapper(image)
-
- type = if (wrappedImage.u.pixelStride == 1) {
- ImageFormat.YUV_420_888
- } else {
- ImageFormat.NV21
- }
- val size = image.width * image.height * 3 / 2
- buffer = if (
- dstBuffer == null || dstBuffer.capacity() < size ||
- dstBuffer.isReadOnly || !dstBuffer.isDirect
- ) {
- ByteBuffer.allocateDirect(size) }
- else {
- dstBuffer
- }
- buffer.rewind()
-
- removePadding(wrappedImage)
- }
-
- // Input buffers are always direct as described in
- // https://developer.android.com/reference/android/media/Image.Plane#getBuffer()
- private fun removePadding(image: ImageWrapper) {
- val sizeLuma = image.y.width * image.y.height
- val sizeChroma = image.u.width * image.u.height
- if (image.y.rowStride > image.y.width) {
- removePaddingCompact(image.y, buffer, 0)
- } else {
- buffer.position(0)
- buffer.put(image.y.buffer)
- }
- if (type == ImageFormat.YUV_420_888) {
- if (image.u.rowStride > image.u.width) {
- removePaddingCompact(image.u, buffer, sizeLuma)
- removePaddingCompact(image.v, buffer, sizeLuma + sizeChroma)
- } else {
- buffer.position(sizeLuma)
- buffer.put(image.u.buffer)
- buffer.position(sizeLuma + sizeChroma)
- buffer.put(image.v.buffer)
- }
- } else {
- if (image.u.rowStride > image.u.width * 2) {
- removePaddingNotCompact(image, buffer, sizeLuma)
- } else {
- buffer.position(sizeLuma)
- var uv = image.v.buffer
- val properUVSize = image.v.height * image.v.rowStride - 1
- if (uv.capacity() > properUVSize) {
- uv = clipBuffer(image.v.buffer, 0, properUVSize)
- }
- buffer.put(uv)
- val lastOne = image.u.buffer[image.u.buffer.capacity() - 1]
- buffer.put(buffer.capacity() - 1, lastOne)
- }
- }
- buffer.rewind()
- }
-
- private fun removePaddingCompact(
- plane: PlaneWrapper,
- dst: ByteBuffer,
- offset: Int
- ) {
- require(plane.pixelStride == 1) {
- "use removePaddingCompact with pixelStride == 1"
- }
-
- val src = plane.buffer
- val rowStride = plane.rowStride
- var row: ByteBuffer
- dst.position(offset)
- for (i in 0 until plane.height) {
- row = clipBuffer(src, i * rowStride, plane.width)
- dst.put(row)
- }
- }
-
- private fun removePaddingNotCompact(
- image: ImageWrapper,
- dst: ByteBuffer,
- offset: Int
- ) {
- require(image.u.pixelStride == 2) {
- "use removePaddingNotCompact pixelStride == 2"
- }
- val width = image.u.width
- val height = image.u.height
- val rowStride = image.u.rowStride
- var row: ByteBuffer
- dst.position(offset)
- for (i in 0 until height - 1) {
- row = clipBuffer(image.v.buffer, i * rowStride, width * 2)
- dst.put(row)
- }
- row = clipBuffer(image.u.buffer, (height - 1) * rowStride - 1, width * 2)
- dst.put(row)
- }
-
- private fun clipBuffer(buffer: ByteBuffer, start: Int, size: Int): ByteBuffer {
- val duplicate = buffer.duplicate()
- duplicate.position(start)
- duplicate.limit(start + size)
- return duplicate.slice()
- }
-
- private class ImageWrapper(image:Image) {
- val width= image.width
- val height = image.height
- val y = PlaneWrapper(width, height, image.planes[0])
- val u = PlaneWrapper(width / 2, height / 2, image.planes[1])
- val v = PlaneWrapper(width / 2, height / 2, image.planes[2])
-
- // Check this is a supported image format
- // https://developer.android.com/reference/android/graphics/ImageFormat#YUV_420_888
- init {
- require(y.pixelStride == 1) {
- "Pixel stride for Y plane must be 1 but got ${y.pixelStride} instead."
- }
- require(u.pixelStride == v.pixelStride && u.rowStride == v.rowStride) {
- "U and V planes must have the same pixel and row strides " +
- "but got pixel=${u.pixelStride} row=${u.rowStride} for U " +
- "and pixel=${v.pixelStride} and row=${v.rowStride} for V"
- }
- require(u.pixelStride == 1 || u.pixelStride == 2) {
- "Supported" + " pixel strides for U and V planes are 1 and 2"
- }
- }
- }
-
- private class PlaneWrapper(width: Int, height: Int, plane: Image.Plane) {
- val width = width
- val height = height
- val buffer: ByteBuffer = plane.buffer
- val rowStride = plane.rowStride
- val pixelStride = plane.pixelStride
- }
-}
\ No newline at end of file
diff --git a/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt b/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt
deleted file mode 100644
index 8dcd5596..00000000
--- a/CameraXAdvanced/utils/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.ImageFormat
-import android.media.Image
-import android.renderscript.Allocation
-import android.renderscript.Element
-import android.renderscript.RenderScript
-import android.renderscript.ScriptIntrinsicYuvToRGB
-import android.renderscript.Type
-import java.nio.ByteBuffer
-
-/**
- * Helper class used to convert a [Image] object from
- * [ImageFormat.YUV_420_888] format to an RGB [Bitmap] object, it has equivalent
- * functionality to https://github
- * .com/androidx/androidx/blob/androidx-main/camera/camera-core/src/main/java/androidx/camera/core/ImageYuvToRgbConverter.java
- *
- * NOTE: This has been tested in a limited number of devices and is not
- * considered production-ready code. It was created for illustration purposes,
- * since this is not an efficient camera pipeline due to the multiple copies
- * required to convert each frame. For example, this
- * implementation
- * (https://stackoverflow.com/questions/52726002/camera2-captured-picture-conversion-from-yuv-420-888-to-nv21/52740776#52740776)
- * might have better performance.
- */
-class YuvToRgbConverter(context: Context) {
- private val rs = RenderScript.create(context)
- private val scriptYuvToRgb =
- ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs))
-
- // Do not add getters/setters functions to these private variables
- // because yuvToRgb() assume they won't be modified elsewhere
- private var yuvBits: ByteBuffer? = null
- private var bytes: ByteArray = ByteArray(0)
- private var inputAllocation: Allocation? = null
- private var outputAllocation: Allocation? = null
-
- @Synchronized
- fun yuvToRgb(image: Image, output: Bitmap) {
- val yuvBuffer = YuvByteBuffer(image, yuvBits)
- yuvBits = yuvBuffer.buffer
-
- if (needCreateAllocations(image, yuvBuffer)) {
- val yuvType = Type.Builder(rs, Element.U8(rs))
- .setX(image.width)
- .setY(image.height)
- .setYuvFormat(yuvBuffer.type)
- inputAllocation = Allocation.createTyped(
- rs,
- yuvType.create(),
- Allocation.USAGE_SCRIPT
- )
- bytes = ByteArray(yuvBuffer.buffer.capacity())
- val rgbaType = Type.Builder(rs, Element.RGBA_8888(rs))
- .setX(image.width)
- .setY(image.height)
- outputAllocation = Allocation.createTyped(
- rs,
- rgbaType.create(),
- Allocation.USAGE_SCRIPT
- )
- }
-
- yuvBuffer.buffer.get(bytes)
- inputAllocation!!.copyFrom(bytes)
-
- // Convert NV21 or YUV_420_888 format to RGB
- inputAllocation!!.copyFrom(bytes)
- scriptYuvToRgb.setInput(inputAllocation)
- scriptYuvToRgb.forEach(outputAllocation)
- outputAllocation!!.copyTo(output)
- }
-
- private fun needCreateAllocations(image: Image, yuvBuffer: YuvByteBuffer): Boolean {
- return (inputAllocation == null || // the very 1st call
- inputAllocation!!.type.x != image.width || // image size changed
- inputAllocation!!.type.y != image.height ||
- inputAllocation!!.type.yuv != yuvBuffer.type || // image format changed
- bytes.size == yuvBuffer.buffer.capacity())
- }
-}
diff --git a/CameraXAdvanced/utils/src/main/res/drawable/ic_shutter.xml b/CameraXAdvanced/utils/src/main/res/drawable/ic_shutter.xml
deleted file mode 100644
index 9bb91ab8..00000000
--- a/CameraXAdvanced/utils/src/main/res/drawable/ic_shutter.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXAdvanced/utils/src/main/res/drawable/ic_shutter_focused.xml b/CameraXAdvanced/utils/src/main/res/drawable/ic_shutter_focused.xml
deleted file mode 100644
index 9bf521da..00000000
--- a/CameraXAdvanced/utils/src/main/res/drawable/ic_shutter_focused.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraXAdvanced/utils/src/main/res/drawable/ic_shutter_normal.xml b/CameraXAdvanced/utils/src/main/res/drawable/ic_shutter_normal.xml
deleted file mode 100644
index cb50026e..00000000
--- a/CameraXAdvanced/utils/src/main/res/drawable/ic_shutter_normal.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraXAdvanced/utils/src/main/res/drawable/ic_shutter_pressed.xml b/CameraXAdvanced/utils/src/main/res/drawable/ic_shutter_pressed.xml
deleted file mode 100644
index 9bf521da..00000000
--- a/CameraXAdvanced/utils/src/main/res/drawable/ic_shutter_pressed.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraXBasic/.gitignore b/CameraXBasic/.gitignore
deleted file mode 100644
index 24bddc82..00000000
--- a/CameraXBasic/.gitignore
+++ /dev/null
@@ -1,60 +0,0 @@
-# Built application files
-app/release
-*.apk
-*.ap_
-
-# Files for the ART/Dalvik VM
-*.dex
-
-# Java class files
-*.class
-
-# Generated files
-bin/
-gen/
-out/
-
-# Gradle files
-.gradle/
-build/
-
-# Local configuration file (sdk path, etc)
-local.properties
-
-# Proguard folder generated by Eclipse
-proguard/
-
-# Log Files
-*.log
-
-# Android Studio Navigation editor temp files
-.navigation/
-
-# Android Studio captures folder
-captures/
-
-# IntelliJ
-*.iml
-.idea/*
-!.idea/runConfigurations
-
-# Keystore files
-*.jks
-
-# External native build folder generated in Android Studio 2.2 and later
-.externalNativeBuild
-
-# Google Services (e.g. APIs or Firebase)
-google-services.json
-
-# Freeline
-freeline.py
-freeline/
-freeline_project_description.json
-
-# fastlane
-fastlane/report.xml
-fastlane/Preview.html
-fastlane/screenshots
-fastlane/test_output
-fastlane/readme.md
diff --git a/CameraXBasic/.google/packaging.yaml b/CameraXBasic/.google/packaging.yaml
deleted file mode 100644
index 63bc77b7..00000000
--- a/CameraXBasic/.google/packaging.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-# GOOGLE SAMPLE PACKAGING DATA
-#
-# This file is used by Google as part of our samples packaging process.
-# End users may safely ignore this file. It has no relevance to other systems.
----
-status: PUBLISHED
-technologies: [Android]
-categories: [Camera]
-languages: [Kotlin]
-solutions: [Mobile]
-github: android/camera
-level: INTERMEDIATE
-icon: screenshots/icon-web.png
-apiRefs:
- - androidx.camera:camera-core
- - androidx.camera:camera-camera2
-license: apache2
diff --git a/CameraXBasic/README.md b/CameraXBasic/README.md
deleted file mode 100644
index 1b10e1fc..00000000
--- a/CameraXBasic/README.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# CameraXbasic
-
-CameraXbasic aims to demonstrate how to use CameraX APIs written in Kotlin.
-
-## Build
-
-To build the app directly from the command line, run:
-```sh
-./gradlew assembleDebug
-```
-
-## Test
-
-Unit testing and instrumented device testing share the same code. To test the app using Robolectric, no device required, run:
-```sh
-./gradlew test
-```
-
-To run the same tests in an Android device connected via ADB, run:
-```sh
-./gradlew connectedAndroidTest
-```
-
-Alternatively, test running configurations can be added to Android Studio for convenience (and a nice UI). To do that:
-1. Go to: `Run` > `Edit Configurations` > `Add New Configuration`.
-1. For Robolectric select `Android JUnit`, for connected device select `Android Instrumented Tests`.
-1. Select `app` module and `com.android.example.cameraxbasic.MainInstrumentedTest` class.
-1. Optional: Give the run configuration a name, like `test robolectric` or `test device`
diff --git a/CameraXBasic/app/.gitignore b/CameraXBasic/app/.gitignore
deleted file mode 100644
index 38c58757..00000000
--- a/CameraXBasic/app/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/build
-.idea
\ No newline at end of file
diff --git a/CameraXBasic/app/build.gradle b/CameraXBasic/app/build.gradle
deleted file mode 100644
index 9e557659..00000000
--- a/CameraXBasic/app/build.gradle
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.
- */
-
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-kapt'
-apply plugin: "androidx.navigation.safeargs"
-
-android {
- compileSdkVersion 31
- defaultConfig {
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- applicationId "com.android.example.cameraxbasic"
- minSdkVersion 21
- targetSdkVersion 30
- versionCode 1
- versionName "1.0.0"
- }
-
- compileOptions {
- sourceCompatibility rootProject.ext.java_version
- targetCompatibility rootProject.ext.java_version
- }
-
- kotlinOptions {
- jvmTarget = rootProject.ext.java_version
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
-
- buildFeatures {
- viewBinding true
- }
-}
-
-dependencies {
- // Kotlin lang
- implementation 'androidx.core:core-ktx:1.6.0'
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0'
-
- // App compat and UI things
- implementation 'androidx.appcompat:appcompat:1.3.1'
- implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
- implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
- implementation 'androidx.viewpager2:viewpager2:1.0.0'
-
- // Concurrent library for asynchronous coroutines
- implementation 'androidx.concurrent:concurrent-futures-ktx:1.1.0'
-
- // Navigation library
- def nav_version = "2.3.5"
- implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
- implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
-
- // CameraX core library
- def camerax_version = '1.1.0-beta01'
- implementation "androidx.camera:camera-core:$camerax_version"
-
- // CameraX Camera2 extensions
- implementation "androidx.camera:camera-camera2:$camerax_version"
-
- // CameraX Lifecycle library
- implementation "androidx.camera:camera-lifecycle:$camerax_version"
-
- // CameraX View class
- implementation "androidx.camera:camera-view:$camerax_version"
-
-
- // CameraX Extensions library
- // implementation "androidx.camera:camera-extensions:$camerax_version"
-
- //WindowManager
- implementation 'androidx.window:window:1.0.0-alpha09'
-
- // Glide
- implementation 'com.github.bumptech.glide:glide:4.12.0'
- kapt 'com.github.bumptech.glide:compiler:4.11.0'
-
- // Unit testing
- testImplementation 'androidx.test.ext:junit:1.1.3'
- testImplementation 'androidx.test:rules:1.4.0'
- testImplementation 'androidx.test:runner:1.4.0'
- testImplementation 'androidx.test.espresso:espresso-core:3.4.0'
- testImplementation 'org.robolectric:robolectric:4.4'
-
- // Instrumented testing
- androidTestImplementation "androidx.test.ext:junit:1.1.3"
- androidTestImplementation "androidx.test:core:1.4.0"
- androidTestImplementation "androidx.test:rules:1.4.0"
- androidTestImplementation "androidx.test:runner:1.4.0"
- androidTestImplementation "androidx.test.espresso:espresso-core:3.4.0"
-}
diff --git a/CameraXBasic/app/src/androidTest/java/com/android/example/cameraxbasic/CameraPreviewTest.kt b/CameraXBasic/app/src/androidTest/java/com/android/example/cameraxbasic/CameraPreviewTest.kt
deleted file mode 100644
index f5a89507..00000000
--- a/CameraXBasic/app/src/androidTest/java/com/android/example/cameraxbasic/CameraPreviewTest.kt
+++ /dev/null
@@ -1,157 +0,0 @@
-package com.android.example.cameraxbasic
-
-import android.Manifest
-import android.content.Context
-import android.graphics.ImageFormat
-import android.media.ImageReader
-import android.os.Handler
-import android.os.HandlerThread
-import android.util.Log
-import android.util.Size
-import android.view.Surface
-import androidx.camera.core.CameraSelector
-import androidx.camera.core.Preview
-import androidx.camera.core.Preview.SurfaceProvider
-import androidx.camera.core.SurfaceRequest
-import androidx.camera.lifecycle.ProcessCameraProvider
-import androidx.core.util.Consumer
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.LifecycleRegistry
-import androidx.test.annotation.UiThreadTest
-import androidx.test.core.app.ApplicationProvider
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.rule.GrantPermissionRule
-import org.junit.*
-import org.junit.runner.RunWith
-import java.util.concurrent.Executors
-import java.util.concurrent.atomic.AtomicInteger
-
-/**
- * @see "https://developer.android.com/training/camerax/architecture.combine-use-cases"
- */
-@RunWith(AndroidJUnit4::class)
-class CameraPreviewTest : LifecycleOwner, ImageReader.OnImageAvailableListener, Consumer {
-
- @get:Rule
- val cameraAccess = GrantPermissionRule.grant(Manifest.permission.CAMERA)
-
- private var registry: LifecycleRegistry? = null
- private val thread = HandlerThread("CameraPreviewTest").also { it.start() }
- private var executor = Executors.newSingleThreadExecutor()
- private var provider: ProcessCameraProvider? = null // requires main thread
-
- /**
- * @implNote We can't use the main executor since it is reserved for the test framework.
- */
- @Before
- fun setup() {
- val context: Context = ApplicationProvider.getApplicationContext()
- Assert.assertNotNull(context)
- provider = ProcessCameraProvider.getInstance(context).get()
- Assert.assertNotNull(provider)
- }
-
- @UiThreadTest
- @After
- fun teardown() {
- provider?.unbindAll()
- executor?.shutdown()
- }
-
- /**
- * @implNote In checkPreviewUseCase, ImageReader will provide a Surface for camera preview test.
- * When each ImageProxy is acquired, the AtomicInteger will be incremented.
- * By doing so we can ensure the camera binding is working as expected.
- */
- private val reader = ImageReader.newInstance(1920, 1080, ImageFormat.YUV_420_888, 30)
- private val count = AtomicInteger(0)
-
- @Before
- fun setupImageReader() {
- reader.setOnImageAvailableListener(this, Handler(thread.looper))
- }
-
- @After
- fun teardownImageReader() {
- reader.close()
- thread.quit()
- }
-
- override fun onImageAvailable(reader: ImageReader) {
- reader.acquireNextImage().use { image ->
- val imageNumber = count.getAndIncrement()
- Log.i("CameraPreviewTest", String.format("image: %d %s", imageNumber, image))
- }
- }
-
- /**
- * @see ProcessCameraProvider.bindToLifecycle
- */
- override fun getLifecycle() = registry!!
-
- @Before
- fun markCreated() {
- registry = LifecycleRegistry(this).also{
- it.markState(Lifecycle.State.INITIALIZED)
- it.markState(Lifecycle.State.CREATED)
- }
- }
-
- @After
- fun markDestroyed() {
- registry?.markState(Lifecycle.State.DESTROYED)
- }
-
- /**
- * @see SurfaceRequest.provideSurface
- */
- override fun accept(result: SurfaceRequest.Result) {
- when (result.resultCode) {
- SurfaceRequest.Result.RESULT_SURFACE_USED_SUCCESSFULLY -> {
- Log.i("CameraPreviewTest", result.toString())
- }
- SurfaceRequest.Result.RESULT_REQUEST_CANCELLED, SurfaceRequest.Result.RESULT_INVALID_SURFACE, SurfaceRequest.Result.RESULT_SURFACE_ALREADY_PROVIDED, SurfaceRequest.Result.RESULT_WILL_NOT_PROVIDE_SURFACE -> {
- Log.e("CameraPreviewTest", result.toString())
- }
- }
- }
-
-
- @UiThreadTest
- @Test
- fun checkPreviewUseCase() {
- // life cycle owner
- registry?.markState(Lifecycle.State.STARTED)
-
- // select Back camera
- val selectorBuilder = CameraSelector.Builder()
- Assert.assertTrue(provider!!.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA))
- selectorBuilder.requireLensFacing(CameraSelector.LENS_FACING_BACK)
-
- // fit the preview size to ImageReader
- val previewBuilder = Preview.Builder()
- previewBuilder.setTargetResolution(Size(reader.width, reader.height))
- previewBuilder.setTargetRotation(Surface.ROTATION_90)
- val preview = previewBuilder.build()
-
- // acquire camera binding
- provider!!.unbindAll()
- val camera = provider!!.bindToLifecycle(this, selectorBuilder.build(), preview)
- Assert.assertNotNull(camera)
- preview.setSurfaceProvider(executor!!, SurfaceProvider { request: SurfaceRequest ->
- val surface = reader.surface
- Log.i("CameraPreviewTest", String.format("providing: %s", surface))
- request.provideSurface(surface, executor!!, this)
- })
-
- // wait until onImageAvailable is invoked. retry several times
- for (repeat in 5 downTo 0) {
- Thread.sleep(600)
- val value = count.get()
- Log.i("CameraPreviewTest", String.format("count: %d", value))
- if (value > 0) return
- }
- Assert.assertNotEquals(0, count.get().toLong())
- }
-}
diff --git a/CameraXBasic/app/src/main/AndroidManifest.xml b/CameraXBasic/app/src/main/AndroidManifest.xml
deleted file mode 100644
index ceebc52c..00000000
--- a/CameraXBasic/app/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/CameraXBasic/app/src/main/ic_launcher-web.png b/CameraXBasic/app/src/main/ic_launcher-web.png
deleted file mode 100644
index 8a486345..00000000
Binary files a/CameraXBasic/app/src/main/ic_launcher-web.png and /dev/null differ
diff --git a/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/MainActivity.kt b/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/MainActivity.kt
deleted file mode 100644
index ca777fb7..00000000
--- a/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/MainActivity.kt
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.example.cameraxbasic
-
-import android.content.Intent
-import android.os.Build
-import android.os.Bundle
-import android.view.KeyEvent
-import androidx.appcompat.app.AppCompatActivity
-import androidx.core.view.WindowCompat
-import androidx.core.view.WindowInsetsCompat
-import androidx.core.view.WindowInsetsControllerCompat
-import androidx.localbroadcastmanager.content.LocalBroadcastManager
-import com.android.example.cameraxbasic.databinding.ActivityMainBinding
-
-const val KEY_EVENT_ACTION = "key_event_action"
-const val KEY_EVENT_EXTRA = "key_event_extra"
-private const val IMMERSIVE_FLAG_TIMEOUT = 500L
-
-/**
- * Main entry point into our app. This app follows the single-activity pattern, and all
- * functionality is implemented in the form of fragments.
- */
-class MainActivity : AppCompatActivity() {
-
- private lateinit var activityMainBinding: ActivityMainBinding
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
- setContentView(activityMainBinding.root)
- }
-
- override fun onResume() {
- super.onResume()
- // Before setting full screen flags, we must wait a bit to let UI settle; otherwise, we may
- // be trying to set app to immersive mode before it's ready and the flags do not stick
- activityMainBinding.fragmentContainer.postDelayed({
- hideSystemUI()
- }, IMMERSIVE_FLAG_TIMEOUT)
- }
-
- /** When key down event is triggered, relay it via local broadcast so fragments can handle it */
- override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
- return when (keyCode) {
- KeyEvent.KEYCODE_VOLUME_DOWN -> {
- val intent = Intent(KEY_EVENT_ACTION).apply { putExtra(KEY_EVENT_EXTRA, keyCode) }
- LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
- true
- }
- else -> super.onKeyDown(keyCode, event)
- }
- }
-
- override fun onBackPressed() {
- if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
- // Workaround for Android Q memory leak issue in IRequestFinishCallback$Stub.
- // (https://issuetracker.google.com/issues/139738913)
- finishAfterTransition()
- } else {
- super.onBackPressed()
- }
- }
-
- private fun hideSystemUI() {
- WindowCompat.setDecorFitsSystemWindows(window, false)
- WindowInsetsControllerCompat(window, activityMainBinding.fragmentContainer).let { controller ->
- controller.hide(WindowInsetsCompat.Type.systemBars())
- controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
- }
- }
-}
diff --git a/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/MainApplication.kt b/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/MainApplication.kt
deleted file mode 100644
index a83caaf0..00000000
--- a/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/MainApplication.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.android.example.cameraxbasic
-
-import android.app.Application
-import android.util.Log
-import androidx.camera.camera2.Camera2Config
-import androidx.camera.core.CameraXConfig
-
-/**
- * Set CameraX logging level to Log.ERROR to avoid excessive logcat messages.
- * Refer to https://developer.android.com/reference/androidx/camera/core/CameraXConfig.Builder#setMinimumLoggingLevel(int)
- * for details.
- */
-class MainApplication : Application(), CameraXConfig.Provider {
- override fun getCameraXConfig(): CameraXConfig {
- return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
- .setMinimumLoggingLevel(Log.ERROR).build()
- }
-}
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/CameraFragment.kt b/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/CameraFragment.kt
deleted file mode 100644
index 6417a946..00000000
--- a/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/CameraFragment.kt
+++ /dev/null
@@ -1,674 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.example.cameraxbasic.fragments
-
-import android.annotation.SuppressLint
-import android.content.*
-import android.content.res.Configuration
-import android.graphics.Color
-import android.graphics.drawable.ColorDrawable
-import android.hardware.display.DisplayManager
-import android.os.Build
-import android.os.Bundle
-import android.provider.MediaStore
-import android.util.Log
-import android.view.KeyEvent
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.Toast
-import androidx.camera.core.*
-import androidx.camera.lifecycle.ProcessCameraProvider
-import androidx.concurrent.futures.await
-import androidx.core.view.setPadding
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.lifecycleScope
-import androidx.localbroadcastmanager.content.LocalBroadcastManager
-import androidx.navigation.Navigation
-import androidx.window.WindowManager
-import com.android.example.cameraxbasic.KEY_EVENT_ACTION
-import com.android.example.cameraxbasic.KEY_EVENT_EXTRA
-import com.android.example.cameraxbasic.R
-import com.android.example.cameraxbasic.databinding.CameraUiContainerBinding
-import com.android.example.cameraxbasic.databinding.FragmentCameraBinding
-import com.android.example.cameraxbasic.utils.ANIMATION_FAST_MILLIS
-import com.android.example.cameraxbasic.utils.ANIMATION_SLOW_MILLIS
-import com.android.example.cameraxbasic.utils.MediaStoreUtils
-import com.android.example.cameraxbasic.utils.simulateClick
-import com.bumptech.glide.Glide
-import com.bumptech.glide.request.RequestOptions
-import kotlinx.coroutines.launch
-import java.nio.ByteBuffer
-import java.text.SimpleDateFormat
-import java.util.*
-import java.util.concurrent.ExecutorService
-import java.util.concurrent.Executors
-import kotlin.math.abs
-import kotlin.math.max
-import kotlin.math.min
-
-/** Helper type alias used for analysis use case callbacks */
-typealias LumaListener = (luma: Double) -> Unit
-
-/**
- * Main fragment for this app. Implements all camera operations including:
- * - Viewfinder
- * - Photo taking
- * - Image analysis
- */
-class CameraFragment : Fragment() {
-
- private var _fragmentCameraBinding: FragmentCameraBinding? = null
-
- private val fragmentCameraBinding get() = _fragmentCameraBinding!!
-
- private var cameraUiContainerBinding: CameraUiContainerBinding? = null
-
- private lateinit var broadcastManager: LocalBroadcastManager
-
- private lateinit var mediaStoreUtils: MediaStoreUtils
-
- private var displayId: Int = -1
- private var lensFacing: Int = CameraSelector.LENS_FACING_BACK
- private var preview: Preview? = null
- private var imageCapture: ImageCapture? = null
- private var imageAnalyzer: ImageAnalysis? = null
- private var camera: Camera? = null
- private var cameraProvider: ProcessCameraProvider? = null
- private lateinit var windowManager: WindowManager
-
- private val displayManager by lazy {
- requireContext().getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
- }
-
- /** Blocking camera operations are performed using this executor */
- private lateinit var cameraExecutor: ExecutorService
-
- /** Volume down button receiver used to trigger shutter */
- private val volumeDownReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- when (intent.getIntExtra(KEY_EVENT_EXTRA, KeyEvent.KEYCODE_UNKNOWN)) {
- // When the volume down button is pressed, simulate a shutter button click
- KeyEvent.KEYCODE_VOLUME_DOWN -> {
- cameraUiContainerBinding?.cameraCaptureButton?.simulateClick()
- }
- }
- }
- }
-
- /**
- * We need a display listener for orientation changes that do not trigger a configuration
- * change, for example if we choose to override config change in manifest or for 180-degree
- * orientation changes.
- */
- private val displayListener = object : DisplayManager.DisplayListener {
- override fun onDisplayAdded(displayId: Int) = Unit
- override fun onDisplayRemoved(displayId: Int) = Unit
- override fun onDisplayChanged(displayId: Int) = view?.let { view ->
- if (displayId == this@CameraFragment.displayId) {
- Log.d(TAG, "Rotation changed: ${view.display.rotation}")
- imageCapture?.targetRotation = view.display.rotation
- imageAnalyzer?.targetRotation = view.display.rotation
- }
- } ?: Unit
- }
-
- override fun onResume() {
- super.onResume()
- // Make sure that all permissions are still present, since the
- // user could have removed them while the app was in paused state.
- if (!PermissionsFragment.hasPermissions(requireContext())) {
- Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(
- CameraFragmentDirections.actionCameraToPermissions()
- )
- }
- }
-
- override fun onDestroyView() {
- _fragmentCameraBinding = null
- super.onDestroyView()
-
- // Shut down our background executor
- cameraExecutor.shutdown()
-
- // Unregister the broadcast receivers and listeners
- broadcastManager.unregisterReceiver(volumeDownReceiver)
- displayManager.unregisterDisplayListener(displayListener)
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- _fragmentCameraBinding = FragmentCameraBinding.inflate(inflater, container, false)
- return fragmentCameraBinding.root
- }
-
- private fun setGalleryThumbnail(filename: String) {
- // Run the operations in the view's thread
- cameraUiContainerBinding?.photoViewButton?.let { photoViewButton ->
- photoViewButton.post {
- // Remove thumbnail padding
- photoViewButton.setPadding(resources.getDimension(R.dimen.stroke_small).toInt())
-
- // Load thumbnail into circular button using Glide
- Glide.with(photoViewButton)
- .load(filename)
- .apply(RequestOptions.circleCropTransform())
- .into(photoViewButton)
- }
- }
- }
-
- @SuppressLint("MissingPermission")
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- // Initialize our background executor
- cameraExecutor = Executors.newSingleThreadExecutor()
-
- broadcastManager = LocalBroadcastManager.getInstance(view.context)
-
- // Set up the intent filter that will receive events from our main activity
- val filter = IntentFilter().apply { addAction(KEY_EVENT_ACTION) }
- broadcastManager.registerReceiver(volumeDownReceiver, filter)
-
- // Every time the orientation of device changes, update rotation for use cases
- displayManager.registerDisplayListener(displayListener, null)
-
- // Initialize WindowManager to retrieve display metrics
- windowManager = WindowManager(view.context)
-
- // Initialize MediaStoreUtils for fetching this app's images
- mediaStoreUtils = MediaStoreUtils(requireContext())
-
- // Wait for the views to be properly laid out
- fragmentCameraBinding.viewFinder.post {
-
- // Keep track of the display in which this view is attached
- displayId = fragmentCameraBinding.viewFinder.display.displayId
-
- // Build UI controls
- updateCameraUi()
-
- // Set up the camera and its use cases
- lifecycleScope.launch {
- setUpCamera()
- }
- }
- }
-
- /**
- * Inflate camera controls and update the UI manually upon config changes to avoid removing
- * and re-adding the view finder from the view hierarchy; this provides a seamless rotation
- * transition on devices that support it.
- *
- * NOTE: The flag is supported starting in Android 8 but there still is a small flash on the
- * screen for devices that run Android 9 or below.
- */
- override fun onConfigurationChanged(newConfig: Configuration) {
- super.onConfigurationChanged(newConfig)
-
- // Rebind the camera with the updated display metrics
- bindCameraUseCases()
-
- // Enable or disable switching between cameras
- updateCameraSwitchButton()
- }
-
- /** Initialize CameraX, and prepare to bind the camera use cases */
- private suspend fun setUpCamera() {
- cameraProvider = ProcessCameraProvider.getInstance(requireContext()).await()
-
- // Select lensFacing depending on the available cameras
- lensFacing = when {
- hasBackCamera() -> CameraSelector.LENS_FACING_BACK
- hasFrontCamera() -> CameraSelector.LENS_FACING_FRONT
- else -> throw IllegalStateException("Back and front camera are unavailable")
- }
-
- // Enable or disable switching between cameras
- updateCameraSwitchButton()
-
- // Build and bind the camera use cases
- bindCameraUseCases()
- }
-
- /** Declare and bind preview, capture and analysis use cases */
- private fun bindCameraUseCases() {
-
- // Get screen metrics used to setup camera for full screen resolution
- val metrics = windowManager.getCurrentWindowMetrics().bounds
- Log.d(TAG, "Screen metrics: ${metrics.width()} x ${metrics.height()}")
-
- val screenAspectRatio = aspectRatio(metrics.width(), metrics.height())
- Log.d(TAG, "Preview aspect ratio: $screenAspectRatio")
-
- val rotation = fragmentCameraBinding.viewFinder.display.rotation
-
- // CameraProvider
- val cameraProvider = cameraProvider
- ?: throw IllegalStateException("Camera initialization failed.")
-
- // CameraSelector
- val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
-
- // Preview
- preview = Preview.Builder()
- // We request aspect ratio but no resolution
- .setTargetAspectRatio(screenAspectRatio)
- // Set initial target rotation
- .setTargetRotation(rotation)
- .build()
-
- // ImageCapture
- imageCapture = ImageCapture.Builder()
- .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
- // We request aspect ratio but no resolution to match preview config, but letting
- // CameraX optimize for whatever specific resolution best fits our use cases
- .setTargetAspectRatio(screenAspectRatio)
- // Set initial target rotation, we will have to call this again if rotation changes
- // during the lifecycle of this use case
- .setTargetRotation(rotation)
- .build()
-
- // ImageAnalysis
- imageAnalyzer = ImageAnalysis.Builder()
- // We request aspect ratio but no resolution
- .setTargetAspectRatio(screenAspectRatio)
- // Set initial target rotation, we will have to call this again if rotation changes
- // during the lifecycle of this use case
- .setTargetRotation(rotation)
- .build()
- // The analyzer can then be assigned to the instance
- .also {
- it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
- // Values returned from our analyzer are passed to the attached listener
- // We log image analysis results here - you should do something useful
- // instead!
- Log.d(TAG, "Average luminosity: $luma")
- })
- }
-
- // Must unbind the use-cases before rebinding them
- cameraProvider.unbindAll()
-
- if (camera != null) {
- // Must remove observers from the previous camera instance
- removeCameraStateObservers(camera!!.cameraInfo)
- }
-
- try {
- // A variable number of use-cases can be passed here -
- // camera provides access to CameraControl & CameraInfo
- camera = cameraProvider.bindToLifecycle(
- this, cameraSelector, preview, imageCapture, imageAnalyzer)
-
- // Attach the viewfinder's surface provider to preview use case
- preview?.setSurfaceProvider(fragmentCameraBinding.viewFinder.surfaceProvider)
- observeCameraState(camera?.cameraInfo!!)
- } catch (exc: Exception) {
- Log.e(TAG, "Use case binding failed", exc)
- }
- }
-
- private fun removeCameraStateObservers(cameraInfo: CameraInfo) {
- cameraInfo.cameraState.removeObservers(viewLifecycleOwner)
- }
-
- private fun observeCameraState(cameraInfo: CameraInfo) {
- cameraInfo.cameraState.observe(viewLifecycleOwner) { cameraState ->
- run {
- when (cameraState.type) {
- CameraState.Type.PENDING_OPEN -> {
- // Ask the user to close other camera apps
- Toast.makeText(context,
- "CameraState: Pending Open",
- Toast.LENGTH_SHORT).show()
- }
- CameraState.Type.OPENING -> {
- // Show the Camera UI
- Toast.makeText(context,
- "CameraState: Opening",
- Toast.LENGTH_SHORT).show()
- }
- CameraState.Type.OPEN -> {
- // Setup Camera resources and begin processing
- Toast.makeText(context,
- "CameraState: Open",
- Toast.LENGTH_SHORT).show()
- }
- CameraState.Type.CLOSING -> {
- // Close camera UI
- Toast.makeText(context,
- "CameraState: Closing",
- Toast.LENGTH_SHORT).show()
- }
- CameraState.Type.CLOSED -> {
- // Free camera resources
- Toast.makeText(context,
- "CameraState: Closed",
- Toast.LENGTH_SHORT).show()
- }
- }
- }
-
- cameraState.error?.let { error ->
- when (error.code) {
- // Open errors
- CameraState.ERROR_STREAM_CONFIG -> {
- // Make sure to setup the use cases properly
- Toast.makeText(context,
- "Stream config error",
- Toast.LENGTH_SHORT).show()
- }
- // Opening errors
- CameraState.ERROR_CAMERA_IN_USE -> {
- // Close the camera or ask user to close another camera app that's using the
- // camera
- Toast.makeText(context,
- "Camera in use",
- Toast.LENGTH_SHORT).show()
- }
- CameraState.ERROR_MAX_CAMERAS_IN_USE -> {
- // Close another open camera in the app, or ask the user to close another
- // camera app that's using the camera
- Toast.makeText(context,
- "Max cameras in use",
- Toast.LENGTH_SHORT).show()
- }
- CameraState.ERROR_OTHER_RECOVERABLE_ERROR -> {
- Toast.makeText(context,
- "Other recoverable error",
- Toast.LENGTH_SHORT).show()
- }
- // Closing errors
- CameraState.ERROR_CAMERA_DISABLED -> {
- // Ask the user to enable the device's cameras
- Toast.makeText(context,
- "Camera disabled",
- Toast.LENGTH_SHORT).show()
- }
- CameraState.ERROR_CAMERA_FATAL_ERROR -> {
- // Ask the user to reboot the device to restore camera function
- Toast.makeText(context,
- "Fatal error",
- Toast.LENGTH_SHORT).show()
- }
- // Closed errors
- CameraState.ERROR_DO_NOT_DISTURB_MODE_ENABLED -> {
- // Ask the user to disable the "Do Not Disturb" mode, then reopen the camera
- Toast.makeText(context,
- "Do not disturb mode enabled",
- Toast.LENGTH_SHORT).show()
- }
- }
- }
- }
- }
-
- /**
- * [androidx.camera.core.ImageAnalysis.Builder] requires enum value of
- * [androidx.camera.core.AspectRatio]. Currently it has values of 4:3 & 16:9.
- *
- * Detecting the most suitable ratio for dimensions provided in @params by counting absolute
- * of preview ratio to one of the provided values.
- *
- * @param width - preview width
- * @param height - preview height
- * @return suitable aspect ratio
- */
- private fun aspectRatio(width: Int, height: Int): Int {
- val previewRatio = max(width, height).toDouble() / min(width, height)
- if (abs(previewRatio - RATIO_4_3_VALUE) <= abs(previewRatio - RATIO_16_9_VALUE)) {
- return AspectRatio.RATIO_4_3
- }
- return AspectRatio.RATIO_16_9
- }
-
- /** Method used to re-draw the camera UI controls, called every time configuration changes. */
- private fun updateCameraUi() {
-
- // Remove previous UI if any
- cameraUiContainerBinding?.root?.let {
- fragmentCameraBinding.root.removeView(it)
- }
-
- cameraUiContainerBinding = CameraUiContainerBinding.inflate(
- LayoutInflater.from(requireContext()),
- fragmentCameraBinding.root,
- true
- )
-
- // In the background, load latest photo taken (if any) for gallery thumbnail
- lifecycleScope.launch {
- val thumbnailUri = mediaStoreUtils.getLatestImageFilename()
- thumbnailUri?.let {
- setGalleryThumbnail(it)
- }
- }
-
- // Listener for button used to capture photo
- cameraUiContainerBinding?.cameraCaptureButton?.setOnClickListener {
-
- // Get a stable reference of the modifiable image capture use case
- imageCapture?.let { imageCapture ->
-
- // Create time stamped name and MediaStore entry.
- val name = SimpleDateFormat(FILENAME, Locale.US)
- .format(System.currentTimeMillis())
- val contentValues = ContentValues().apply {
- put(MediaStore.MediaColumns.DISPLAY_NAME, name)
- put(MediaStore.MediaColumns.MIME_TYPE, PHOTO_TYPE)
- if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
- val appName = requireContext().resources.getString(R.string.app_name)
- put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/${appName}")
- }
- }
-
- // Create output options object which contains file + metadata
- val outputOptions = ImageCapture.OutputFileOptions
- .Builder(requireContext().contentResolver,
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
- contentValues)
- .build()
-
- // Setup image capture listener which is triggered after photo has been taken
- imageCapture.takePicture(
- outputOptions, cameraExecutor, object : ImageCapture.OnImageSavedCallback {
- override fun onError(exc: ImageCaptureException) {
- Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
- }
-
- override fun onImageSaved(output: ImageCapture.OutputFileResults) {
- val savedUri = output.savedUri
- Log.d(TAG, "Photo capture succeeded: $savedUri")
-
- // We can only change the foreground Drawable using API level 23+ API
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- // Update the gallery thumbnail with latest picture taken
- setGalleryThumbnail(savedUri.toString())
- }
-
- // Implicit broadcasts will be ignored for devices running API level >= 24
- // so if you only target API level 24+ you can remove this statement
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
- // Suppress deprecated Camera usage needed for API level 23 and below
- @Suppress("DEPRECATION")
- requireActivity().sendBroadcast(
- Intent(android.hardware.Camera.ACTION_NEW_PICTURE, savedUri)
- )
- }
- }
- })
-
- // We can only change the foreground Drawable using API level 23+ API
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-
- // Display flash animation to indicate that photo was captured
- fragmentCameraBinding.root.postDelayed({
- fragmentCameraBinding.root.foreground = ColorDrawable(Color.WHITE)
- fragmentCameraBinding.root.postDelayed(
- { fragmentCameraBinding.root.foreground = null }, ANIMATION_FAST_MILLIS)
- }, ANIMATION_SLOW_MILLIS)
- }
- }
- }
-
- // Setup for button used to switch cameras
- cameraUiContainerBinding?.cameraSwitchButton?.let {
-
- // Disable the button until the camera is set up
- it.isEnabled = false
-
- // Listener for button used to switch cameras. Only called if the button is enabled
- it.setOnClickListener {
- lensFacing = if (CameraSelector.LENS_FACING_FRONT == lensFacing) {
- CameraSelector.LENS_FACING_BACK
- } else {
- CameraSelector.LENS_FACING_FRONT
- }
- // Re-bind use cases to update selected camera
- bindCameraUseCases()
- }
- }
-
- // Listener for button used to view the most recent photo
- cameraUiContainerBinding?.photoViewButton?.setOnClickListener {
- // Only navigate when the gallery has photos
- lifecycleScope.launch {
- if (mediaStoreUtils.getImages().isNotEmpty()) {
- Navigation.findNavController(requireActivity(), R.id.fragment_container)
- .navigate(CameraFragmentDirections.actionCameraToGallery(
- mediaStoreUtils.mediaStoreCollection.toString()
- )
- )
- }
- }
- }
- }
-
- /** Enabled or disabled a button to switch cameras depending on the available cameras */
- private fun updateCameraSwitchButton() {
- try {
- cameraUiContainerBinding?.cameraSwitchButton?.isEnabled = hasBackCamera() && hasFrontCamera()
- } catch (exception: CameraInfoUnavailableException) {
- cameraUiContainerBinding?.cameraSwitchButton?.isEnabled = false
- }
- }
-
- /** Returns true if the device has an available back camera. False otherwise */
- private fun hasBackCamera(): Boolean {
- return cameraProvider?.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA) ?: false
- }
-
- /** Returns true if the device has an available front camera. False otherwise */
- private fun hasFrontCamera(): Boolean {
- return cameraProvider?.hasCamera(CameraSelector.DEFAULT_FRONT_CAMERA) ?: false
- }
-
- /**
- * Our custom image analysis class.
- *
- *
All we need to do is override the function `analyze` with our desired operations. Here,
- * we compute the average luminosity of the image by looking at the Y plane of the YUV frame.
- */
- private class LuminosityAnalyzer(listener: LumaListener? = null) : ImageAnalysis.Analyzer {
- private val frameRateWindow = 8
- private val frameTimestamps = ArrayDeque(5)
- private val listeners = ArrayList().apply { listener?.let { add(it) } }
- private var lastAnalyzedTimestamp = 0L
- var framesPerSecond: Double = -1.0
- private set
-
- /**
- * Helper extension function used to extract a byte array from an image plane buffer
- */
- private fun ByteBuffer.toByteArray(): ByteArray {
- rewind() // Rewind the buffer to zero
- val data = ByteArray(remaining())
- get(data) // Copy the buffer into a byte array
- return data // Return the byte array
- }
-
- /**
- * Analyzes an image to produce a result.
- *
- *
The caller is responsible for ensuring this analysis method can be executed quickly
- * enough to prevent stalls in the image acquisition pipeline. Otherwise, newly available
- * images will not be acquired and analyzed.
- *
- *
The image passed to this method becomes invalid after this method returns. The caller
- * should not store external references to this image, as these references will become
- * invalid.
- *
- * @param image image being analyzed VERY IMPORTANT: Analyzer method implementation must
- * call image.close() on received images when finished using them. Otherwise, new images
- * may not be received or the camera may stall, depending on back pressure setting.
- *
- */
- override fun analyze(image: ImageProxy) {
- // If there are no listeners attached, we don't need to perform analysis
- if (listeners.isEmpty()) {
- image.close()
- return
- }
-
- // Keep track of frames analyzed
- val currentTime = System.currentTimeMillis()
- frameTimestamps.push(currentTime)
-
- // Compute the FPS using a moving average
- while (frameTimestamps.size >= frameRateWindow) frameTimestamps.removeLast()
- val timestampFirst = frameTimestamps.peekFirst() ?: currentTime
- val timestampLast = frameTimestamps.peekLast() ?: currentTime
- framesPerSecond = 1.0 / ((timestampFirst - timestampLast) /
- frameTimestamps.size.coerceAtLeast(1).toDouble()) * 1000.0
-
- // Analysis could take an arbitrarily long amount of time
- // Since we are running in a different thread, it won't stall other use cases
-
- lastAnalyzedTimestamp = frameTimestamps.first
-
- // Since format in ImageAnalysis is YUV, image.planes[0] contains the luminance plane
- val buffer = image.planes[0].buffer
-
- // Extract image data from callback object
- val data = buffer.toByteArray()
-
- // Convert the data into an array of pixel values ranging 0-255
- val pixels = data.map { it.toInt() and 0xFF }
-
- // Compute average luminance for the image
- val luma = pixels.average()
-
- // Call all listeners with new value
- listeners.forEach { it(luma) }
-
- image.close()
- }
- }
-
- companion object {
- private const val TAG = "CameraXBasic"
- private const val FILENAME = "yyyy-MM-dd-HH-mm-ss-SSS"
- private const val PHOTO_TYPE = "image/jpeg"
- private const val RATIO_4_3_VALUE = 4.0 / 3.0
- private const val RATIO_16_9_VALUE = 16.0 / 9.0
- }
-}
diff --git a/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/GalleryFragment.kt b/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/GalleryFragment.kt
deleted file mode 100644
index de0bdaa2..00000000
--- a/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/GalleryFragment.kt
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.example.cameraxbasic.fragments
-
-import android.content.Intent
-import android.media.MediaScannerConnection
-import android.os.Build
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.webkit.MimeTypeMap
-import androidx.appcompat.app.AlertDialog
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentManager
-import androidx.lifecycle.lifecycleScope
-import androidx.navigation.Navigation
-import androidx.navigation.fragment.navArgs
-import androidx.viewpager2.adapter.FragmentStateAdapter
-import com.android.example.cameraxbasic.R
-import com.android.example.cameraxbasic.databinding.FragmentGalleryBinding
-import com.android.example.cameraxbasic.utils.MediaStoreFile
-import com.android.example.cameraxbasic.utils.MediaStoreUtils
-import com.android.example.cameraxbasic.utils.padWithDisplayCutout
-import com.android.example.cameraxbasic.utils.showImmersive
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.launch
-
-/** Fragment used to present the user with a gallery of photos taken */
-class GalleryFragment internal constructor() : Fragment() {
-
- /** Android ViewBinding */
- private var _fragmentGalleryBinding: FragmentGalleryBinding? = null
-
- private val fragmentGalleryBinding get() = _fragmentGalleryBinding!!
-
- /** AndroidX navigation arguments */
- private val args: GalleryFragmentArgs by navArgs()
-
- private var mediaList: MutableList = mutableListOf()
- private var hasMediaItems = CompletableDeferred()
-
- /** Adapter class used to present a fragment containing one photo or video as a page */
- inner class MediaPagerAdapter(fm: FragmentManager,
- private var mediaList: MutableList) :
- FragmentStateAdapter(fm, lifecycle) {
- override fun getItemCount(): Int = mediaList.size
- override fun createFragment(position: Int): Fragment =
- PhotoFragment.create(mediaList[position])
- override fun getItemId(position: Int): Long {
- return mediaList[position].id
- }
- override fun containsItem(itemId: Long): Boolean {
- return null != mediaList.firstOrNull { it.id == itemId }
- }
- fun setMediaListAndNotify(mediaList: MutableList) {
- this.mediaList = mediaList
- notifyDataSetChanged()
- }
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- lifecycleScope.launch {
- // Get images this app has access to from MediaStore
- mediaList = MediaStoreUtils(requireContext()).getImages()
- (fragmentGalleryBinding.photoViewPager.adapter as MediaPagerAdapter)
- .setMediaListAndNotify(mediaList)
- hasMediaItems.complete(mediaList.isNotEmpty())
- }
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- _fragmentGalleryBinding = FragmentGalleryBinding.inflate(inflater, container, false)
- return fragmentGalleryBinding.root
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- lifecycleScope.launch {
- fragmentGalleryBinding.deleteButton.isEnabled = hasMediaItems.await()
- fragmentGalleryBinding.shareButton.isEnabled = hasMediaItems.await()
- }
-
- // Populate the ViewPager and implement a cache of two media items
- fragmentGalleryBinding.photoViewPager.apply {
- offscreenPageLimit = 2
- adapter = MediaPagerAdapter(childFragmentManager, mediaList)
- }
-
- // Make sure that the cutout "safe area" avoids the screen notch if any
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
- // Use extension method to pad "inside" view containing UI using display cutout's bounds
- fragmentGalleryBinding.cutoutSafeArea.padWithDisplayCutout()
- }
-
- // Handle back button press
- fragmentGalleryBinding.backButton.setOnClickListener {
- Navigation.findNavController(requireActivity(), R.id.fragment_container)
- .navigateUp()
- }
-
- // Handle share button press
- fragmentGalleryBinding.shareButton.setOnClickListener {
-
- mediaList.getOrNull(fragmentGalleryBinding.photoViewPager.currentItem)
- ?.let { mediaStoreFile ->
- val mediaFile = mediaStoreFile.file
- // Create a sharing intent
- val intent = Intent().apply {
- // Infer media type from file extension
- val mediaType = MimeTypeMap.getSingleton()
- .getMimeTypeFromExtension(mediaFile.extension)
- // Set the appropriate intent extra, type, action and flags
- type = mediaType
- action = Intent.ACTION_SEND
- flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
- putExtra(Intent.EXTRA_STREAM, mediaStoreFile.uri)
- }
-
- // Launch the intent letting the user choose which app to share with
- startActivity(Intent.createChooser(intent, getString(R.string.share_hint)))
- }
- }
-
- // Handle delete button press
- fragmentGalleryBinding.deleteButton.setOnClickListener {
-
- mediaList.getOrNull(fragmentGalleryBinding.photoViewPager.currentItem)
- ?.let { mediaStoreFile ->
- val mediaFile = mediaStoreFile.file
-
- AlertDialog.Builder(view.context, android.R.style.Theme_Material_Dialog)
- .setTitle(getString(R.string.delete_title))
- .setMessage(getString(R.string.delete_dialog))
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setPositiveButton(android.R.string.ok) { _, _ ->
-
- // Delete current photo
- mediaFile.delete()
-
- // Send relevant broadcast to notify other apps of deletion
- MediaScannerConnection.scanFile(
- view.context, arrayOf(mediaFile.absolutePath), null, null
- )
-
- // Notify our view pager
- mediaList.removeAt(fragmentGalleryBinding.photoViewPager.currentItem)
- fragmentGalleryBinding.photoViewPager.adapter?.notifyDataSetChanged()
-
- // If all photos have been deleted, return to camera
- if (mediaList.isEmpty()) {
- Navigation.findNavController(
- requireActivity(),
- R.id.fragment_container
- ).navigateUp()
- }
-
- }
-
- .setNegativeButton(android.R.string.cancel, null)
- .create().showImmersive()
- }
- }
- }
-
- override fun onDestroyView() {
- _fragmentGalleryBinding = null
- super.onDestroyView()
- }
-}
diff --git a/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/PermissionsFragment.kt b/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/PermissionsFragment.kt
deleted file mode 100644
index eb70e6d4..00000000
--- a/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/PermissionsFragment.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.example.cameraxbasic.fragments
-
-import android.Manifest
-import android.content.Context
-import android.content.pm.PackageManager
-import android.os.Build
-import android.os.Bundle
-import android.widget.Toast
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.core.content.ContextCompat
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.lifecycleScope
-import androidx.navigation.Navigation
-import com.android.example.cameraxbasic.R
-
-private var PERMISSIONS_REQUIRED = arrayOf(Manifest.permission.CAMERA)
-
-/**
- * The sole purpose of this fragment is to request permissions and, once granted, display the
- * camera fragment to the user.
- */
-class PermissionsFragment : Fragment() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // add the storage access permission request for Android 9 and below.
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
- val permissionList = PERMISSIONS_REQUIRED.toMutableList()
- permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
- PERMISSIONS_REQUIRED = permissionList.toTypedArray()
- }
-
- if (!hasPermissions(requireContext())) {
- // Request camera-related permissions
- activityResultLauncher.launch(PERMISSIONS_REQUIRED)
- } else {
- navigateToCamera()
- }
- }
-
- private fun navigateToCamera() {
- lifecycleScope.launchWhenStarted {
- Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(
- PermissionsFragmentDirections.actionPermissionsToCamera())
- }
- }
-
- private val activityResultLauncher =
- registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions())
- { permissions ->
- // Handle Permission granted/rejected
- var permissionGranted = true
- permissions.entries.forEach {
- if (it.key in PERMISSIONS_REQUIRED && it.value == false)
- permissionGranted = false
- }
- if (!permissionGranted) {
- Toast.makeText(context, "Permission request denied", Toast.LENGTH_LONG).show()
- } else {
- navigateToCamera()
- }
- }
-
- companion object {
- /** Convenience method used to check if all permissions required by this app are granted */
- fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {
- ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
- }
- }
-}
diff --git a/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/PhotoFragment.kt b/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/PhotoFragment.kt
deleted file mode 100644
index 0aea05dd..00000000
--- a/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/PhotoFragment.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.example.cameraxbasic.fragments
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ImageView
-import androidx.fragment.app.Fragment
-import com.android.example.cameraxbasic.R
-import com.android.example.cameraxbasic.utils.MediaStoreFile
-import com.bumptech.glide.Glide
-import java.io.File
-
-
-/** Fragment used for each individual page showing a photo inside of [GalleryFragment] */
-class PhotoFragment internal constructor() : Fragment() {
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
- savedInstanceState: Bundle?) = ImageView(context)
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- val args = arguments ?: return
- val resource = args.getString(FILE_NAME_KEY)?.let { File(it) } ?: R.drawable.ic_photo
- Glide.with(view).load(resource).into(view as ImageView)
- }
-
- companion object {
- private const val FILE_NAME_KEY = "file_name"
-
- fun create(mediaStoreFile: MediaStoreFile) = PhotoFragment().apply {
- val image = mediaStoreFile.file
- arguments = Bundle().apply {
- putString(FILE_NAME_KEY, image.absolutePath)
- }
- }
- }
-}
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/utils/MediaStoreUtils.kt b/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/utils/MediaStoreUtils.kt
deleted file mode 100644
index c4d48487..00000000
--- a/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/utils/MediaStoreUtils.kt
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.example.cameraxbasic.utils
-
-import android.content.ContentUris
-import android.content.Context
-import android.database.Cursor
-import android.net.Uri
-import android.os.Build
-import android.provider.MediaStore
-import androidx.core.net.toUri
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.withContext
-import java.io.File
-
-
-/**
- * A utility class for accessing this app's photo storage.
- *
- * Since this app doesn't request any external storage permissions, it will only be able to access
- * photos taken with this app. If the app is uninstalled, the photos taken with this app will stay
- * on the device, but reinstalling the app will not give it access to photos taken with the app's
- * previous instance. You can request further permissions to change this app's access. See this
- * guide for more: https://developer.android.com/training/data-storage.
- */
-class MediaStoreUtils(private val context: Context) {
-
- val mediaStoreCollection: Uri? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
- } else {
- context.getExternalFilesDir(null)?.toUri()
- }
-
- private suspend fun getMediaStoreImageCursor(mediaStoreCollection: Uri): Cursor? {
- var cursor: Cursor?
- withContext(Dispatchers.IO) {
- val projection = arrayOf(imageDataColumnIndex, imageIdColumnIndex)
- val sortOrder = "DATE_ADDED DESC"
- cursor = context.contentResolver.query(
- mediaStoreCollection, projection, null, null, sortOrder
- )
- }
- return cursor
- }
-
- suspend fun getLatestImageFilename(): String? {
- var filename: String?
- if (mediaStoreCollection == null) return null
-
- getMediaStoreImageCursor(mediaStoreCollection).use { cursor ->
- if (cursor?.moveToFirst() != true) return null
- filename = cursor.getString(cursor.getColumnIndexOrThrow(imageDataColumnIndex))
- }
-
- return filename
- }
-
- suspend fun getImages(): MutableList {
- val files = mutableListOf()
- if (mediaStoreCollection == null) return files
-
- getMediaStoreImageCursor(mediaStoreCollection).use { cursor ->
- val imageDataColumn = cursor?.getColumnIndexOrThrow(imageDataColumnIndex)
- val imageIdColumn = cursor?.getColumnIndexOrThrow(imageIdColumnIndex)
-
- if (cursor != null && imageDataColumn != null && imageIdColumn != null) {
- while (cursor.moveToNext()) {
- val id = cursor.getLong(imageIdColumn)
- val contentUri: Uri = ContentUris.withAppendedId(
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
- id
- )
- val contentFile = File(cursor.getString(imageDataColumn))
- files.add(MediaStoreFile(contentUri, contentFile, id))
- }
- }
- }
-
- return files
- }
-
- companion object {
- // Suppress DATA index deprecation warning since we need the file location for the Glide library
- @Suppress("DEPRECATION")
- private const val imageDataColumnIndex = MediaStore.Images.Media.DATA
- private const val imageIdColumnIndex = MediaStore.Images.Media._ID
- }
-}
-
-data class MediaStoreFile(val uri: Uri, val file: File, val id: Long)
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/utils/ViewExtensions.kt b/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/utils/ViewExtensions.kt
deleted file mode 100644
index b3357444..00000000
--- a/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/utils/ViewExtensions.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.example.cameraxbasic.utils
-
-import android.os.Build
-import android.view.DisplayCutout
-import android.view.View
-import android.view.Window
-import android.view.WindowManager
-import android.widget.ImageButton
-import androidx.annotation.RequiresApi
-import androidx.appcompat.app.AlertDialog
-import androidx.core.view.WindowCompat
-import androidx.core.view.WindowInsetsCompat
-import androidx.core.view.WindowInsetsControllerCompat
-
-/** Milliseconds used for UI animations */
-const val ANIMATION_FAST_MILLIS = 50L
-const val ANIMATION_SLOW_MILLIS = 100L
-
-/**
- * Simulate a button click, including a small delay while it is being pressed to trigger the
- * appropriate animations.
- */
-fun ImageButton.simulateClick(delay: Long = ANIMATION_FAST_MILLIS) {
- performClick()
- isPressed = true
- invalidate()
- postDelayed({
- invalidate()
- isPressed = false
- }, delay)
-}
-
-/** Pad this view with the insets provided by the device cutout (i.e. notch) */
-@RequiresApi(Build.VERSION_CODES.P)
-fun View.padWithDisplayCutout() {
-
- /** Helper method that applies padding from cutout's safe insets */
- fun doPadding(cutout: DisplayCutout) = setPadding(
- cutout.safeInsetLeft,
- cutout.safeInsetTop,
- cutout.safeInsetRight,
- cutout.safeInsetBottom)
-
- // Apply padding using the display cutout designated "safe area"
- rootWindowInsets?.displayCutout?.let { doPadding(it) }
-
- // Set a listener for window insets since view.rootWindowInsets may not be ready yet
- setOnApplyWindowInsetsListener { _, insets ->
- insets.displayCutout?.let { doPadding(it) }
- insets
- }
-}
-
-/** Same as [AlertDialog.show] but setting immersive mode in the dialog's window */
-fun AlertDialog.showImmersive() {
- // Set the dialog to not focusable
- window?.setFlags(
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
-
- // Make sure that the dialog's window is in full screen
- window?.let { hideSystemUI(it) }
-
- // Show the dialog while still in immersive mode
- show()
-
- // Set the dialog to focusable again
- window?.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
-}
-
-private fun hideSystemUI(window: Window) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- WindowCompat.setDecorFitsSystemWindows(window, false)
- WindowInsetsControllerCompat(window, window.decorView).let { controller ->
- controller.hide(WindowInsetsCompat.Type.systemBars())
- controller.systemBarsBehavior =
- WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
- }
- } else {
- // For api level 29 and before, set deprecated systemUiVisibility to the combination of all
- // flags required to put activity into immersive mode.
- @Suppress("DEPRECATION")
- val fullscreenFlags =
- View.SYSTEM_UI_FLAG_LOW_PROFILE or
- View.SYSTEM_UI_FLAG_FULLSCREEN or
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- @Suppress("DEPRECATION")
- window.decorView.systemUiVisibility = fullscreenFlags
- }
-}
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/res/color/selector_button_text.xml b/CameraXBasic/app/src/main/res/color/selector_button_text.xml
deleted file mode 100644
index 56ee9df3..00000000
--- a/CameraXBasic/app/src/main/res/color/selector_button_text.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/res/color/selector_ic.xml b/CameraXBasic/app/src/main/res/color/selector_ic.xml
deleted file mode 100644
index f808f929..00000000
--- a/CameraXBasic/app/src/main/res/color/selector_ic.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/res/drawable/button_round_corners.xml b/CameraXBasic/app/src/main/res/drawable/button_round_corners.xml
deleted file mode 100644
index 6a1a8166..00000000
--- a/CameraXBasic/app/src/main/res/drawable/button_round_corners.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/res/drawable/ic_back.xml b/CameraXBasic/app/src/main/res/drawable/ic_back.xml
deleted file mode 100644
index 6994a08d..00000000
--- a/CameraXBasic/app/src/main/res/drawable/ic_back.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
diff --git a/CameraXBasic/app/src/main/res/drawable/ic_delete.xml b/CameraXBasic/app/src/main/res/drawable/ic_delete.xml
deleted file mode 100644
index 798cf217..00000000
--- a/CameraXBasic/app/src/main/res/drawable/ic_delete.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
diff --git a/CameraXBasic/app/src/main/res/drawable/ic_launcher_background.xml b/CameraXBasic/app/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 8e258c0f..00000000
--- a/CameraXBasic/app/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,185 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/CameraXBasic/app/src/main/res/drawable/ic_outer_circle.xml b/CameraXBasic/app/src/main/res/drawable/ic_outer_circle.xml
deleted file mode 100644
index 9f4983c4..00000000
--- a/CameraXBasic/app/src/main/res/drawable/ic_outer_circle.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/res/drawable/ic_photo.xml b/CameraXBasic/app/src/main/res/drawable/ic_photo.xml
deleted file mode 100644
index 72b6b9b2..00000000
--- a/CameraXBasic/app/src/main/res/drawable/ic_photo.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/res/drawable/ic_share.xml b/CameraXBasic/app/src/main/res/drawable/ic_share.xml
deleted file mode 100644
index a36ac425..00000000
--- a/CameraXBasic/app/src/main/res/drawable/ic_share.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/res/drawable/ic_shutter.xml b/CameraXBasic/app/src/main/res/drawable/ic_shutter.xml
deleted file mode 100644
index 9bb91ab8..00000000
--- a/CameraXBasic/app/src/main/res/drawable/ic_shutter.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/res/drawable/ic_shutter_focused.xml b/CameraXBasic/app/src/main/res/drawable/ic_shutter_focused.xml
deleted file mode 100644
index 9bf521da..00000000
--- a/CameraXBasic/app/src/main/res/drawable/ic_shutter_focused.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraXBasic/app/src/main/res/drawable/ic_shutter_normal.xml b/CameraXBasic/app/src/main/res/drawable/ic_shutter_normal.xml
deleted file mode 100644
index cb50026e..00000000
--- a/CameraXBasic/app/src/main/res/drawable/ic_shutter_normal.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraXBasic/app/src/main/res/drawable/ic_shutter_pressed.xml b/CameraXBasic/app/src/main/res/drawable/ic_shutter_pressed.xml
deleted file mode 100644
index 9bf521da..00000000
--- a/CameraXBasic/app/src/main/res/drawable/ic_shutter_pressed.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraXBasic/app/src/main/res/drawable/ic_switch.xml b/CameraXBasic/app/src/main/res/drawable/ic_switch.xml
deleted file mode 100644
index e1c8cabf..00000000
--- a/CameraXBasic/app/src/main/res/drawable/ic_switch.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/res/layout-land/camera_ui_container.xml b/CameraXBasic/app/src/main/res/layout-land/camera_ui_container.xml
deleted file mode 100644
index 60a7dc77..00000000
--- a/CameraXBasic/app/src/main/res/layout-land/camera_ui_container.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/CameraXBasic/app/src/main/res/layout/activity_main.xml b/CameraXBasic/app/src/main/res/layout/activity_main.xml
deleted file mode 100644
index 0fe4342e..00000000
--- a/CameraXBasic/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
diff --git a/CameraXBasic/app/src/main/res/layout/camera_ui_container.xml b/CameraXBasic/app/src/main/res/layout/camera_ui_container.xml
deleted file mode 100644
index 3d6de459..00000000
--- a/CameraXBasic/app/src/main/res/layout/camera_ui_container.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/res/layout/fragment_camera.xml b/CameraXBasic/app/src/main/res/layout/fragment_camera.xml
deleted file mode 100644
index 5a8f95ab..00000000
--- a/CameraXBasic/app/src/main/res/layout/fragment_camera.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/res/layout/fragment_gallery.xml b/CameraXBasic/app/src/main/res/layout/fragment_gallery.xml
deleted file mode 100644
index 4214ea73..00000000
--- a/CameraXBasic/app/src/main/res/layout/fragment_gallery.xml
+++ /dev/null
@@ -1,78 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/CameraXBasic/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/CameraXBasic/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index d9ccb3e0..00000000
--- a/CameraXBasic/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/CameraXBasic/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index d9ccb3e0..00000000
--- a/CameraXBasic/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/res/mipmap-hdpi/ic_launcher.png b/CameraXBasic/app/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index edd7b363..00000000
Binary files a/CameraXBasic/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/CameraXBasic/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/CameraXBasic/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
deleted file mode 100644
index a050bf23..00000000
Binary files a/CameraXBasic/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/CameraXBasic/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/CameraXBasic/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index ec13a435..00000000
Binary files a/CameraXBasic/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/CameraXBasic/app/src/main/res/mipmap-mdpi/ic_launcher.png b/CameraXBasic/app/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 6fd4f910..00000000
Binary files a/CameraXBasic/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/CameraXBasic/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/CameraXBasic/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
deleted file mode 100644
index df81982e..00000000
Binary files a/CameraXBasic/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/CameraXBasic/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/CameraXBasic/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 26160487..00000000
Binary files a/CameraXBasic/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/CameraXBasic/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/CameraXBasic/app/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index fdae11da..00000000
Binary files a/CameraXBasic/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/CameraXBasic/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/CameraXBasic/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
deleted file mode 100644
index 667d5669..00000000
Binary files a/CameraXBasic/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/CameraXBasic/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/CameraXBasic/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index cb11f725..00000000
Binary files a/CameraXBasic/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/CameraXBasic/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/CameraXBasic/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 5d4fa36c..00000000
Binary files a/CameraXBasic/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/CameraXBasic/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/CameraXBasic/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
deleted file mode 100644
index 33dc1c6e..00000000
Binary files a/CameraXBasic/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/CameraXBasic/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/CameraXBasic/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 07d13934..00000000
Binary files a/CameraXBasic/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/CameraXBasic/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/CameraXBasic/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 675e3138..00000000
Binary files a/CameraXBasic/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/CameraXBasic/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/CameraXBasic/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
deleted file mode 100644
index 9f9808a0..00000000
Binary files a/CameraXBasic/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/CameraXBasic/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/CameraXBasic/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 54e07e05..00000000
Binary files a/CameraXBasic/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/CameraXBasic/app/src/main/res/navigation/nav_graph.xml b/CameraXBasic/app/src/main/res/navigation/nav_graph.xml
deleted file mode 100644
index 2c854a1f..00000000
--- a/CameraXBasic/app/src/main/res/navigation/nav_graph.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/res/values-v28/styles.xml b/CameraXBasic/app/src/main/res/values-v28/styles.xml
deleted file mode 100644
index 950f1470..00000000
--- a/CameraXBasic/app/src/main/res/values-v28/styles.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/CameraXBasic/app/src/main/res/values/colors.xml b/CameraXBasic/app/src/main/res/values/colors.xml
deleted file mode 100644
index 603acdc8..00000000
--- a/CameraXBasic/app/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
- #3F51B5
- #303F9F
- #FF4081
- #FFFFFFFF
- #DDFFFFFF
- #AAFFFFFF
-
diff --git a/CameraXBasic/app/src/main/res/values/dimens.xml b/CameraXBasic/app/src/main/res/values/dimens.xml
deleted file mode 100644
index 1c818010..00000000
--- a/CameraXBasic/app/src/main/res/values/dimens.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
- 16dp
- 32dp
- 48dp
- 64dp
- 92dp
-
- 4dp
- 8dp
- 16dp
-
- 4dp
- 8dp
- 12dp
-
- 32dp
- 64dp
- 92dp
-
- 12dp
- 80dp
- 24dp
- 80dp
-
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/res/values/ic_launcher_background.xml b/CameraXBasic/app/src/main/res/values/ic_launcher_background.xml
deleted file mode 100644
index c5d5899f..00000000
--- a/CameraXBasic/app/src/main/res/values/ic_launcher_background.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- #FFFFFF
-
\ No newline at end of file
diff --git a/CameraXBasic/app/src/main/res/values/strings.xml b/CameraXBasic/app/src/main/res/values/strings.xml
deleted file mode 100644
index 6b95fa5d..00000000
--- a/CameraXBasic/app/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
- CameraX Basic
- Capture
- Switch camera
- Gallery
- Back
- Share
- Share using
- Camera
- HDR
- Night
- Delete
- Delete current photo?
- Confirm
-
diff --git a/CameraXBasic/app/src/main/res/values/styles.xml b/CameraXBasic/app/src/main/res/values/styles.xml
deleted file mode 100644
index b9b5162e..00000000
--- a/CameraXBasic/app/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/CameraXBasic/app/src/test/java/com/android/example/cameraxbasic/MainInstrumentedTest.kt b/CameraXBasic/app/src/test/java/com/android/example/cameraxbasic/MainInstrumentedTest.kt
deleted file mode 100644
index 50c49fae..00000000
--- a/CameraXBasic/app/src/test/java/com/android/example/cameraxbasic/MainInstrumentedTest.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.example.cameraxbasic
-
-import android.Manifest
-import android.content.Context
-import androidx.test.core.app.ApplicationProvider
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.rule.ActivityTestRule
-import androidx.test.rule.GrantPermissionRule
-import org.junit.Assert.assertEquals
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class MainInstrumentedTest {
-
- @get:Rule
- val permissionRule = GrantPermissionRule.grant(
- Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
-
- @get:Rule
- val activityRule: ActivityTestRule = ActivityTestRule(MainActivity::class.java)
-
- @Test
- fun useAppContext() {
- // Context of the app under test
- val context = ApplicationProvider.getApplicationContext() as Context
- assertEquals("com.android.example.cameraxbasic", context.packageName)
- }
-}
\ No newline at end of file
diff --git a/CameraXBasic/build.gradle b/CameraXBasic/build.gradle
deleted file mode 100644
index c4f1d98d..00000000
--- a/CameraXBasic/build.gradle
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.
- */
-
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
-
-buildscript {
- // Top-level variables used for versioning
- ext.kotlin_version = '1.5.21'
- ext.java_version = JavaVersion.VERSION_1_8
-
- repositories {
- google()
- mavenCentral()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:7.2.2'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.4.1'
-
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
- }
-}
-
-allprojects {
- repositories {
- google()
- mavenCentral()
- }
-}
-
-task clean(type: Delete) {
- delete rootProject.buildDir
-}
diff --git a/CameraXBasic/gradle.properties b/CameraXBasic/gradle.properties
deleted file mode 100644
index c512ddd3..00000000
--- a/CameraXBasic/gradle.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# Project-wide Gradle settings.
-# IDE (e.g. Android Studio) users:
-# Gradle settings configured through the IDE *will override*
-# any settings specified in this file.
-# For more details on how to configure your build environment visit
-# http://www.gradle.org/docs/current/userguide/build_environment.html
-# Specifies the JVM arguments used for the daemon process.
-# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx1536m
-android.enableJetifier=true
-android.useAndroidX=true
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
\ No newline at end of file
diff --git a/CameraXBasic/gradle/wrapper/gradle-wrapper.jar b/CameraXBasic/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index f6b961fd..00000000
Binary files a/CameraXBasic/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/CameraXBasic/gradle/wrapper/gradle-wrapper.properties b/CameraXBasic/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 9a91c30b..00000000
--- a/CameraXBasic/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-#Fri May 21 10:07:10 CEST 2021
-distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
-distributionPath=wrapper/dists
-zipStorePath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
diff --git a/CameraXBasic/gradlew b/CameraXBasic/gradlew
deleted file mode 100755
index cccdd3d5..00000000
--- a/CameraXBasic/gradlew
+++ /dev/null
@@ -1,172 +0,0 @@
-#!/usr/bin/env sh
-
-##############################################################################
-##
-## Gradle start up script for UN*X
-##
-##############################################################################
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn () {
- echo "$*"
-}
-
-die () {
- echo
- echo "$*"
- echo
- exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
- else
- JAVACMD="$JAVA_HOME/bin/java"
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
- fi
- i=$((i+1))
- done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
-fi
-
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=$(save "$@")
-
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-
-# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
-if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
- cd "$(dirname "$0")"
-fi
-
-exec "$JAVACMD" "$@"
diff --git a/CameraXBasic/gradlew.bat b/CameraXBasic/gradlew.bat
deleted file mode 100644
index f9553162..00000000
--- a/CameraXBasic/gradlew.bat
+++ /dev/null
@@ -1,84 +0,0 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/CameraXBasic/settings.gradle b/CameraXBasic/settings.gradle
deleted file mode 100644
index b269de8f..00000000
--- a/CameraXBasic/settings.gradle
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.
- */
-
-include ':app'
diff --git a/CameraXExtensions/.gitignore b/CameraXExtensions/.gitignore
deleted file mode 100644
index 24bddc82..00000000
--- a/CameraXExtensions/.gitignore
+++ /dev/null
@@ -1,60 +0,0 @@
-# Built application files
-app/release
-*.apk
-*.ap_
-
-# Files for the ART/Dalvik VM
-*.dex
-
-# Java class files
-*.class
-
-# Generated files
-bin/
-gen/
-out/
-
-# Gradle files
-.gradle/
-build/
-
-# Local configuration file (sdk path, etc)
-local.properties
-
-# Proguard folder generated by Eclipse
-proguard/
-
-# Log Files
-*.log
-
-# Android Studio Navigation editor temp files
-.navigation/
-
-# Android Studio captures folder
-captures/
-
-# IntelliJ
-*.iml
-.idea/*
-!.idea/runConfigurations
-
-# Keystore files
-*.jks
-
-# External native build folder generated in Android Studio 2.2 and later
-.externalNativeBuild
-
-# Google Services (e.g. APIs or Firebase)
-google-services.json
-
-# Freeline
-freeline.py
-freeline/
-freeline_project_description.json
-
-# fastlane
-fastlane/report.xml
-fastlane/Preview.html
-fastlane/screenshots
-fastlane/test_output
-fastlane/readme.md
diff --git a/CameraXExtensions/.java-version b/CameraXExtensions/.java-version
deleted file mode 100644
index 8e2afd34..00000000
--- a/CameraXExtensions/.java-version
+++ /dev/null
@@ -1 +0,0 @@
-17
\ No newline at end of file
diff --git a/CameraXExtensions/README.md b/CameraXExtensions/README.md
deleted file mode 100644
index 0f9d36cf..00000000
--- a/CameraXExtensions/README.md
+++ /dev/null
@@ -1,53 +0,0 @@
-
-Android CameraX Extensions Sample
-=================================
-
-This sample demonstrates using Camera Extensions with CameraX API to capture a JPEG.
-The source code shows an example of how to display the camera preview, query for
-supported extensions, select an extension, and capture a still image.
-
-Introduction
-------------
-
-The [CameraX Extensions API][1] allows users to apply manufacturer specific
-extensions to still captures. The available extensions include: Auto, Bokeh, Face
-Retouch, HDR (High Dynamic Range), and Night.
-
-This sample displays the available list of camera extensions for the selected camera
-lens. Selecting an extensions allows the user to capture a still using that
-extension.
-
-[1]: https://developer.android.com/training/camerax/vendor-extensions
-
-Pre-requisites
---------------
-
-- Android SDK 32+
-- Android Studio 3.5+
-
-Screenshots
--------------
-
-
-Architecture:
-The sample demonstrates using an MVVM architecture. The view model emits the camera
-state and capture state. The UI emits actions. The activity acts as the mediator
-between the view model and view.
-
-
-Getting Started
----------------
-
-This sample uses the Gradle build system. To build this project, use the
-"gradlew build" command or use "Import Project" in Android Studio.
-
-Support
--------
-
-- Stack Overflow: http://stackoverflow.com/questions/tagged/android
-
-If you've found an error in this sample, please file an issue:
-https://github.com/android/camera-samples
-
-Patches are encouraged, and may be submitted by forking this project and
-submitting a pull request through GitHub. Please see CONTRIBUTING.md for more details.
diff --git a/CameraXExtensions/app/.gitignore b/CameraXExtensions/app/.gitignore
deleted file mode 100644
index 42afabfd..00000000
--- a/CameraXExtensions/app/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
\ No newline at end of file
diff --git a/CameraXExtensions/app/build.gradle b/CameraXExtensions/app/build.gradle
deleted file mode 100644
index 23f27ee4..00000000
--- a/CameraXExtensions/app/build.gradle
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.
- */
-
-plugins {
- id 'com.android.application'
- id 'kotlin-android'
- id 'kotlin-kapt'
-}
-
-kapt {
- correctErrorTypes = true
- useBuildCache = true
-}
-
-android {
- namespace 'com.example.android.cameraxextensions'
- compileSdk 34
-
- defaultConfig {
- applicationId "com.example.android.cameraxextensions"
- minSdk 24
- targetSdk 34
- versionCode 1
- versionName "1.0.0"
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- }
-
- buildFeatures {
- compose true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion = "1.5.8"
- }
-
- buildTypes {
- release {
- minifyEnabled true
- shrinkResources true
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
-
- compileOptions {
- sourceCompatibility rootProject.ext.java_version
- targetCompatibility rootProject.ext.java_version
- }
- kotlinOptions {
- jvmTarget = rootProject.ext.java_version
- }
-
-}
-
-dependencies {
- // Kotlin lang
- implementation 'androidx.core:core-ktx:1.13.1'
-
- // CameraX
- implementation "androidx.camera:camera-core:$camerax_version"
- implementation "androidx.camera:camera-camera2:$camerax_version"
- implementation "androidx.camera:camera-view:$camerax_version"
- implementation "androidx.camera:camera-lifecycle:$camerax_version"
-
- // CameraX Extensions
- implementation "androidx.camera:camera-extensions:$camerax_version"
-
- // Coroutines
- implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
- implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
- implementation "org.jetbrains.kotlinx:kotlinx-coroutines-guava:$coroutines_version"
-
- // Lifecycle
- implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
- implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
-
- // Image loading
- implementation "io.coil-kt:coil:2.4.0"
-
- // Material Components
- implementation 'com.google.android.material:material:1.12.0'
-
- // Compose
- implementation 'androidx.compose.material:material:1.6.7'
- implementation 'androidx.compose.ui:ui:1.6.7'
- implementation 'androidx.compose.ui:ui-tooling-preview:1.6.7'
- debugImplementation 'androidx.compose.ui:ui-tooling:1.6.7'
- implementation 'androidx.activity:activity-compose:1.9.0'
- implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0'
-
- implementation 'androidx.activity:activity-ktx:1.9.0'
- implementation 'androidx.appcompat:appcompat:1.6.1'
- implementation 'com.google.android.material:material:1.12.0'
- implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
- implementation "androidx.recyclerview:recyclerview:1.3.2"
-
- // Test
- testImplementation 'junit:junit:4.13.2'
- androidTestImplementation 'androidx.test.ext:junit:1.1.5'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
-}
diff --git a/CameraXExtensions/app/src/androidTest/java/com/example/android/cameraxextensions/ExampleInstrumentedTest.kt b/CameraXExtensions/app/src/androidTest/java/com/example/android/cameraxextensions/ExampleInstrumentedTest.kt
deleted file mode 100644
index 22acd77b..00000000
--- a/CameraXExtensions/app/src/androidTest/java/com/example/android/cameraxextensions/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions
-
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.ext.junit.runners.AndroidJUnit4
-
-import org.junit.Test
-import org.junit.runner.RunWith
-
-import org.junit.Assert.*
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-@RunWith(AndroidJUnit4::class)
-class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.example.android.cameraxextensions", appContext.packageName)
- }
-}
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/AndroidManifest.xml b/CameraXExtensions/app/src/main/AndroidManifest.xml
deleted file mode 100644
index 799334d7..00000000
--- a/CameraXExtensions/app/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/MainActivity.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/MainActivity.kt
deleted file mode 100644
index 783b633b..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/MainActivity.kt
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions
-
-import android.Manifest
-import android.content.pm.PackageManager
-import android.net.Uri
-import android.os.Bundle
-import android.util.Log
-import androidx.activity.OnBackPressedCallback
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.appcompat.app.AppCompatActivity
-import androidx.camera.core.ImageCaptureLatencyEstimate
-import androidx.camera.extensions.ExtensionMode
-import androidx.core.app.ActivityCompat
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import com.example.android.cameraxextensions.adapter.CameraExtensionItem
-import com.example.android.cameraxextensions.model.CameraState
-import com.example.android.cameraxextensions.model.CameraUiAction
-import com.example.android.cameraxextensions.model.CaptureState
-import com.example.android.cameraxextensions.model.PermissionState
-import com.example.android.cameraxextensions.repository.ImageCaptureRepository
-import com.example.android.cameraxextensions.ui.CameraExtensionsScreen
-import com.example.android.cameraxextensions.ui.doOnLaidOut
-import com.example.android.cameraxextensions.viewmodel.CameraExtensionsViewModel
-import com.example.android.cameraxextensions.viewmodel.CameraExtensionsViewModelFactory
-import com.example.android.cameraxextensions.viewstate.CaptureScreenViewState
-import com.example.android.cameraxextensions.viewstate.PostCaptureScreenViewState
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.launch
-
-/**
- * Displays the camera preview with camera controls and available extensions. Tapping on the shutter
- * button will capture a photo and display the photo.
- */
-class MainActivity : AppCompatActivity() {
- private companion object {
- const val TAG = "MainActivity"
- const val CAPTURE_LATENCY_INDICATOR_THRESHOLD_MS = 500
- }
-
- private val extensionName = mapOf(
- ExtensionMode.AUTO to R.string.camera_mode_auto,
- ExtensionMode.NIGHT to R.string.camera_mode_night,
- ExtensionMode.HDR to R.string.camera_mode_hdr,
- ExtensionMode.FACE_RETOUCH to R.string.camera_mode_face_retouch,
- ExtensionMode.BOKEH to R.string.camera_mode_bokeh,
- ExtensionMode.NONE to R.string.camera_mode_none,
- )
-
- // tracks the current view state
- private val captureScreenViewState = MutableStateFlow(CaptureScreenViewState())
-
- // handles back press if the current screen is the photo post capture screen
- private val postCaptureBackPressedCallback = object : OnBackPressedCallback(false) {
- override fun handleOnBackPressed() {
- lifecycleScope.launch {
- closePhotoPreview()
- }
- }
- }
-
- private lateinit var cameraExtensionsScreen: CameraExtensionsScreen
-
- // view model for operating on the camera and capturing a photo
- private lateinit var cameraExtensionsViewModel: CameraExtensionsViewModel
-
- // monitors changes in camera permission state
- private lateinit var permissionState: MutableStateFlow
-
- private var captureUri: Uri? = null
- private var progressComplete: Boolean = false
-
- private suspend fun showCapture() {
- if (captureUri == null || !progressComplete) return
-
- cameraExtensionsViewModel.stopPreview()
- captureScreenViewState.emit(
- captureScreenViewState.value
- .updatePostCaptureScreen {
- captureUri?.let {
- PostCaptureScreenViewState.PostCaptureScreenVisibleViewState(it)
- } ?: PostCaptureScreenViewState.PostCaptureScreenHiddenViewState
- }.updateCameraScreen {
- it.hideCameraControls()
- .hideProcessProgressViewState()
- }
- )
- captureUri = null
- progressComplete = false
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- setContentView(R.layout.activity_main)
-
- cameraExtensionsViewModel = ViewModelProvider(
- this,
- CameraExtensionsViewModelFactory(
- application,
- ImageCaptureRepository.create(applicationContext)
- )
- )[CameraExtensionsViewModel::class.java]
-
- // capture screen abstracts the UI logic and exposes simple functions on how to interact
- // with the UI layer.
- cameraExtensionsScreen = CameraExtensionsScreen(findViewById(R.id.root))
-
- // consume and dispatch the current view state to update the camera extensions screen
- lifecycleScope.launch {
- captureScreenViewState.collectLatest { state ->
- cameraExtensionsScreen.setCaptureScreenViewState(state)
- postCaptureBackPressedCallback.isEnabled =
- state.postCaptureScreenViewState is PostCaptureScreenViewState.PostCaptureScreenVisibleViewState
- }
- }
-
- onBackPressedDispatcher.addCallback(this, postCaptureBackPressedCallback)
-
- // initialize the permission state flow with the current camera permission status
- permissionState = MutableStateFlow(getCurrentPermissionState())
-
- val requestPermissionsLauncher =
- registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
- if (isGranted) {
- lifecycleScope.launch { permissionState.emit(PermissionState.Granted) }
- } else {
- lifecycleScope.launch { permissionState.emit(PermissionState.Denied(true)) }
- }
- }
-
- lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.RESUMED) {
- // check the current permission state every time upon the activity is resumed
- permissionState.emit(getCurrentPermissionState())
- }
- }
-
- // Consumes actions emitted by the UI and performs the appropriate operation associated with
- // the view model or permission flow.
- // Note that this flow is a shared flow and will not emit the last action unlike the state
- // flows exposed by the view model for consuming UI state.
- lifecycleScope.launch {
- cameraExtensionsScreen.action.collectLatest { action ->
- when (action) {
- is CameraUiAction.SelectCameraExtension -> {
- cameraExtensionsViewModel.setExtensionMode(action.extension)
- }
- CameraUiAction.ShutterButtonClick -> {
- cameraExtensionsViewModel.capturePhoto()
- }
- CameraUiAction.SwitchCameraClick -> {
- cameraExtensionsViewModel.switchCamera()
- }
- CameraUiAction.ClosePhotoPreviewClick -> {
- closePhotoPreview()
- }
- CameraUiAction.RequestPermissionClick -> {
- requestPermissionsLauncher.launch(Manifest.permission.CAMERA)
- }
- CameraUiAction.ProcessProgressComplete -> {
- progressComplete = true
- showCapture()
- }
- is CameraUiAction.Focus -> {
- cameraExtensionsViewModel.focus(action.meteringPoint)
- }
- is CameraUiAction.Scale -> {
- cameraExtensionsViewModel.scale(action.scaleFactor)
- }
- }
- }
- }
-
- // Consume state emitted by the view model to render the Photo Capture state.
- // Upon collecting this state, the last emitted state will be immediately received.
- lifecycleScope.launch {
- cameraExtensionsViewModel.captureUiState.collectLatest { state ->
- when (state) {
- CaptureState.CaptureNotReady -> {
- captureScreenViewState.emit(
- captureScreenViewState.value
- .updatePostCaptureScreen {
- PostCaptureScreenViewState.PostCaptureScreenHiddenViewState
- }
- .updateCameraScreen {
- it.enableCameraShutter(true)
- .enableSwitchLens(true)
- .hidePostview()
- }
- )
- }
- CaptureState.CaptureReady -> {
- captureUri = null
- progressComplete = false
- captureScreenViewState.emit(
- captureScreenViewState.value
- .updateCameraScreen {
- it.enableCameraShutter(true)
- .enableSwitchLens(true)
- }
- )
- }
- CaptureState.CaptureStarted -> {
- captureScreenViewState.emit(
- captureScreenViewState.value
- .updateCameraScreen {
- it.enableCameraShutter(false)
- .enableSwitchLens(false)
- }
- )
- }
- is CaptureState.CaptureFinished -> {
- captureUri = state.outputResults.savedUri
- if (!state.isProcessProgressSupported) {
- progressComplete = true
- }
- showCapture()
- }
- is CaptureState.CaptureFailed -> {
- cameraExtensionsScreen.showCaptureError("Couldn't take photo")
- cameraExtensionsViewModel.startPreview(
- this@MainActivity as LifecycleOwner, cameraExtensionsScreen.previewView
- )
- captureScreenViewState.emit(
- captureScreenViewState.value
- .updateCameraScreen {
- it.showCameraControls()
- .enableCameraShutter(true)
- .enableSwitchLens(true)
- .hideProcessProgressViewState()
- .hidePostview()
- }
- )
- }
- is CaptureState.CapturePostview -> {
- captureScreenViewState.emit(
- captureScreenViewState.value
- .updateCameraScreen {
- it.showPostview(state.bitmap)
- }
- )
- }
- is CaptureState.CaptureProcessProgress -> {
- captureScreenViewState.emit(
- captureScreenViewState.value
- .updateCameraScreen {
- it.showProcessProgressViewState(state.progress)
- }
- )
- }
- }
- }
- }
-
- // Because camera state is dependent on the camera permission status, we combine both camera
- // UI state and permission state such that each emission accurately captures the current
- // permission status and camera UI state.
- // The camera permission is always checked to see if it's granted. If it isn't then stop
- // interacting with the camera and display the permission request screen. The user can tap
- // on "Turn On" to request permissions.
- lifecycleScope.launch {
- permissionState.combine(cameraExtensionsViewModel.cameraUiState) { permissionState, cameraUiState ->
- Pair(permissionState, cameraUiState)
- }
- .collectLatest { (permissionState, cameraUiState) ->
- when (permissionState) {
- PermissionState.Granted -> {
- cameraExtensionsScreen.hidePermissionsRequest()
- }
- is PermissionState.Denied -> {
- if (cameraUiState.cameraState != CameraState.PREVIEW_STOPPED) {
- cameraExtensionsScreen.showPermissionsRequest(permissionState.shouldShowRationale)
- return@collectLatest
- }
- }
- }
-
- when (cameraUiState.cameraState) {
- CameraState.NOT_READY -> {
- captureScreenViewState.emit(
- captureScreenViewState.value
- .updatePostCaptureScreen {
- PostCaptureScreenViewState.PostCaptureScreenHiddenViewState
- }
- .updateCameraScreen {
- it.showCameraControls()
- .hidePostview()
- .enableCameraShutter(false)
- .enableSwitchLens(false)
- }
- )
- cameraExtensionsViewModel.initializeCamera()
- }
- CameraState.READY -> {
- Log.d(TAG, "Camera is ready")
- cameraExtensionsScreen.previewView.doOnLaidOut {
- cameraExtensionsViewModel.startPreview(
- this@MainActivity as LifecycleOwner,
- cameraExtensionsScreen.previewView
- )
- }
- captureScreenViewState.emit(
- captureScreenViewState.value
- .updateCameraScreen { s ->
- s.showCameraControls()
- .setAvailableExtensions(
- cameraUiState.availableExtensions.map {
- CameraExtensionItem(
- it,
- getString(extensionName[it]!!),
- cameraUiState.extensionMode == it
- )
- }
- )
- }
- )
- }
- CameraState.PREVIEW_ACTIVE -> {
- Log.d(TAG, "Camera preview is active")
- captureScreenViewState.emit(captureScreenViewState.value.updateCameraScreen { s ->
- val latencyEstimate = cameraUiState.realtimeCaptureLatencyEstimate
- if (latencyEstimate == ImageCaptureLatencyEstimate.UNDEFINED_IMAGE_CAPTURE_LATENCY) {
- Log.d(TAG, "Camera preview is active: hide latency estimate indicator")
- s.hideLatencyEstimateIndicator()
- } else if (latencyEstimate.captureLatencyMillis <= CAPTURE_LATENCY_INDICATOR_THRESHOLD_MS) {
- Log.d(TAG, "Camera preview is active: hide latency estimate indicator (under ${CAPTURE_LATENCY_INDICATOR_THRESHOLD_MS}ms)")
- s.hideLatencyEstimateIndicator()
- } else {
- Log.d(TAG, "Camera preview is active: show latency estimate indicator (${latencyEstimate.captureLatencyMillis}ms")
- s.showLatencyEstimateIndicator(latencyEstimate.captureLatencyMillis)
- }
- })
- }
- CameraState.PREVIEW_STOPPED -> Unit
- }
- }
- }
- }
-
- private suspend fun closePhotoPreview() {
- captureScreenViewState.emit(
- captureScreenViewState.value
- .updateCameraScreen { state ->
- state.showCameraControls()
- state.hidePostview()
- }
- .updatePostCaptureScreen {
- PostCaptureScreenViewState.PostCaptureScreenHiddenViewState
- }
- )
- cameraExtensionsViewModel.startPreview(
- this as LifecycleOwner,
- cameraExtensionsScreen.previewView
- )
- }
-
- private fun getCurrentPermissionState(): PermissionState {
- val status = ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
- return if (status == PackageManager.PERMISSION_GRANTED) {
- PermissionState.Granted
- } else {
- PermissionState.Denied(
- ActivityCompat.shouldShowRequestPermissionRationale(
- this,
- Manifest.permission.CAMERA
- )
- )
- }
- }
-}
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/adapter/CameraExtensionItem.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/adapter/CameraExtensionItem.kt
deleted file mode 100644
index 9511ec96..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/adapter/CameraExtensionItem.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions.adapter
-
-import androidx.camera.extensions.ExtensionMode
-
-/**
- * Defines the item model for a camera extension displayed by the adapter.
- * @see CameraExtensionsSelectorAdapter
- */
-data class CameraExtensionItem(
- @ExtensionMode.Mode val extensionMode: Int,
- val name: String,
- val selected: Boolean = false
-)
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/adapter/CameraExtensionsSelectorAdapter.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/adapter/CameraExtensionsSelectorAdapter.kt
deleted file mode 100644
index 9931906a..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/adapter/CameraExtensionsSelectorAdapter.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions.adapter
-
-import android.graphics.Color
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.ListAdapter
-import androidx.recyclerview.widget.RecyclerView
-import com.example.android.cameraxextensions.R
-
-/**
- * Adapter used to display CameraExtensionItems in a RecyclerView.
- */
-class CameraExtensionsSelectorAdapter(private val onItemClick: (view: View) -> Unit) :
- ListAdapter(ItemCallback()) {
-
- override fun onCreateViewHolder(
- parent: ViewGroup,
- viewType: Int
- ): CameraExtensionItemViewHolder =
- CameraExtensionItemViewHolder(
- LayoutInflater.from(parent.context)
- .inflate(R.layout.view_extension_type, parent, false) as TextView,
- onItemClick
- )
-
- override fun onBindViewHolder(holder: CameraExtensionItemViewHolder, position: Int) {
- holder.bind(getItem(position))
- }
-
- internal class ItemCallback : DiffUtil.ItemCallback() {
- override fun areItemsTheSame(
- oldItem: CameraExtensionItem,
- newItem: CameraExtensionItem
- ): Boolean = oldItem.extensionMode == newItem.extensionMode
-
- override fun areContentsTheSame(
- oldItem: CameraExtensionItem,
- newItem: CameraExtensionItem
- ): Boolean = oldItem.selected == newItem.selected
- }
-}
-
-class CameraExtensionItemViewHolder internal constructor(
- private val extensionView: TextView,
- private val onItemClick: (view: View) -> Unit
-) :
- RecyclerView.ViewHolder(extensionView) {
-
- init {
- extensionView.setOnClickListener { onItemClick(it) }
- }
-
- internal fun bind(extensionModel: CameraExtensionItem) {
- extensionView.text = extensionModel.name
- if (extensionModel.selected) {
- extensionView.setBackgroundResource(R.drawable.pill_selected_background)
- extensionView.setTextColor(Color.BLACK)
- } else {
- extensionView.setBackgroundResource(R.drawable.pill_unselected_background)
- extensionView.setTextColor(Color.WHITE)
- }
- }
-}
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/app/CameraExtensionsApplication.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/app/CameraExtensionsApplication.kt
deleted file mode 100644
index 888bca2f..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/app/CameraExtensionsApplication.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions.app
-
-import android.app.Application
-import android.util.Log
-import androidx.camera.camera2.Camera2Config
-import androidx.camera.core.CameraXConfig
-import coil.ImageLoader
-import coil.ImageLoaderFactory
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.asExecutor
-
-class CameraExtensionsApplication : Application(), CameraXConfig.Provider, ImageLoaderFactory {
- override fun getCameraXConfig(): CameraXConfig =
- CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
- .setCameraExecutor(Dispatchers.IO.asExecutor())
- .setMinimumLoggingLevel(Log.ERROR)
- .build()
-
- override fun newImageLoader(): ImageLoader =
- ImageLoader.Builder(this)
- .crossfade(true)
- .build()
-}
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/model/CameraUiAction.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/model/CameraUiAction.kt
deleted file mode 100644
index 13c9dfb1..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/model/CameraUiAction.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions.model
-
-import androidx.camera.core.MeteringPoint
-import androidx.camera.extensions.ExtensionMode
-
-/**
- * User initiated actions related to camera operations.
- */
-sealed class CameraUiAction {
- data object RequestPermissionClick : CameraUiAction()
- data object SwitchCameraClick : CameraUiAction()
- data object ShutterButtonClick : CameraUiAction()
- data object ClosePhotoPreviewClick : CameraUiAction()
- data object ProcessProgressComplete : CameraUiAction()
- data class SelectCameraExtension(@ExtensionMode.Mode val extension: Int) : CameraUiAction()
- data class Focus(val meteringPoint: MeteringPoint) : CameraUiAction()
- data class Scale(val scaleFactor: Float) : CameraUiAction()
-}
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/model/CameraUiState.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/model/CameraUiState.kt
deleted file mode 100644
index a9cc63eb..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/model/CameraUiState.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions.model
-
-import android.graphics.Bitmap
-import androidx.camera.core.CameraSelector.LENS_FACING_BACK
-import androidx.camera.core.CameraSelector.LensFacing
-import androidx.camera.core.ImageCapture
-import androidx.camera.core.ImageCaptureLatencyEstimate
-import androidx.camera.extensions.ExtensionMode
-
-/**
- * Defines the current UI state of the camera during pre-capture.
- * The state encapsulates the available camera extensions, the available camera lenses to toggle,
- * the current camera lens, the current extension mode, and the state of the camera.
- */
-data class CameraUiState(
- val cameraState: CameraState = CameraState.NOT_READY,
- val availableExtensions: List = emptyList(),
- val availableCameraLens: List = listOf(LENS_FACING_BACK),
- @LensFacing val cameraLens: Int = LENS_FACING_BACK,
- @ExtensionMode.Mode val extensionMode: Int = ExtensionMode.NONE,
- val realtimeCaptureLatencyEstimate: ImageCaptureLatencyEstimate =
- ImageCaptureLatencyEstimate.UNDEFINED_IMAGE_CAPTURE_LATENCY
-)
-
-/**
- * Defines the current state of the camera.
- */
-enum class CameraState {
- /**
- * Camera hasn't been initialized.
- */
- NOT_READY,
-
- /**
- * Camera is open and presenting a preview stream.
- */
- READY,
-
- /**
- * Camera is open and preview stream is currently running.
- * Updates during this period are from camera state operations.
- */
- PREVIEW_ACTIVE,
-
- /**
- * Camera is initialized but the preview has been stopped.
- */
- PREVIEW_STOPPED
-}
-
-/**
- * Defines the various states during post-capture.
- */
-sealed class CaptureState {
- /**
- * Capture capability isn't ready. This could be because the camera isn't initialized, or the
- * camera is changing the lens, or the camera is switching to a new extension mode.
- */
- object CaptureNotReady : CaptureState()
-
- /**
- * Capture capability is ready.
- */
- object CaptureReady : CaptureState()
-
- /**
- * Capture process has started.
- */
- object CaptureStarted : CaptureState()
-
- /**
- * Capture postview is ready
- */
- data class CapturePostview(val bitmap: Bitmap): CaptureState()
-
- /**
- * Capture process progress updated with the [progress] value
- */
- data class CaptureProcessProgress(val progress: Int): CaptureState()
-
- /**
- * Capture completed successfully.
- */
- data class CaptureFinished(
- val outputResults: ImageCapture.OutputFileResults,
- val isProcessProgressSupported: Boolean
- ) : CaptureState()
-
- /**
- * Capture failed with an error.
- */
- data class CaptureFailed(val exception: Exception) : CaptureState()
-}
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/model/PermissionState.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/model/PermissionState.kt
deleted file mode 100644
index 7b5e789a..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/model/PermissionState.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions.model
-
-sealed interface PermissionState {
- object Granted : PermissionState
- data class Denied(val shouldShowRationale: Boolean) : PermissionState
-}
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/repository/ImageCaptureRepository.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/repository/ImageCaptureRepository.kt
deleted file mode 100644
index f2a93142..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/repository/ImageCaptureRepository.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions.repository
-
-import android.content.Context
-import android.content.Intent
-import android.hardware.Camera.ACTION_NEW_PICTURE
-import android.media.MediaScannerConnection
-import android.net.Uri
-import android.webkit.MimeTypeMap
-import androidx.core.content.FileProvider
-import androidx.core.net.toFile
-import com.example.android.cameraxextensions.R
-import java.io.File
-import java.util.*
-
-/**
- * Manages photo capture filename and location generation. Once a photo is captured and saved to
- * disk, the repository will also notify that the image has been created such that other
- * applications can view it.
- */
-class ImageCaptureRepository internal constructor(private val rootDirectory: File) {
- companion object {
- const val TAG = "ImageCaptureRepository"
-
- private const val PHOTO_EXTENSION = ".jpg"
-
- fun create(context: Context): ImageCaptureRepository {
- // Use external media if it is available and this app's file directory otherwise
- val appContext = context.applicationContext
- val mediaDir = context.externalMediaDirs.firstOrNull()?.let {
- File(it, appContext.resources.getString(R.string.app_name)).apply { mkdirs() }
- }
- val file = if (mediaDir?.exists() == true) mediaDir else appContext.filesDir
- return ImageCaptureRepository(file)
- }
- }
-
- fun notifyImageCreated(context: Context, savedUri: Uri) {
- val file = savedUri.toFile()
- val fileProviderUri =
- FileProvider.getUriForFile(context, context.packageName + ".provider", file)
- context.sendBroadcast(
- Intent(ACTION_NEW_PICTURE, fileProviderUri)
- )
-
- // Notify other apps so they can access the captured image
- val mimeType = MimeTypeMap.getSingleton()
- .getMimeTypeFromExtension(file.extension)
-
- MediaScannerConnection.scanFile(
- context,
- arrayOf(file.absolutePath),
- arrayOf(mimeType),
- ) { _, _ -> }
- }
-
- fun createImageOutputFile(): File = File(rootDirectory, generateFilename(PHOTO_EXTENSION))
-
- private fun generateFilename(extension: String): String =
- UUID.randomUUID().toString() + extension
-}
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/CameraExtensionsScreen.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/CameraExtensionsScreen.kt
deleted file mode 100644
index 14311cfa..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/CameraExtensionsScreen.kt
+++ /dev/null
@@ -1,455 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions.ui
-
-import android.animation.Animator
-import android.animation.AnimatorListenerAdapter
-import android.animation.ObjectAnimator
-import android.annotation.SuppressLint
-import android.content.Context
-import android.graphics.Bitmap
-import android.net.Uri
-import android.util.TypedValue
-import android.view.GestureDetector.SimpleOnGestureListener
-import android.view.MotionEvent
-import android.view.ScaleGestureDetector
-import android.view.View
-import android.widget.ImageView
-import android.widget.TextView
-import android.widget.Toast
-import androidx.camera.view.PreviewView
-import androidx.core.animation.doOnEnd
-import androidx.core.view.GestureDetectorCompat
-import androidx.core.view.isVisible
-import androidx.dynamicanimation.animation.DynamicAnimation
-import androidx.dynamicanimation.animation.SpringAnimation
-import androidx.dynamicanimation.animation.SpringForce
-import androidx.lifecycle.findViewTreeLifecycleOwner
-import androidx.lifecycle.lifecycleScope
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import coil.load
-import com.example.android.cameraxextensions.R
-import com.example.android.cameraxextensions.adapter.CameraExtensionsSelectorAdapter
-import com.example.android.cameraxextensions.model.CameraUiAction
-import com.example.android.cameraxextensions.viewstate.CameraPreviewScreenViewState
-import com.example.android.cameraxextensions.viewstate.CaptureScreenViewState
-import com.example.android.cameraxextensions.viewstate.PostCaptureScreenViewState
-import com.google.android.material.progressindicator.CircularProgressIndicator
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.launch
-import java.util.concurrent.TimeUnit
-import kotlin.math.max
-import kotlin.math.roundToInt
-
-/**
- * Displays the camera preview and captured photo.
- * Encapsulates the details of how the screen is constructed and exposes a set of explicit
- * operations clients can perform on the screen.
- */
-@SuppressLint("ClickableViewAccessibility")
-class CameraExtensionsScreen(private val root: View) {
-
- private companion object {
- // animation constants for focus point
- private const val SPRING_STIFFNESS_ALPHA_OUT = 100f
- private const val SPRING_STIFFNESS = 800f
- private const val SPRING_DAMPING_RATIO = 0.35f
- private const val MAX_PROGRESS_ANIM_DURATION_MS = 3000
- }
-
- private val context: Context = root.context
-
- private val cameraShutterButton: View = root.findViewById(R.id.cameraShutter)
- private val photoPreview: ImageView = root.findViewById(R.id.photoPreview)
- private val closePhotoPreview: View = root.findViewById(R.id.closePhotoPreview)
- private val switchLensButton = root.findViewById(R.id.switchLens)
- private val extensionSelector: RecyclerView = root.findViewById(R.id.extensionSelector)
- private val extensionsAdapter: CameraExtensionsSelectorAdapter
- private val focusPointView: View = root.findViewById(R.id.focusPoint)
- private val permissionsRationaleContainer: View =
- root.findViewById(R.id.permissionsRationaleContainer)
- private val permissionsRationale: TextView = root.findViewById(R.id.permissionsRationale)
- private val permissionsRequestButton: TextView =
- root.findViewById(R.id.permissionsRequestButton)
- private val photoPostview: ImageView = root.findViewById(R.id.photoPostview)
- private val processProgressContainer: View =
- root.findViewById(R.id.processProgressContainer)
- private val processProgressIndicator: CircularProgressIndicator =
- root.findViewById(R.id.processProgressIndicator)
- private val latencyEstimateIndicator: TextView =
- root.findViewById(R.id.latencyEstimateIndicator)
-
- val previewView: PreviewView = root.findViewById(R.id.previewView)
-
- private val _action: MutableSharedFlow = MutableSharedFlow()
- val action: Flow = _action
-
- init {
- val snapHelper = CenterItemSnapHelper()
-
- extensionsAdapter = CameraExtensionsSelectorAdapter { view -> onItemClick(view) }
- extensionSelector.apply {
- layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
- adapter = extensionsAdapter
- addItemDecoration(OffsetCenterItemDecoration())
- addOnScrollListener(object : RecyclerView.OnScrollListener() {
- private var snapPosition = RecyclerView.NO_POSITION
-
- override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
- if (newState == RecyclerView.SCROLL_STATE_IDLE) {
- val layoutManager = recyclerView.layoutManager ?: return
- val snapView = snapHelper.findSnapView(layoutManager) ?: return
- val newSnapPosition = layoutManager.getPosition(snapView)
- onItemSelected(snapPosition, newSnapPosition)
- snapPosition = newSnapPosition
- }
- }
-
- private fun onItemSelected(oldPosition: Int, newPosition: Int) {
- if (oldPosition == newPosition) return
- selectItem(newPosition)
- extensionsAdapter.currentList[newPosition]
- ?.let {
- root.findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
- _action.emit(CameraUiAction.SelectCameraExtension(it.extensionMode))
- }
- }
- }
-
- private fun selectItem(position: Int) {
- val data =
- extensionsAdapter.currentList.mapIndexed { index, cameraExtensionModel ->
- cameraExtensionModel.copy(selected = position == index)
- }
- extensionsAdapter.submitList(data)
- }
- })
- }
-
- snapHelper.attachToRecyclerView(extensionSelector)
-
- cameraShutterButton.setOnClickListener {
- root.findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
- _action.emit(CameraUiAction.ShutterButtonClick)
- }
- }
-
- switchLensButton.setOnClickListener {
- switchLens(root, it)
- }
-
- closePhotoPreview.setOnClickListener {
- root.findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
- _action.emit(CameraUiAction.ClosePhotoPreviewClick)
- }
- }
-
- permissionsRequestButton.setOnClickListener {
- root.findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
- _action.emit(CameraUiAction.RequestPermissionClick)
- }
- }
-
- val gestureDetector = GestureDetectorCompat(context, object : SimpleOnGestureListener() {
- override fun onDown(e: MotionEvent): Boolean = true
-
- override fun onSingleTapUp(e: MotionEvent): Boolean {
- val meteringPointFactory = previewView.meteringPointFactory
- val focusPoint = meteringPointFactory.createPoint(e.x, e.y)
- root.findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
- _action.emit(CameraUiAction.Focus(focusPoint))
- }
- showFocusPoint(focusPointView, e.x, e.y)
- return true
- }
-
- override fun onDoubleTap(e: MotionEvent): Boolean {
- switchLens(root, switchLensButton)
- return true
- }
- })
-
- val scaleGestureDetector = ScaleGestureDetector(
- context,
- object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
- override fun onScale(detector: ScaleGestureDetector): Boolean {
- root.findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
- _action.emit(CameraUiAction.Scale(detector.scaleFactor))
- }
- return true
- }
- })
-
- previewView.setOnTouchListener { _, event ->
- var didConsume = scaleGestureDetector.onTouchEvent(event)
- if (!scaleGestureDetector.isInProgress) {
- didConsume = gestureDetector.onTouchEvent(event)
- }
- didConsume
- }
- }
-
- fun setCaptureScreenViewState(state: CaptureScreenViewState) {
- setCameraScreenViewState(state.cameraPreviewScreenViewState)
- when (state.postCaptureScreenViewState) {
- PostCaptureScreenViewState.PostCaptureScreenHiddenViewState -> hidePhoto()
- is PostCaptureScreenViewState.PostCaptureScreenVisibleViewState -> showPhoto(state.postCaptureScreenViewState.uri)
- }
- }
-
- fun showCaptureError(errorMessage: String) {
- Toast.makeText(context, errorMessage, Toast.LENGTH_LONG).show()
- }
-
- fun hidePermissionsRequest() {
- permissionsRationaleContainer.isVisible = false
- }
-
- fun showPermissionsRequest(shouldShowRationale: Boolean) {
- permissionsRationaleContainer.isVisible = true
- if (shouldShowRationale) {
- permissionsRationale.text =
- context.getString(R.string.camera_permissions_request_with_rationale)
- } else {
- permissionsRationale.text = context.getString(R.string.camera_permissions_request)
- }
- }
-
- private fun showPostview(bitmap: Bitmap) {
- if (photoPostview.isVisible) return
- photoPostview.isVisible = true
- photoPostview.load(bitmap) {
- crossfade(true)
- crossfade(200)
- }
- }
-
- private fun hidePostview() {
- photoPostview.isVisible = false
- }
-
- private fun showProcessProgressIndicator(progress: Int) {
- processProgressContainer.isVisible = true
- if (progress == processProgressIndicator.progress) return
-
- ObjectAnimator.ofInt(processProgressIndicator, "progress", progress).apply {
- val currentProgress = processProgressIndicator.progress
- val progressStep = max(0, progress - currentProgress)
- duration = (progressStep / 100f * MAX_PROGRESS_ANIM_DURATION_MS).toLong()
- doOnEnd {
- if (animatedValue == 100) {
- root.findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
- _action.emit(CameraUiAction.ProcessProgressComplete)
- }
- }
- }
- start()
- }
- }
-
- private fun hideProcessProgressIndicator() {
- processProgressContainer.isVisible = false
- processProgressIndicator.progress = 0
- }
-
- private fun showLatencyEstimate(latencyEstimateMillis: Long) {
- val estimateSeconds = (latencyEstimateMillis.toFloat() / 1000).roundToInt()
-
- if (!latencyEstimateIndicator.isVisible) {
- val alphaAnimation =
- SpringAnimation(latencyEstimateIndicator, DynamicAnimation.ALPHA, 1f).apply {
- spring.stiffness = SpringForce.STIFFNESS_LOW
- spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
- }
-
- val scaleAnimationX =
- SpringAnimation(latencyEstimateIndicator, DynamicAnimation.SCALE_X, 1f).apply {
- spring.stiffness = SpringForce.STIFFNESS_LOW
- spring.dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY
- }
-
- val scaleAnimationY =
- SpringAnimation(latencyEstimateIndicator, DynamicAnimation.SCALE_Y, 1f).apply {
- spring.stiffness = SpringForce.STIFFNESS_LOW
- spring.dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY
- }
-
- latencyEstimateIndicator.apply {
- isVisible = true
- alpha = 0f
- scaleX = 0.2f
- scaleY = 0.2f
- }
-
- alphaAnimation.start()
- scaleAnimationX.start()
- scaleAnimationY.start()
- }
-
- latencyEstimateIndicator.text =
- context.getString(R.string.latency_estimate, estimateSeconds)
- }
-
- private fun hideLatencyEstimate() {
- if (latencyEstimateIndicator.isVisible) {
- val alphaAnimation =
- SpringAnimation(latencyEstimateIndicator, DynamicAnimation.ALPHA, 0f).apply {
- spring.stiffness = SpringForce.STIFFNESS_LOW
- spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
-
- addEndListener { _, canceled, _, _ ->
- if (!canceled) {
- latencyEstimateIndicator.isVisible = false
- latencyEstimateIndicator.text = ""
- }
- }
- }
-
- val scaleAnimationX =
- SpringAnimation(latencyEstimateIndicator, DynamicAnimation.SCALE_X, 0f).apply {
- spring.stiffness = SpringForce.STIFFNESS_LOW
- spring.dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY
- }
-
- val scaleAnimationY =
- SpringAnimation(latencyEstimateIndicator, DynamicAnimation.SCALE_Y, 0f).apply {
- spring.stiffness = SpringForce.STIFFNESS_LOW
- spring.dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY
- }
-
- alphaAnimation.start()
- scaleAnimationX.start()
- scaleAnimationY.start()
- }
- }
-
- private fun showPhoto(uri: Uri?) {
- if (uri == null) return
- photoPreview.isVisible = true
- photoPreview.load(uri) {
- crossfade(true)
- crossfade(200)
- }
- closePhotoPreview.isVisible = true
- }
-
- private fun hidePhoto() {
- photoPreview.isVisible = false
- closePhotoPreview.isVisible = false
- }
-
- private fun setCameraScreenViewState(state: CameraPreviewScreenViewState) {
- cameraShutterButton.isEnabled = state.shutterButtonViewState.isEnabled
- cameraShutterButton.isVisible = state.shutterButtonViewState.isVisible
-
- switchLensButton.isEnabled = state.switchLensButtonViewState.isEnabled
- switchLensButton.isVisible = state.switchLensButtonViewState.isVisible
-
- extensionSelector.isVisible = state.extensionsSelectorViewState.isVisible
- extensionsAdapter.submitList(state.extensionsSelectorViewState.extensions)
-
- if (state.postviewViewState.isVisible) {
- showPostview(state.postviewViewState.bitmap!!)
- } else {
- hidePostview()
- }
-
- if (state.processProgressViewState.isVisible) {
- showProcessProgressIndicator(state.processProgressViewState.progress)
- } else {
- hideProcessProgressIndicator()
- }
-
- if (state.latencyEstimateIndicatorViewState.isVisible) {
- showLatencyEstimate(state.latencyEstimateIndicatorViewState.latencyEstimateMillis)
- } else {
- hideLatencyEstimate()
- }
- }
-
- private fun onItemClick(view: View) {
- val layoutManager = extensionSelector.layoutManager as? LinearLayoutManager ?: return
- val viewMiddle = view.left + view.width / 2
- val middle = layoutManager.width / 2
- val dx = viewMiddle - middle
- extensionSelector.smoothScrollBy(dx, 0)
- }
-
- private fun switchLens(root: View, switchLensButton: View) {
- root.findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
- _action.emit(CameraUiAction.SwitchCameraClick)
- }
- switchLensButton.animate().apply {
- rotation(180f)
- duration = 300L
- setListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- switchLensButton.rotation = 0f
- }
- })
- start()
- }
- }
-
- private fun showFocusPoint(view: View, x: Float, y: Float) {
- val drawable = FocusPointDrawable()
- val strokeWidth = TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP,
- 3f,
- context.resources.displayMetrics
- )
- drawable.setStrokeWidth(strokeWidth)
-
- val alphaAnimation = SpringAnimation(view, DynamicAnimation.ALPHA, 1f).apply {
- spring.stiffness = SPRING_STIFFNESS
- spring.dampingRatio = SPRING_DAMPING_RATIO
-
- addEndListener { _, _, _, _ ->
- SpringAnimation(view, DynamicAnimation.ALPHA, 0f)
- .apply {
- spring.stiffness = SPRING_STIFFNESS_ALPHA_OUT
- spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
- }
- .start()
- }
- }
- val scaleAnimationX = SpringAnimation(view, DynamicAnimation.SCALE_X, 1f).apply {
- spring.stiffness = SPRING_STIFFNESS
- spring.dampingRatio = SPRING_DAMPING_RATIO
- }
- val scaleAnimationY = SpringAnimation(view, DynamicAnimation.SCALE_Y, 1f).apply {
- spring.stiffness = SPRING_STIFFNESS
- spring.dampingRatio = SPRING_DAMPING_RATIO
- }
-
- view.apply {
- background = drawable
- isVisible = true
- translationX = x - width / 2f
- translationY = y - height / 2f
- alpha = 0f
- scaleX = 1.5f
- scaleY = 1.5f
- }
-
- alphaAnimation.start()
- scaleAnimationX.start()
- scaleAnimationY.start()
- }
-}
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/CenterItemSnapHelper.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/CenterItemSnapHelper.kt
deleted file mode 100644
index 775c3671..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/CenterItemSnapHelper.kt
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions.ui
-
-import android.content.Context
-import android.util.DisplayMetrics
-import android.view.View
-import android.view.animation.DecelerateInterpolator
-import android.widget.Scroller
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.LinearSmoothScroller
-import androidx.recyclerview.widget.LinearSnapHelper
-import androidx.recyclerview.widget.RecyclerView
-import androidx.recyclerview.widget.RecyclerView.LayoutManager
-import androidx.recyclerview.widget.RecyclerView.SmoothScroller
-import androidx.recyclerview.widget.RecyclerView.SmoothScroller.ScrollVectorProvider
-import kotlin.math.abs
-import kotlin.math.max
-import kotlin.math.min
-
-/**
- * Snaps the item to the center of the RecyclerView. Note that this SnapHelper ignores any
- * decorations applied to the child view. This is required since the first and last item are
- * centered by applying padding to the start or end of the view via an Item Decoration
- * @see OffsetCenterItemDecoration
- */
-class CenterItemSnapHelper : LinearSnapHelper() {
-
- companion object {
- private const val MILLISECONDS_PER_INCH = 100f
- private const val MAX_SCROLL_ON_FLING_DURATION_MS = 1000
- }
-
- private var context: Context? = null
- private var recyclerView: RecyclerView? = null
- private var scroller: Scroller? = null
- private var maxScrollDistance = 0
-
- override fun attachToRecyclerView(recyclerView: RecyclerView?) {
- if (recyclerView != null) {
- context = recyclerView.context
- this.recyclerView = recyclerView
- scroller = Scroller(context, DecelerateInterpolator())
- } else {
- context = null
- this.recyclerView = null
- scroller = null
- }
- super.attachToRecyclerView(recyclerView)
- }
-
- override fun findSnapView(layoutManager: LayoutManager?): View? =
- findMiddleView(layoutManager)
-
- override fun createScroller(layoutManager: LayoutManager): SmoothScroller? {
- if (layoutManager !is ScrollVectorProvider)
- return super.createScroller(layoutManager)
- val context = context ?: return null
- return object : LinearSmoothScroller(context) {
- override fun onTargetFound(
- targetView: View,
- state: RecyclerView.State,
- action: Action
- ) {
- val snapDistance = calculateDistanceToFinalSnap(layoutManager, targetView)
- val dx = snapDistance[0]
- val dy = snapDistance[1]
- val dt = calculateTimeForDeceleration(Math.abs(dx))
- val time = max(1, min(MAX_SCROLL_ON_FLING_DURATION_MS, dt))
- action.update(dx, dy, time, mDecelerateInterpolator)
- }
-
- override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float =
- MILLISECONDS_PER_INCH / displayMetrics.densityDpi
- }
- }
-
- override fun calculateDistanceToFinalSnap(
- layoutManager: LayoutManager,
- targetView: View
- ): IntArray {
- val out = IntArray(2)
- out[0] = distanceToMiddleView(layoutManager, targetView)
- return out
- }
-
- override fun calculateScrollDistance(velocityX: Int, velocityY: Int): IntArray {
- val out = IntArray(2)
- val layoutManager: LinearLayoutManager =
- (recyclerView?.layoutManager as? LinearLayoutManager) ?: return out
-
- if (maxScrollDistance == 0) {
- maxScrollDistance = (layoutManager.width) / 2
- }
-
- scroller?.fling(0, 0, velocityX, velocityY, -maxScrollDistance, maxScrollDistance, 0, 0)
- out[0] = scroller?.finalX ?: 0
- out[1] = scroller?.finalY ?: 0
- return out
- }
-
- private fun distanceToMiddleView(layoutManager: LayoutManager, targetView: View): Int {
- val middle = layoutManager.width / 2
- val targetMiddle = targetView.left + targetView.width / 2
- return targetMiddle - middle
- }
-
- private fun findMiddleView(layoutManager: LayoutManager?): View? {
- if (layoutManager == null) return null
-
- val childCount = layoutManager.childCount
- if (childCount == 0) return null
-
- var absClosest = Integer.MAX_VALUE
- var closestView: View? = null
- val middle = layoutManager.width / 2
-
- for (i in 0 until childCount) {
- val child = layoutManager.getChildAt(i) ?: continue
- val absDistanceToMiddle = abs((child.left + child.width / 2) - middle)
- if (absDistanceToMiddle < absClosest) {
- absClosest = absDistanceToMiddle
- closestView = child
- }
- }
- return closestView
- }
-}
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/FocusPointDrawable.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/FocusPointDrawable.kt
deleted file mode 100644
index 5a05ce4b..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/FocusPointDrawable.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions.ui
-
-import android.graphics.*
-import android.graphics.drawable.Drawable
-import kotlin.math.min
-
-class FocusPointDrawable : Drawable() {
- private val paint = Paint().apply {
- isAntiAlias = true
- style = Paint.Style.STROKE
- color = Color.WHITE
- }
-
- private var radius: Float = 0f
- private var centerX: Float = 0f
- private var centerY: Float = 0f
-
- fun setStrokeWidth(strokeWidth: Float): Boolean =
- if (paint.strokeWidth == strokeWidth) {
- false
- } else {
- paint.strokeWidth = strokeWidth
- true
- }
-
- override fun onBoundsChange(bounds: Rect) {
- val width = bounds.width()
- val height = bounds.height()
- radius = min(width, height) / 2f - paint.strokeWidth / 2f
- centerX = width / 2f
- centerY = height / 2f
- }
-
- override fun draw(canvas: Canvas) {
- if (radius == 0f) return
-
- canvas.drawCircle(centerX, centerY, radius, paint)
- }
-
- override fun setAlpha(alpha: Int) {
- paint.alpha = alpha
- }
-
- override fun setColorFilter(colorFiter: ColorFilter?) {
- paint.colorFilter = colorFilter
- }
-
- @Deprecated("Deprecated in Java")
- override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
-}
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/OffsetCenterItemDecoration.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/OffsetCenterItemDecoration.kt
deleted file mode 100644
index a0774f22..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/OffsetCenterItemDecoration.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions.ui
-
-import android.graphics.Rect
-import android.view.View
-import androidx.core.view.ViewCompat
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-
-/**
- * An ItemDecoration used to center the first and last items within the RecyclerView.
- */
-class OffsetCenterItemDecoration : RecyclerView.ItemDecoration() {
- override fun getItemOffsets(
- outRect: Rect,
- view: View,
- parent: RecyclerView,
- state: RecyclerView.State
- ) {
- val layoutManager = parent.layoutManager as? LinearLayoutManager ?: return
- val position = layoutManager.getPosition(view)
- if (position == 0 || position == layoutManager.itemCount - 1) {
- measureChild(parent, view)
- val width = view.measuredWidth
- when (position) {
- 0 -> {
- outRect.left = (parent.width - width) / 2
- outRect.right = 0
- }
- layoutManager.itemCount - 1 -> {
- outRect.left = 0
- outRect.right = (parent.width - width) / 2
- }
- else -> {
- outRect.left = 0
- outRect.right = 0
- }
- }
- }
- }
-
- /**
- * Forces a measure if the view hasn't been measured yet.
- */
- private fun measureChild(parent: RecyclerView, child: View) {
- if (ViewCompat.isLaidOut(child)) return
- val layoutManager = parent.layoutManager as? LinearLayoutManager ?: return
- val lp = child.layoutParams
-
- val widthSpec = RecyclerView.LayoutManager.getChildMeasureSpec(
- layoutManager.width, layoutManager.widthMode,
- layoutManager.paddingLeft + layoutManager.paddingRight, lp.width,
- layoutManager.canScrollHorizontally()
- )
- val heightSpec = RecyclerView.LayoutManager.getChildMeasureSpec(
- layoutManager.height, layoutManager.heightMode,
- layoutManager.paddingTop + layoutManager.paddingBottom, lp.height,
- layoutManager.canScrollVertically()
- )
- child.measure(widthSpec, heightSpec)
- }
-}
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/ViewKtx.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/ViewKtx.kt
deleted file mode 100644
index 86b1e663..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/ui/ViewKtx.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions.ui
-
-import android.view.View
-import android.view.ViewTreeObserver.OnGlobalLayoutListener
-import androidx.core.view.ViewCompat
-
-/**
- * Apply the action when this view is attached to the window and has been measured.
- * If the view is already attached and measured then the action is immediately invoked.
- *
- * @param action The action to apply when the view is laid out
- */
-fun View.doOnLaidOut(action: () -> Unit) {
- if (isAttachedToWindow && ViewCompat.isLaidOut(this)) {
- action()
- } else {
- viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
- override fun onGlobalLayout() {
- viewTreeObserver.removeOnGlobalLayoutListener(this)
- action()
- }
- })
- }
-}
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewmodel/CameraExtensionsViewModel.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewmodel/CameraExtensionsViewModel.kt
deleted file mode 100644
index 5bc63312..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewmodel/CameraExtensionsViewModel.kt
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions.viewmodel
-
-import android.app.Application
-import android.graphics.Bitmap
-import android.util.Log
-import androidx.camera.core.AspectRatio
-import androidx.camera.core.Camera
-import androidx.camera.core.CameraSelector
-import androidx.camera.core.CameraSelector.LensFacing
-import androidx.camera.core.FocusMeteringAction
-import androidx.camera.core.ImageCapture
-import androidx.camera.core.ImageCaptureException
-import androidx.camera.core.ImageCaptureLatencyEstimate
-import androidx.camera.core.MeteringPoint
-import androidx.camera.core.Preview
-import androidx.camera.core.UseCaseGroup
-import androidx.camera.extensions.ExtensionMode
-import androidx.camera.extensions.ExtensionsManager
-import androidx.camera.lifecycle.ProcessCameraProvider
-import androidx.camera.view.PreviewView
-import androidx.core.net.toUri
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import com.example.android.cameraxextensions.model.CameraState
-import com.example.android.cameraxextensions.model.CameraUiState
-import com.example.android.cameraxextensions.model.CaptureState
-import com.example.android.cameraxextensions.repository.ImageCaptureRepository
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.asExecutor
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.guava.await
-import kotlinx.coroutines.isActive
-import kotlinx.coroutines.launch
-import androidx.lifecycle.asFlow
-import kotlinx.coroutines.CoroutineScope
-
-/**
- * View model for camera extensions. This manages all the operations on the camera.
- * This includes opening and closing the camera, showing the camera preview, capturing a photo,
- * checking which extensions are available, and selecting an extension.
- *
- * Camera UI state is communicated via the cameraUiState flow.
- * Capture UI state is communicated via the captureUiState flow.
- *
- * Rebinding to the UI state flows will always emit the last UI state.
- */
-class CameraExtensionsViewModel(
- private val application: Application,
- private val imageCaptureRepository: ImageCaptureRepository
-) : ViewModel() {
- private companion object {
- const val TAG = "CameraExtensionsViewModel"
- const val REALTIME_LATENCY_UPDATE_INTERVAL_MILLIS = 1000L
- }
-
- private lateinit var cameraProvider: ProcessCameraProvider
- private lateinit var extensionsManager: ExtensionsManager
-
- private var camera: Camera? = null
-
- private var imageCapture = ImageCapture.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- .build()
- private var realtimeLatencyEstimateJob: Job? = null
-
- private val preview = Preview.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- .build()
-
- private val _cameraUiState: MutableStateFlow = MutableStateFlow(CameraUiState())
- private val _captureUiState: MutableStateFlow =
- MutableStateFlow(CaptureState.CaptureNotReady)
-
- val cameraUiState: Flow = _cameraUiState
- val captureUiState: Flow = _captureUiState
-
- /**
- * Initializes the camera and checks which extensions are available for the selected camera lens
- * face. If no extensions are available then the selected extension will be set to None and the
- * available extensions list will also contain None.
- * Because this operation is async, clients should wait for cameraUiState to emit
- * CameraState.READY. Once the camera is ready the client can start the preview.
- */
- fun initializeCamera() {
- viewModelScope.launch {
- val currentCameraUiState = _cameraUiState.value
-
- // get the camera selector for the select lens face
- val cameraSelector = cameraLensToSelector(currentCameraUiState.cameraLens)
-
- // wait for the camera provider instance and extensions manager instance
- cameraProvider = ProcessCameraProvider.getInstance(application).await()
- extensionsManager =
- ExtensionsManager.getInstanceAsync(application, cameraProvider).await()
-
- val availableCameraLens =
- listOf(
- CameraSelector.LENS_FACING_BACK,
- CameraSelector.LENS_FACING_FRONT
- ).filter { lensFacing ->
- cameraProvider.hasCamera(cameraLensToSelector(lensFacing))
- }
-
- // get the supported extensions for the selected camera lens by filtering the full list
- // of extensions and checking each one if it's available
- val availableExtensions = listOf(
- ExtensionMode.AUTO,
- ExtensionMode.BOKEH,
- ExtensionMode.HDR,
- ExtensionMode.NIGHT,
- ExtensionMode.FACE_RETOUCH
- ).filter { extensionMode ->
- extensionsManager.isExtensionAvailable(cameraSelector, extensionMode)
- }
-
- // prepare the new camera UI state which is now in the READY state and contains the list
- // of available extensions, available lens faces.
- val newCameraUiState = currentCameraUiState.copy(
- cameraState = CameraState.READY,
- availableExtensions = listOf(ExtensionMode.NONE) + availableExtensions,
- availableCameraLens = availableCameraLens,
- extensionMode = if (availableExtensions.isEmpty()) ExtensionMode.NONE else currentCameraUiState.extensionMode,
- realtimeCaptureLatencyEstimate = ImageCaptureLatencyEstimate.UNDEFINED_IMAGE_CAPTURE_LATENCY,
- )
- _cameraUiState.emit(newCameraUiState)
- }
- }
-
- /**
- * Starts the preview stream. The camera state should be in the READY or PREVIEW_STOPPED state
- * when calling this operation.
- * This process will bind the preview and image capture uses cases to the camera provider.
- */
- fun startPreview(
- lifecycleOwner: LifecycleOwner,
- previewView: PreviewView
- ) {
- realtimeLatencyEstimateJob?.cancel()
-
- val currentCameraUiState = _cameraUiState.value
- val cameraSelector = if (currentCameraUiState.extensionMode == ExtensionMode.NONE) {
- cameraLensToSelector(currentCameraUiState.cameraLens)
- } else {
- extensionsManager.getExtensionEnabledCameraSelector(
- cameraLensToSelector(currentCameraUiState.cameraLens),
- currentCameraUiState.extensionMode
- )
- }
-
- cameraProvider.unbindAll()
- camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector)
-
- camera?.cameraInfo?.let {
- val isPostviewSupported =
- ImageCapture.getImageCaptureCapabilities(it).isPostviewSupported
- imageCapture = ImageCapture.Builder()
- .setTargetAspectRatio(AspectRatio.RATIO_16_9)
- .setPostviewEnabled(isPostviewSupported)
- .build()
- }
-
- val useCaseGroup = UseCaseGroup.Builder()
- .setViewPort(previewView.viewPort!!)
- .addUseCase(imageCapture)
- .addUseCase(preview)
- .build()
- cameraProvider.unbindAll()
- camera = cameraProvider.bindToLifecycle(
- lifecycleOwner,
- cameraSelector,
- useCaseGroup
- )
-
- preview.surfaceProvider = previewView.surfaceProvider
-
- viewModelScope.launch {
- _cameraUiState.emit(_cameraUiState.value.copy(cameraState = CameraState.READY))
- _captureUiState.emit(CaptureState.CaptureReady)
- previewView.previewStreamState.asFlow().collect { previewStreamState ->
- when (previewStreamState) {
- PreviewView.StreamState.IDLE -> {
- realtimeLatencyEstimateJob?.cancel()
- realtimeLatencyEstimateJob = null
- }
- PreviewView.StreamState.STREAMING -> {
- if (realtimeLatencyEstimateJob == null) {
- realtimeLatencyEstimateJob = launch {
- observeRealtimeLatencyEstimate()
- }
- }
- }
- }
- }
- }
- }
-
- private suspend fun CoroutineScope.observeRealtimeLatencyEstimate() {
- Log.d(TAG, "Starting realtime latency estimate job")
-
- val currentCameraUiState = _cameraUiState.value
- val isSupported =
- currentCameraUiState.extensionMode != ExtensionMode.NONE
- && imageCapture.realtimeCaptureLatencyEstimate != ImageCaptureLatencyEstimate.UNDEFINED_IMAGE_CAPTURE_LATENCY
-
- if (!isSupported) {
- Log.d(TAG, "Starting realtime latency estimate job: no extension mode or not supported")
- _cameraUiState.emit(
- _cameraUiState.value.copy(
- cameraState = CameraState.PREVIEW_ACTIVE,
- realtimeCaptureLatencyEstimate = ImageCaptureLatencyEstimate.UNDEFINED_IMAGE_CAPTURE_LATENCY
- )
- )
- return
- }
-
- while (isActive) {
- updateRealtimeCaptureLatencyEstimate()
- delay(REALTIME_LATENCY_UPDATE_INTERVAL_MILLIS)
- }
- }
-
- /**
- * Stops the preview stream. This should be invoked when the captured image is displayed.
- */
- fun stopPreview() {
- realtimeLatencyEstimateJob?.cancel()
- preview.surfaceProvider = null
- viewModelScope.launch {
- _cameraUiState.emit(_cameraUiState.value.copy(
- cameraState = CameraState.PREVIEW_STOPPED,
- realtimeCaptureLatencyEstimate = ImageCaptureLatencyEstimate.UNDEFINED_IMAGE_CAPTURE_LATENCY
- ))
- }
- }
-
- /**
- * Toggle the camera lens face. This has no effect if there is only one available camera lens.
- */
- fun switchCamera() {
- realtimeLatencyEstimateJob?.cancel()
- val currentCameraUiState = _cameraUiState.value
- if (currentCameraUiState.cameraState == CameraState.READY || currentCameraUiState.cameraState == CameraState.PREVIEW_ACTIVE) {
- // To switch the camera lens, there has to be at least 2 camera lenses
- if (currentCameraUiState.availableCameraLens.size == 1) return
-
- val camLensFacing = currentCameraUiState.cameraLens
- // Toggle the lens facing
- val newCameraUiState = if (camLensFacing == CameraSelector.LENS_FACING_BACK) {
- currentCameraUiState.copy(cameraLens = CameraSelector.LENS_FACING_FRONT)
- } else {
- currentCameraUiState.copy(cameraLens = CameraSelector.LENS_FACING_BACK)
- }
-
- viewModelScope.launch {
- _cameraUiState.emit(
- newCameraUiState.copy(
- cameraState = CameraState.NOT_READY,
- )
- )
- _captureUiState.emit(CaptureState.CaptureNotReady)
- }
- }
- }
-
- /**
- * Captures the photo and saves it to the pictures directory that's inside the app-specific
- * directory on external storage.
- * Upon successful capture, the captureUiState flow will emit CaptureFinished with the URI to
- * the captured photo.
- * If the capture operation failed then captureUiState flow will emit CaptureFailed with the
- * exception containing more details on the reason for failure.
- */
- fun capturePhoto() {
- realtimeLatencyEstimateJob?.cancel()
- viewModelScope.launch {
- _captureUiState.emit(CaptureState.CaptureStarted)
- }
- val photoFile = imageCaptureRepository.createImageOutputFile()
- val metadata = ImageCapture.Metadata().apply {
- // Mirror image when using the front camera
- isReversedHorizontal =
- _cameraUiState.value.cameraLens == CameraSelector.LENS_FACING_FRONT
- }
- val outputFileOptions =
- ImageCapture.OutputFileOptions.Builder(photoFile)
- .setMetadata(metadata)
- .build()
-
- camera?.cameraInfo?.let {
- if (ImageCapture.getImageCaptureCapabilities(it).isCaptureProcessProgressSupported) {
- viewModelScope.launch {
- _captureUiState.emit(CaptureState.CaptureProcessProgress(0))
- }
- }
- }
-
- imageCapture.takePicture(
- outputFileOptions,
- Dispatchers.Default.asExecutor(),
- object : ImageCapture.OnImageSavedCallback {
- override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
- imageCaptureRepository.notifyImageCreated(
- application,
- outputFileResults.savedUri ?: photoFile.toUri()
- )
- val isProcessProgressSupported = camera?.cameraInfo?.let {
- ImageCapture.getImageCaptureCapabilities(it).isCaptureProcessProgressSupported
- } ?: false
- viewModelScope.launch {
- if (isProcessProgressSupported) {
- _captureUiState.emit(CaptureState.CaptureProcessProgress(100))
- }
- _captureUiState.emit(
- CaptureState.CaptureFinished(
- outputFileResults,
- isProcessProgressSupported
- )
- )
- }
- }
-
- override fun onError(exception: ImageCaptureException) {
- viewModelScope.launch {
- _captureUiState.emit(CaptureState.CaptureFailed(exception))
- }
- }
-
- override fun onCaptureProcessProgressed(progress: Int) {
- viewModelScope.launch {
- _captureUiState.emit(CaptureState.CaptureProcessProgress(progress))
- }
- }
-
- override fun onPostviewBitmapAvailable(bitmap: Bitmap) {
- viewModelScope.launch {
- _captureUiState.emit(CaptureState.CapturePostview(bitmap))
- }
- }
- })
- }
-
- /**
- * Sets the current extension mode. This will force the camera to rebind the use cases.
- */
- fun setExtensionMode(@ExtensionMode.Mode extensionMode: Int) {
- viewModelScope.launch {
- _cameraUiState.emit(
- _cameraUiState.value.copy(
- cameraState = CameraState.NOT_READY,
- extensionMode = extensionMode,
- realtimeCaptureLatencyEstimate = ImageCaptureLatencyEstimate.UNDEFINED_IMAGE_CAPTURE_LATENCY,
- )
- )
- _captureUiState.emit(CaptureState.CaptureNotReady)
- }
- }
-
- fun focus(meteringPoint: MeteringPoint) {
- val camera = camera ?: return
-
- val meteringAction = FocusMeteringAction.Builder(meteringPoint).build()
- camera.cameraControl.startFocusAndMetering(meteringAction)
- }
-
- fun scale(scaleFactor: Float) {
- val camera = camera ?: return
- val currentZoomRatio: Float = camera.cameraInfo.zoomState.value?.zoomRatio ?: 1f
- camera.cameraControl.setZoomRatio(scaleFactor * currentZoomRatio)
- }
-
- private fun cameraLensToSelector(@LensFacing lensFacing: Int): CameraSelector =
- when (lensFacing) {
- CameraSelector.LENS_FACING_FRONT -> CameraSelector.DEFAULT_FRONT_CAMERA
- CameraSelector.LENS_FACING_BACK -> CameraSelector.DEFAULT_BACK_CAMERA
- else -> throw IllegalArgumentException("Invalid lens facing type: $lensFacing")
- }
-
- private suspend fun updateRealtimeCaptureLatencyEstimate() {
- val estimate = imageCapture.realtimeCaptureLatencyEstimate
- Log.d(TAG, "Realtime capture latency estimate: $estimate")
- if (estimate == ImageCaptureLatencyEstimate.UNDEFINED_IMAGE_CAPTURE_LATENCY) {
- return
- }
- _cameraUiState.emit(
- _cameraUiState.value.copy(
- cameraState = CameraState.PREVIEW_ACTIVE,
- realtimeCaptureLatencyEstimate = estimate
- )
- )
- }
-}
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewmodel/CameraExtensionsViewModelFactory.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewmodel/CameraExtensionsViewModelFactory.kt
deleted file mode 100644
index 3981c679..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewmodel/CameraExtensionsViewModelFactory.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions.viewmodel
-
-import android.app.Application
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-import com.example.android.cameraxextensions.repository.ImageCaptureRepository
-
-/**
- * Creates ViewModel instances of [CameraExtensionsViewModel] to support injection of [Application]
- * and [ImageCaptureRepository]
- */
-class CameraExtensionsViewModelFactory(
- private val application: Application,
- private val imageCaptureRepository: ImageCaptureRepository
-) : ViewModelProvider.Factory {
-
- override fun create(modelClass: Class): T {
- return CameraExtensionsViewModel(application, imageCaptureRepository) as T
- }
-}
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewstate/CameraPreviewScreenViewState.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewstate/CameraPreviewScreenViewState.kt
deleted file mode 100644
index 9055caa7..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewstate/CameraPreviewScreenViewState.kt
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions.viewstate
-
-import android.graphics.Bitmap
-import com.example.android.cameraxextensions.adapter.CameraExtensionItem
-
-/**
- * Represents the camera preview screen view state. The camera preview screen shows camera controls
- * and the camera preview.
- */
-data class CameraPreviewScreenViewState(
- val shutterButtonViewState: ShutterButtonViewState = ShutterButtonViewState(),
- val switchLensButtonViewState: SwitchLensButtonViewState = SwitchLensButtonViewState(),
- val extensionsSelectorViewState: CameraExtensionSelectorViewState = CameraExtensionSelectorViewState(),
- val processProgressViewState: ProcessProgressIndicatorViewState = ProcessProgressIndicatorViewState(),
- val postviewViewState: PostviewViewState = PostviewViewState(),
- val latencyEstimateIndicatorViewState: LatencyEstimateIndicatorViewState = LatencyEstimateIndicatorViewState()
-) {
- fun hideCameraControls(): CameraPreviewScreenViewState =
- copy(
- shutterButtonViewState = shutterButtonViewState.copy(isVisible = false),
- switchLensButtonViewState = switchLensButtonViewState.copy(isVisible = false),
- extensionsSelectorViewState = extensionsSelectorViewState.copy(isVisible = false)
- )
-
- fun showCameraControls(): CameraPreviewScreenViewState =
- copy(
- shutterButtonViewState = shutterButtonViewState.copy(isVisible = true),
- switchLensButtonViewState = switchLensButtonViewState.copy(isVisible = true),
- extensionsSelectorViewState = extensionsSelectorViewState.copy(isVisible = true)
- )
-
- fun enableCameraShutter(isEnabled: Boolean): CameraPreviewScreenViewState =
- copy(shutterButtonViewState = shutterButtonViewState.copy(isEnabled = isEnabled))
-
- fun enableSwitchLens(isEnabled: Boolean): CameraPreviewScreenViewState =
- copy(switchLensButtonViewState = switchLensButtonViewState.copy(isEnabled = isEnabled))
-
- fun setAvailableExtensions(extensions: List): CameraPreviewScreenViewState =
- copy(extensionsSelectorViewState = extensionsSelectorViewState.copy(extensions = extensions))
-
- fun showPostview(bitmap: Bitmap): CameraPreviewScreenViewState =
- copy(postviewViewState = PostviewViewState(isVisible = true, bitmap = bitmap))
-
- fun hidePostview(): CameraPreviewScreenViewState = copy(postviewViewState = PostviewViewState())
-
- fun showProcessProgressViewState(progress: Int): CameraPreviewScreenViewState =
- copy(processProgressViewState = ProcessProgressIndicatorViewState(isVisible = true, progress = progress))
-
- fun hideProcessProgressViewState(): CameraPreviewScreenViewState =
- copy(processProgressViewState = ProcessProgressIndicatorViewState())
-
- fun showLatencyEstimateIndicator(latencyEstimateMillis: Long): CameraPreviewScreenViewState =
- copy(
- latencyEstimateIndicatorViewState = LatencyEstimateIndicatorViewState(
- isVisible = true,
- latencyEstimateMillis = latencyEstimateMillis
- )
- )
-
- fun hideLatencyEstimateIndicator(): CameraPreviewScreenViewState =
- copy(
- latencyEstimateIndicatorViewState = LatencyEstimateIndicatorViewState(
- isVisible = false,
- latencyEstimateMillis = 0
- )
- )
-}
-
-data class CameraExtensionSelectorViewState(
- val isVisible: Boolean = false,
- val extensions: List = emptyList()
-)
-
-data class ShutterButtonViewState(
- val isVisible: Boolean = false,
- val isEnabled: Boolean = false
-)
-
-data class SwitchLensButtonViewState(
- val isVisible: Boolean = false,
- val isEnabled: Boolean = false
-)
-
-data class ProcessProgressIndicatorViewState(
- val isVisible: Boolean = false,
- val progress: Int = 0
-)
-
-data class PostviewViewState(
- val isVisible: Boolean = false,
- val bitmap: Bitmap? = null
-)
-
-data class LatencyEstimateIndicatorViewState(
- val isVisible: Boolean = false,
- val latencyEstimateMillis: Long = 0
-)
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewstate/CaptureScreenViewState.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewstate/CaptureScreenViewState.kt
deleted file mode 100644
index 7f4d6520..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewstate/CaptureScreenViewState.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions.viewstate
-
-/**
- * Capture Screen is the top level view state. A capture screen contains a camera preview screen
- * and a post capture screen.
- */
-data class CaptureScreenViewState(
- val cameraPreviewScreenViewState: CameraPreviewScreenViewState = CameraPreviewScreenViewState(),
- val postCaptureScreenViewState: PostCaptureScreenViewState = PostCaptureScreenViewState.PostCaptureScreenHiddenViewState
-) {
- fun updateCameraScreen(block: (cameraPreviewScreenViewState: CameraPreviewScreenViewState) -> CameraPreviewScreenViewState): CaptureScreenViewState =
- copy(cameraPreviewScreenViewState = block(cameraPreviewScreenViewState))
-
- fun updatePostCaptureScreen(block: (postCaptureScreenViewState: PostCaptureScreenViewState) -> PostCaptureScreenViewState) =
- copy(postCaptureScreenViewState = block(postCaptureScreenViewState))
-}
diff --git a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewstate/PostCaptureScreenViewState.kt b/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewstate/PostCaptureScreenViewState.kt
deleted file mode 100644
index ca6e1bd8..00000000
--- a/CameraXExtensions/app/src/main/java/com/example/android/cameraxextensions/viewstate/PostCaptureScreenViewState.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions.viewstate
-
-import android.net.Uri
-
-/**
- * Represents the post capture screen view state. This can be either visible with a uri for the
- * photo captured or hidden.
- */
-sealed interface PostCaptureScreenViewState {
- object PostCaptureScreenHiddenViewState : PostCaptureScreenViewState
-
- data class PostCaptureScreenVisibleViewState(val uri: Uri) : PostCaptureScreenViewState
-}
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/res/color/button.xml b/CameraXExtensions/app/src/main/res/color/button.xml
deleted file mode 100644
index aed14320..00000000
--- a/CameraXExtensions/app/src/main/res/color/button.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/CameraXExtensions/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index c5b73e79..00000000
--- a/CameraXExtensions/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/res/drawable/circle_background.xml b/CameraXExtensions/app/src/main/res/drawable/circle_background.xml
deleted file mode 100644
index 774c2eb3..00000000
--- a/CameraXExtensions/app/src/main/res/drawable/circle_background.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/res/drawable/ic_camera_shutter.xml b/CameraXExtensions/app/src/main/res/drawable/ic_camera_shutter.xml
deleted file mode 100644
index 395cb5a4..00000000
--- a/CameraXExtensions/app/src/main/res/drawable/ic_camera_shutter.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/CameraXExtensions/app/src/main/res/drawable/ic_close.xml b/CameraXExtensions/app/src/main/res/drawable/ic_close.xml
deleted file mode 100644
index 4e8f8b75..00000000
--- a/CameraXExtensions/app/src/main/res/drawable/ic_close.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/CameraXExtensions/app/src/main/res/drawable/ic_flip_camera_android.xml b/CameraXExtensions/app/src/main/res/drawable/ic_flip_camera_android.xml
deleted file mode 100644
index cd65003c..00000000
--- a/CameraXExtensions/app/src/main/res/drawable/ic_flip_camera_android.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/CameraXExtensions/app/src/main/res/drawable/ic_launcher_background.xml b/CameraXExtensions/app/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 97145c24..00000000
--- a/CameraXExtensions/app/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,186 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/CameraXExtensions/app/src/main/res/drawable/pill_selected_background.xml b/CameraXExtensions/app/src/main/res/drawable/pill_selected_background.xml
deleted file mode 100644
index 2b283fa6..00000000
--- a/CameraXExtensions/app/src/main/res/drawable/pill_selected_background.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/CameraXExtensions/app/src/main/res/drawable/pill_unselected_background.xml b/CameraXExtensions/app/src/main/res/drawable/pill_unselected_background.xml
deleted file mode 100644
index 8facd61c..00000000
--- a/CameraXExtensions/app/src/main/res/drawable/pill_unselected_background.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/CameraXExtensions/app/src/main/res/drawable/selector_background.xml b/CameraXExtensions/app/src/main/res/drawable/selector_background.xml
deleted file mode 100644
index 7325ae18..00000000
--- a/CameraXExtensions/app/src/main/res/drawable/selector_background.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/CameraXExtensions/app/src/main/res/layout/activity_main.xml b/CameraXExtensions/app/src/main/res/layout/activity_main.xml
deleted file mode 100644
index 12ffc03d..00000000
--- a/CameraXExtensions/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,196 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/res/layout/view_extension_type.xml b/CameraXExtensions/app/src/main/res/layout/view_extension_type.xml
deleted file mode 100644
index e5e3199c..00000000
--- a/CameraXExtensions/app/src/main/res/layout/view_extension_type.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/CameraXExtensions/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 3fa11007..00000000
--- a/CameraXExtensions/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/CameraXExtensions/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 3fa11007..00000000
--- a/CameraXExtensions/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/CameraXExtensions/app/src/main/res/mipmap-hdpi/ic_launcher.webp
deleted file mode 100644
index c209e78e..00000000
Binary files a/CameraXExtensions/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ
diff --git a/CameraXExtensions/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/CameraXExtensions/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
deleted file mode 100644
index b2dfe3d1..00000000
Binary files a/CameraXExtensions/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/CameraXExtensions/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/CameraXExtensions/app/src/main/res/mipmap-mdpi/ic_launcher.webp
deleted file mode 100644
index 4f0f1d64..00000000
Binary files a/CameraXExtensions/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ
diff --git a/CameraXExtensions/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/CameraXExtensions/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
deleted file mode 100644
index 62b611da..00000000
Binary files a/CameraXExtensions/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/CameraXExtensions/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/CameraXExtensions/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
deleted file mode 100644
index 948a3070..00000000
Binary files a/CameraXExtensions/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ
diff --git a/CameraXExtensions/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/CameraXExtensions/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
deleted file mode 100644
index 1b9a6956..00000000
Binary files a/CameraXExtensions/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/CameraXExtensions/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/CameraXExtensions/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
deleted file mode 100644
index 28d4b77f..00000000
Binary files a/CameraXExtensions/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ
diff --git a/CameraXExtensions/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/CameraXExtensions/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
deleted file mode 100644
index 9287f508..00000000
Binary files a/CameraXExtensions/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/CameraXExtensions/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/CameraXExtensions/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
deleted file mode 100644
index aa7d6427..00000000
Binary files a/CameraXExtensions/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ
diff --git a/CameraXExtensions/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/CameraXExtensions/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
deleted file mode 100644
index 9126ae37..00000000
Binary files a/CameraXExtensions/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/CameraXExtensions/app/src/main/res/values-night/themes.xml b/CameraXExtensions/app/src/main/res/values-night/themes.xml
deleted file mode 100644
index ab21e341..00000000
--- a/CameraXExtensions/app/src/main/res/values-night/themes.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/res/values/colors.xml b/CameraXExtensions/app/src/main/res/values/colors.xml
deleted file mode 100644
index 309afe03..00000000
--- a/CameraXExtensions/app/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
- #FFBB86FC
- #FF6200EE
- #FF3700B3
- #FF03DAC5
- #FF018786
- #FF000000
- #FFFFFFFF
-
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/res/values/strings.xml b/CameraXExtensions/app/src/main/res/values/strings.xml
deleted file mode 100644
index de803f5a..00000000
--- a/CameraXExtensions/app/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
- CameraX Extensions
-
- None
- Auto
- Bokeh
- HDR
- Night
- Face Retouch
-
- You can\'t use camera extensions unless CameraX Extensions has access to your camera.
- Allow CameraX Extensions access to your camera to try camera extensions.
- Turn On
-
- Hold Still
- %ds
-
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/res/values/themes.xml b/CameraXExtensions/app/src/main/res/values/themes.xml
deleted file mode 100644
index 73b832c4..00000000
--- a/CameraXExtensions/app/src/main/res/values/themes.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/res/xml/backup_rules.xml b/CameraXExtensions/app/src/main/res/xml/backup_rules.xml
deleted file mode 100644
index c2dae00c..00000000
--- a/CameraXExtensions/app/src/main/res/xml/backup_rules.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/res/xml/data_extraction_rules.xml b/CameraXExtensions/app/src/main/res/xml/data_extraction_rules.xml
deleted file mode 100644
index eb1b0d72..00000000
--- a/CameraXExtensions/app/src/main/res/xml/data_extraction_rules.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/main/res/xml/file_paths.xml b/CameraXExtensions/app/src/main/res/xml/file_paths.xml
deleted file mode 100644
index 13640ba1..00000000
--- a/CameraXExtensions/app/src/main/res/xml/file_paths.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXExtensions/app/src/test/java/com/example/android/cameraxextensions/ExampleUnitTest.kt b/CameraXExtensions/app/src/test/java/com/example/android/cameraxextensions/ExampleUnitTest.kt
deleted file mode 100644
index 1ddeb375..00000000
--- a/CameraXExtensions/app/src/test/java/com/example/android/cameraxextensions/ExampleUnitTest.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.android.cameraxextensions
-
-import org.junit.Test
-
-import org.junit.Assert.*
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
\ No newline at end of file
diff --git a/CameraXExtensions/build.gradle b/CameraXExtensions/build.gradle
deleted file mode 100644
index 79dac633..00000000
--- a/CameraXExtensions/build.gradle
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.
- */
-
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
-buildscript {
- // Top-level variables used for versioning
- ext.kotlin_version = '1.9.22'
- ext.java_version = JavaVersion.VERSION_11
-
- ext.camerax_version = '1.4.1'
- ext.coroutines_version = '1.8.1'
- ext.lifecycle_version = '2.8.0'
-
- repositories {
- google()
- mavenCentral()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:8.7.3'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
-
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
- }
-}
-
-allprojects {
- repositories {
- google()
- mavenCentral()
- }
-}
-
-task clean(type: Delete) {
- delete rootProject.buildDir
-}
diff --git a/CameraXExtensions/gradle.properties b/CameraXExtensions/gradle.properties
deleted file mode 100644
index fad33040..00000000
--- a/CameraXExtensions/gradle.properties
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# Copyright 2022 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
-#
-# https://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.
-#
-
-# Project-wide Gradle settings.
-# IDE (e.g. Android Studio) users:
-# Gradle settings configured through the IDE *will override*
-# any settings specified in this file.
-# For more details on how to configure your build environment visit
-# http://www.gradle.org/docs/current/userguide/build_environment.html
-# Specifies the JVM arguments used for the daemon process.
-# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
-# AndroidX package structure to make it clearer which packages are bundled with the
-# Android operating system, and which are packaged with your app's APK
-# https://developer.android.com/topic/libraries/support-library/androidx-rn
-android.useAndroidX=true
-# Kotlin code style for this project: "official" or "obsolete":
-kotlin.code.style=official
-# Enables namespacing of each library's R class so that its R class includes only the
-# resources declared in the library itself and none from the library's dependencies,
-# thereby reducing the size of the R class for that library
-android.nonTransitiveRClass=true
-android.defaults.buildfeatures.buildconfig=true
-android.nonFinalResIds=false
\ No newline at end of file
diff --git a/CameraXExtensions/gradle/wrapper/gradle-wrapper.jar b/CameraXExtensions/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index e708b1c0..00000000
Binary files a/CameraXExtensions/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/CameraXExtensions/gradle/wrapper/gradle-wrapper.properties b/CameraXExtensions/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 39bbb124..00000000
--- a/CameraXExtensions/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# Copyright 2022 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
-#
-# https://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.
-#
-
-#Wed Aug 03 16:12:29 EDT 2022
-distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip
-distributionPath=wrapper/dists
-zipStorePath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
diff --git a/CameraXExtensions/gradlew b/CameraXExtensions/gradlew
deleted file mode 100755
index 4f906e0c..00000000
--- a/CameraXExtensions/gradlew
+++ /dev/null
@@ -1,185 +0,0 @@
-#!/usr/bin/env sh
-
-#
-# Copyright 2015 the original author or authors.
-#
-# 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
-#
-# https://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.
-#
-
-##############################################################################
-##
-## Gradle start up script for UN*X
-##
-##############################################################################
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn () {
- echo "$*"
-}
-
-die () {
- echo
- echo "$*"
- echo
- exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
- else
- JAVACMD="$JAVA_HOME/bin/java"
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin or MSYS, switch paths to Windows format before running java
-if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
- fi
- i=`expr $i + 1`
- done
- case $i in
- 0) set -- ;;
- 1) set -- "$args0" ;;
- 2) set -- "$args0" "$args1" ;;
- 3) set -- "$args0" "$args1" "$args2" ;;
- 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
-fi
-
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=`save "$@"`
-
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-
-exec "$JAVACMD" "$@"
diff --git a/CameraXExtensions/gradlew.bat b/CameraXExtensions/gradlew.bat
deleted file mode 100644
index ac1b06f9..00000000
--- a/CameraXExtensions/gradlew.bat
+++ /dev/null
@@ -1,89 +0,0 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/CameraXExtensions/screenshots/camerax_camera_extensions_screenshot.png b/CameraXExtensions/screenshots/camerax_camera_extensions_screenshot.png
deleted file mode 100644
index 6401e91a..00000000
Binary files a/CameraXExtensions/screenshots/camerax_camera_extensions_screenshot.png and /dev/null differ
diff --git a/CameraXExtensions/screenshots/camerax_extensions_architecture_diagram.png b/CameraXExtensions/screenshots/camerax_extensions_architecture_diagram.png
deleted file mode 100644
index c05de694..00000000
Binary files a/CameraXExtensions/screenshots/camerax_extensions_architecture_diagram.png and /dev/null differ
diff --git a/CameraXExtensions/settings.gradle b/CameraXExtensions/settings.gradle
deleted file mode 100644
index 778b52c4..00000000
--- a/CameraXExtensions/settings.gradle
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright 2022 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
- *
- * https://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.
- */
-
-rootProject.name = "CameraX Extensions"
-include ':app'
diff --git a/CameraXVideo/.google/packaging.yaml b/CameraXVideo/.google/packaging.yaml
deleted file mode 100644
index 6093fa09..00000000
--- a/CameraXVideo/.google/packaging.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-# GOOGLE SAMPLE PACKAGING DATA
-#
-# This file is used by Google as part of our samples packaging process.
-# End users may safely ignore this file. It has no relevance to other systems.
----
-status: PUBLISHED
-technologies: [Android]
-categories: [Camera]
-languages: [Kotlin]
-solutions: [Mobile]
-github: android/camera-samples
-level: INTERMEDIATE
-icon: screenshots/icon-web.png
-apiRefs:
- - androidx.camera:camera-video
-license: apache2
diff --git a/CameraXVideo/README.md b/CameraXVideo/README.md
deleted file mode 100644
index 929017a4..00000000
--- a/CameraXVideo/README.md
+++ /dev/null
@@ -1,70 +0,0 @@
-
-Android CameraXVideo Sample
-===========================
-
-This sample demonstrates CameraX Video Capture (Recorder) API with Capture + Preview use case combination.
-VideoCapture is in early release cycle, try it out and share your
-thoughts and experience; especially for the functionalities you need but unavailable,
-please let us know [hear](https://groups.google.com/a/android.com/g/camerax-developers) them.
-
-Introduction
-------------
-CameraX VideoCapture is available from [version 1.1.0-alpha10+](https://developer.android.com/jetpack/androidx/releases/camera), can capture to local file with the formats of:
-- [MediaStore](https://developer.android.com/reference/android/provider/MediaStore)
-- [File](https://developer.android.com/reference/java/io/File)
-- [FileDescriptor](https://developer.android.com/reference/java/io/FileDescriptor)
-
-This sample demonstrates MediaStore capture case, the other 2 types are very similar
-(just replacing the MediaStoreOutputOptions with FileOutputOptions or
-FileDescriptorOutputOptions in CameraFragment.kt).
-
-Conceptually 3 steps to use video recorder in this Alpha version: create a recorder(use it to create recordings),
-bind it to pipeline, record with recording (created from recorder). A little more detailed steps:
-1. Create a QualitySelector(video resolution) -> Recorder -> VideoCapture
-2. Bind the VideoCapture to the CameraLifeCycle (with a CameraSelector) (Camera needs some time to settle)
-3. further configure the Recorder:
-- set OutputOptions(MediaStoreOutputOptions, FileDescriptorOutputOptions, FileOutputOptions): location and size. This step returns PendingRecording object
-- enable audio, register VideoRecordEvent with the PendingRecording object
-4. start() capture, returning ActiveRecording object
-5. pause(), resume(), stop()/close() with the ActiveRecording
-6. react to capture events: app gets notifications from the registered VideoRecordEvent listener for recording state changes
-
-The steps are not strict, but some do have dependencies (like Recorder created then bind);
-recordings could be created before or after binding, but activeRecording needs to be after binding.
-VideoCapture does not support multiple concurrent streams(capturing),so only one ActiveRecording could exists.
-
-Related code is in CameraFragment.kt (in its own fragment), and most of the code deals with UI, the essential
-VideoCapture code is quite simple and straightforward (in startVideoCapture() function )
-
-To learn more about the VideoCapture, refer to
-- [the official documentation](https://developer.android.com/training/camerax/video-capture)
-- [the latest API list](https://github.com/androidx/androidx/blob/androidx-main/camera/camera-video/api/current.txt)
-- [Camerax release notes](https://developer.android.com/jetpack/androidx/releases/camera)
-
-Pre-requisites
---------------
-- Android SDK 31+
-- Android Studio Arctic Fox (2020.3.1)
-- Device with video capture capability (or emulator)
-
-Getting Started
----------------
-This sample uses the Gradle build system. To build this project, use the
-"gradlew build" command or use "Import Project" in Android Studio.
-
-Screenshots
--------------
-
-
-
-
-Support
--------
-
-- Stack Overflow: http://stackoverflow.com/questions/tagged/android
-
-If you've found an error in this sample, please file an issue:
-https://github.com/android/camera-samples
-
-Patches are encouraged, and may be submitted by forking this project and
-submitting a pull request through GitHub. Please see CONTRIBUTING.md for more details.
diff --git a/CameraXVideo/app/build.gradle b/CameraXVideo/app/build.gradle
deleted file mode 100644
index 7ae7d631..00000000
--- a/CameraXVideo/app/build.gradle
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.
- */
-
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-kapt'
-apply plugin: "androidx.navigation.safeargs"
-
-android {
- compileSdkVersion 31
- defaultConfig {
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- applicationId "com.android.example.camerax.video"
- minSdkVersion 21
- targetSdkVersion 31
- versionCode 1
- versionName "1.0.0"
- }
-
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
-
- kotlinOptions {
- jvmTarget = JavaVersion.VERSION_1_8
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
-
- // Set the source of tests to same for both Unit and Instrumented tests
- sourceSets {
- String sharedTestDir = 'src/test/java'
- test {
- java.srcDir sharedTestDir
- }
- androidTest {
- java.srcDir sharedTestDir
- }
- }
-
- buildFeatures {
- viewBinding true
- }
-}
-
-dependencies {
- implementation project(path: ':utils')
-
- // Kotlin lang
- implementation 'androidx.core:core-ktx:1.7.0'
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.30"
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
- implementation "androidx.concurrent:concurrent-futures-ktx:1.1.0"
-
- // App compat and UI things
- implementation 'androidx.appcompat:appcompat:1.4.0'
- implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
- implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
-
- // Navigation library
- def nav_version = "2.4.0-rc01"
- implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
- implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
-
- implementation 'com.android.databinding:viewbinding:7.0.4'
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
- implementation 'androidx.activity:activity-ktx:1.4.0'
- implementation "androidx.fragment:fragment-ktx:1.4.0"
-
- // Unit testing
- testImplementation 'androidx.test.ext:junit:1.1.3'
- testImplementation 'androidx.test:rules:1.4.0'
- testImplementation 'androidx.test:runner:1.4.0'
- testImplementation 'androidx.test.espresso:espresso-core:3.4.0'
- testImplementation "org.robolectric:robolectric:4.3.1"
-
- // Instrumented testing
- androidTestImplementation 'androidx.test.ext:junit:1.1.3'
- androidTestImplementation 'androidx.test:rules:1.4.0'
- androidTestImplementation 'androidx.test:runner:1.4.0'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
-
- // CameraX dependencies (first release for video is: "1.1.0-alpha10")
- def camerax_version = "1.1.0-beta01"
- // The following line is optional, as the core library is included indirectly by camera-camera2
- implementation "androidx.camera:camera-core:${camerax_version}"
- implementation "androidx.camera:camera-camera2:${camerax_version}"
- implementation "androidx.camera:camera-lifecycle:${camerax_version}"
- implementation "androidx.camera:camera-video:${camerax_version}"
- implementation "androidx.camera:camera-view:${camerax_version}"
-}
diff --git a/CameraXVideo/app/src/main/AndroidManifest.xml b/CameraXVideo/app/src/main/AndroidManifest.xml
deleted file mode 100644
index 2fdfd692..00000000
--- a/CameraXVideo/app/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/CameraXVideo/app/src/main/java/com/example/android/camerax/video/MainActivity.kt b/CameraXVideo/app/src/main/java/com/example/android/camerax/video/MainActivity.kt
deleted file mode 100644
index c0056eb8..00000000
--- a/CameraXVideo/app/src/main/java/com/example/android/camerax/video/MainActivity.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2021 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
- *
- * https://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.android.camerax.video
-
-import android.annotation.SuppressLint
-import android.content.pm.ActivityInfo
-import android.os.Bundle
-import android.view.View
-import androidx.appcompat.app.AppCompatActivity
-import com.example.android.camerax.video.databinding.ActivityMainBinding
-
-class MainActivity : AppCompatActivity() {
-
- private lateinit var activityMainBinding: ActivityMainBinding
-
- @SuppressLint("SourceLockedOrientationActivity")
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
- setContentView(activityMainBinding.root)
-
- // Fix the screen orientation for this sample to focus on cameraX API
- // rather than UI
- requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
- }
-
- override fun onResume() {
- super.onResume()
- // Before setting full screen flags, we must wait a bit to let UI settle; otherwise, we may
- // be trying to set app to immersive mode before it's ready and the flags do not stick
- activityMainBinding.fragmentContainer.postDelayed({
- activityMainBinding.fragmentContainer.systemUiVisibility = FLAGS_FULLSCREEN
- }, IMMERSIVE_FLAG_TIMEOUT)
- }
-
- companion object {
- /** Combination of all flags required to put activity into immersive mode */
- const val FLAGS_FULLSCREEN =
- View.SYSTEM_UI_FLAG_LOW_PROFILE or
- View.SYSTEM_UI_FLAG_FULLSCREEN or
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
-
- /** Milliseconds used for UI animations */
- const val ANIMATION_FAST_MILLIS = 50L
- const val ANIMATION_SLOW_MILLIS = 100L
- const val IMMERSIVE_FLAG_TIMEOUT = 500L
- }
-}
diff --git a/CameraXVideo/app/src/main/java/com/example/android/camerax/video/MainApplication.kt b/CameraXVideo/app/src/main/java/com/example/android/camerax/video/MainApplication.kt
deleted file mode 100644
index 931d2d74..00000000
--- a/CameraXVideo/app/src/main/java/com/example/android/camerax/video/MainApplication.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2021 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
- *
- * https://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.android.camerax.video
-
-import android.app.Application
-import android.util.Log
-import androidx.camera.camera2.Camera2Config
-import androidx.camera.core.CameraXConfig
-
-/**
- * Set CameraX logging level to Log.ERROR to avoid excessive logcat messages.
- * Refer to https://developer.android.com/reference/androidx/camera/core/CameraXConfig.Builder#setMinimumLoggingLevel(int)
- * for details.
- */
-class MainApplication : Application(), CameraXConfig.Provider {
- override fun getCameraXConfig(): CameraXConfig {
- return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
- .setMinimumLoggingLevel(Log.ERROR).build()
- }
-}
\ No newline at end of file
diff --git a/CameraXVideo/app/src/main/java/com/example/android/camerax/video/extensions/VideoQualityExt.kt b/CameraXVideo/app/src/main/java/com/example/android/camerax/video/extensions/VideoQualityExt.kt
deleted file mode 100644
index f4d2e448..00000000
--- a/CameraXVideo/app/src/main/java/com/example/android/camerax/video/extensions/VideoQualityExt.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/**
- * Copyright 2021 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
- *
- * https://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.android.camerax.video.extensions
-
-import androidx.camera.core.AspectRatio
-import androidx.camera.video.Quality
-
-/**
- * a helper function to retrieve the aspect ratio from a QualitySelector enum.
- */
-fun Quality.getAspectRatio(quality: Quality): Int {
- return when {
- arrayOf(Quality.UHD, Quality.FHD, Quality.HD)
- .contains(quality) -> AspectRatio.RATIO_16_9
- (quality == Quality.SD) -> AspectRatio.RATIO_4_3
- else -> throw UnsupportedOperationException()
- }
-}
-
-/**
- * a helper function to retrieve the aspect ratio string from a Quality enum.
- */
-fun Quality.getAspectRatioString(quality: Quality, portraitMode:Boolean) :String {
- val hdQualities = arrayOf(Quality.UHD, Quality.FHD, Quality.HD)
- val ratio =
- when {
- hdQualities.contains(quality) -> Pair(16, 9)
- quality == Quality.SD -> Pair(4, 3)
- else -> throw UnsupportedOperationException()
- }
-
- return if (portraitMode) "V,${ratio.second}:${ratio.first}"
- else "H,${ratio.first}:${ratio.second}"
-}
-
-/**
- * Get the name (a string) from the given Video.Quality object.
- */
-fun Quality.getNameString() :String {
- return when (this) {
- Quality.UHD -> "QUALITY_UHD(2160p)"
- Quality.FHD -> "QUALITY_FHD(1080p)"
- Quality.HD -> "QUALITY_HD(720p)"
- Quality.SD -> "QUALITY_SD(480p)"
- else -> throw IllegalArgumentException("Quality $this is NOT supported")
- }
-}
-
-/**
- * Translate Video.Quality name(a string) to its Quality object.
- */
-fun Quality.getQualityObject(name:String) : Quality {
- return when (name) {
- Quality.UHD.getNameString() -> Quality.UHD
- Quality.FHD.getNameString() -> Quality.FHD
- Quality.HD.getNameString() -> Quality.HD
- Quality.SD.getNameString() -> Quality.SD
- else -> throw IllegalArgumentException("Quality string $name is NOT supported")
- }
-}
diff --git a/CameraXVideo/app/src/main/java/com/example/android/camerax/video/extensions/VideoRecordEventExt.kt b/CameraXVideo/app/src/main/java/com/example/android/camerax/video/extensions/VideoRecordEventExt.kt
deleted file mode 100644
index bf4bfb2d..00000000
--- a/CameraXVideo/app/src/main/java/com/example/android/camerax/video/extensions/VideoRecordEventExt.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright 2021 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
- *
- * https://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.android.camerax.video.extensions
-
-import androidx.camera.video.VideoRecordEvent
-
-/**
- * A helper extended function to get the name(string) for the VideoRecordEvent.
- */
-fun VideoRecordEvent.getNameString() : String {
- return when (this) {
- is VideoRecordEvent.Status -> "Status"
- is VideoRecordEvent.Start -> "Started"
- is VideoRecordEvent.Finalize-> "Finalized"
- is VideoRecordEvent.Pause -> "Paused"
- is VideoRecordEvent.Resume -> "Resumed"
- else -> throw IllegalArgumentException("Unknown VideoRecordEvent: $this")
- }
-}
diff --git a/CameraXVideo/app/src/main/java/com/example/android/camerax/video/fragments/CaptureFragment.kt b/CameraXVideo/app/src/main/java/com/example/android/camerax/video/fragments/CaptureFragment.kt
deleted file mode 100644
index a2f3be85..00000000
--- a/CameraXVideo/app/src/main/java/com/example/android/camerax/video/fragments/CaptureFragment.kt
+++ /dev/null
@@ -1,552 +0,0 @@
-/**
- * Copyright 2021 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
- *
- * https://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.
- */
-
-/**
- * Simple app to demonstrate CameraX Video capturing with Recorder ( to local files ), with the
- * following simple control follow:
- * - user starts capture.
- * - this app disables all UI selections.
- * - this app enables capture run-time UI (pause/resume/stop).
- * - user controls recording with run-time UI, eventually tap "stop" to end.
- * - this app informs CameraX recording to stop with recording.stop() (or recording.close()).
- * - CameraX notify this app that the recording is indeed stopped, with the Finalize event.
- * - this app starts VideoViewer fragment to view the captured result.
-*/
-
-package com.example.android.camerax.video.fragments
-
-import android.annotation.SuppressLint
-import android.content.ContentValues
-import android.content.res.Configuration
-import java.text.SimpleDateFormat
-import android.os.Bundle
-import android.provider.MediaStore
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.*
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.lifecycleScope
-import androidx.navigation.NavController
-import androidx.navigation.Navigation
-import com.example.android.camerax.video.R
-import com.example.android.camerax.video.databinding.FragmentCaptureBinding
-import androidx.camera.lifecycle.ProcessCameraProvider
-import androidx.camera.core.CameraSelector
-import androidx.camera.core.Preview
-import androidx.camera.video.*
-import androidx.concurrent.futures.await
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.core.content.ContextCompat
-import androidx.core.util.Consumer
-import androidx.core.view.updateLayoutParams
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.whenCreated
-import androidx.recyclerview.widget.LinearLayoutManager
-import com.example.android.camera.utils.GenericListAdapter
-import com.example.android.camerax.video.extensions.getAspectRatio
-import com.example.android.camerax.video.extensions.getAspectRatioString
-import com.example.android.camerax.video.extensions.getNameString
-import kotlinx.coroutines.*
-import java.util.*
-
-class CaptureFragment : Fragment() {
-
- // UI with ViewBinding
- private var _captureViewBinding: FragmentCaptureBinding? = null
- private val captureViewBinding get() = _captureViewBinding!!
- private val captureLiveStatus = MutableLiveData()
-
- /** Host's navigation controller */
- private val navController: NavController by lazy {
- Navigation.findNavController(requireActivity(), R.id.fragment_container)
- }
-
- private val cameraCapabilities = mutableListOf()
-
- private lateinit var videoCapture: VideoCapture
- private var currentRecording: Recording? = null
- private lateinit var recordingState:VideoRecordEvent
-
- // Camera UI states and inputs
- enum class UiState {
- IDLE, // Not recording, all UI controls are active.
- RECORDING, // Camera is recording, only display Pause/Resume & Stop button.
- FINALIZED, // Recording just completes, disable all RECORDING UI controls.
- RECOVERY // For future use.
- }
- private var cameraIndex = 0
- private var qualityIndex = DEFAULT_QUALITY_IDX
- private var audioEnabled = false
-
- private val mainThreadExecutor by lazy { ContextCompat.getMainExecutor(requireContext()) }
- private var enumerationDeferred:Deferred? = null
-
- // main cameraX capture functions
- /**
- * Always bind preview + video capture use case combinations in this sample
- * (VideoCapture can work on its own). The function should always execute on
- * the main thread.
- */
- private suspend fun bindCaptureUsecase() {
- val cameraProvider = ProcessCameraProvider.getInstance(requireContext()).await()
-
- val cameraSelector = getCameraSelector(cameraIndex)
-
- // create the user required QualitySelector (video resolution): we know this is
- // supported, a valid qualitySelector will be created.
- val quality = cameraCapabilities[cameraIndex].qualities[qualityIndex]
- val qualitySelector = QualitySelector.from(quality)
-
- captureViewBinding.previewView.updateLayoutParams {
- val orientation = this@CaptureFragment.resources.configuration.orientation
- dimensionRatio = quality.getAspectRatioString(quality,
- (orientation == Configuration.ORIENTATION_PORTRAIT))
- }
-
- val preview = Preview.Builder()
- .setTargetAspectRatio(quality.getAspectRatio(quality))
- .build().apply {
- setSurfaceProvider(captureViewBinding.previewView.surfaceProvider)
- }
-
- // build a recorder, which can:
- // - record video/audio to MediaStore(only shown here), File, ParcelFileDescriptor
- // - be used create recording(s) (the recording performs recording)
- val recorder = Recorder.Builder()
- .setQualitySelector(qualitySelector)
- .build()
- videoCapture = VideoCapture.withOutput(recorder)
-
- try {
- cameraProvider.unbindAll()
- cameraProvider.bindToLifecycle(
- viewLifecycleOwner,
- cameraSelector,
- videoCapture,
- preview
- )
- } catch (exc: Exception) {
- // we are on main thread, let's reset the controls on the UI.
- Log.e(TAG, "Use case binding failed", exc)
- resetUIandState("bindToLifecycle failed: $exc")
- }
- enableUI(true)
- }
-
- /**
- * Kick start the video recording
- * - config Recorder to capture to MediaStoreOutput
- * - register RecordEvent Listener
- * - apply audio request from user
- * - start recording!
- * After this function, user could start/pause/resume/stop recording and application listens
- * to VideoRecordEvent for the current recording status.
- */
- @SuppressLint("MissingPermission")
- private fun startRecording() {
- // create MediaStoreOutputOptions for our recorder: resulting our recording!
- val name = "CameraX-recording-" +
- SimpleDateFormat(FILENAME_FORMAT, Locale.US)
- .format(System.currentTimeMillis()) + ".mp4"
- val contentValues = ContentValues().apply {
- put(MediaStore.Video.Media.DISPLAY_NAME, name)
- }
- val mediaStoreOutput = MediaStoreOutputOptions.Builder(
- requireActivity().contentResolver,
- MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
- .setContentValues(contentValues)
- .build()
-
- // configure Recorder and Start recording to the mediaStoreOutput.
- currentRecording = videoCapture.output
- .prepareRecording(requireActivity(), mediaStoreOutput)
- .apply { if (audioEnabled) withAudioEnabled() }
- .start(mainThreadExecutor, captureListener)
-
- Log.i(TAG, "Recording started")
- }
-
- /**
- * CaptureEvent listener.
- */
- private val captureListener = Consumer { event ->
- // cache the recording state
- if (event !is VideoRecordEvent.Status)
- recordingState = event
-
- updateUI(event)
-
- if (event is VideoRecordEvent.Finalize) {
- // display the captured video
- lifecycleScope.launch {
- navController.navigate(
- CaptureFragmentDirections.actionCaptureToVideoViewer(
- event.outputResults.outputUri
- )
- )
- }
- }
- }
-
- /**
- * Retrieve the asked camera's type(lens facing type). In this sample, only 2 types:
- * idx is even number: CameraSelector.LENS_FACING_BACK
- * odd number: CameraSelector.LENS_FACING_FRONT
- */
- private fun getCameraSelector(idx: Int) : CameraSelector {
- if (cameraCapabilities.size == 0) {
- Log.i(TAG, "Error: This device does not have any camera, bailing out")
- requireActivity().finish()
- }
- return (cameraCapabilities[idx % cameraCapabilities.size].camSelector)
- }
-
- data class CameraCapability(val camSelector: CameraSelector, val qualities:List)
- /**
- * Query and cache this platform's camera capabilities, run only once.
- */
- init {
- enumerationDeferred = lifecycleScope.async {
- whenCreated {
- val provider = ProcessCameraProvider.getInstance(requireContext()).await()
-
- provider.unbindAll()
- for (camSelector in arrayOf(
- CameraSelector.DEFAULT_BACK_CAMERA,
- CameraSelector.DEFAULT_FRONT_CAMERA
- )) {
- try {
- // just get the camera.cameraInfo to query capabilities
- // we are not binding anything here.
- if (provider.hasCamera(camSelector)) {
- val camera = provider.bindToLifecycle(requireActivity(), camSelector)
- QualitySelector
- .getSupportedQualities(camera.cameraInfo)
- .filter { quality ->
- listOf(Quality.UHD, Quality.FHD, Quality.HD, Quality.SD)
- .contains(quality)
- }.also {
- cameraCapabilities.add(CameraCapability(camSelector, it))
- }
- }
- } catch (exc: java.lang.Exception) {
- Log.e(TAG, "Camera Face $camSelector is not supported")
- }
- }
- }
- }
- }
-
- /**
- * One time initialize for CameraFragment (as a part of fragment layout's creation process).
- * This function performs the following:
- * - initialize but disable all UI controls except the Quality selection.
- * - set up the Quality selection recycler view.
- * - bind use cases to a lifecycle camera, enable UI controls.
- */
- private fun initCameraFragment() {
- initializeUI()
- viewLifecycleOwner.lifecycleScope.launch {
- if (enumerationDeferred != null) {
- enumerationDeferred!!.await()
- enumerationDeferred = null
- }
- initializeQualitySectionsUI()
-
- bindCaptureUsecase()
- }
- }
-
- /**
- * Initialize UI. Preview and Capture actions are configured in this function.
- * Note that preview and capture are both initialized either by UI or CameraX callbacks
- * (except the very 1st time upon entering to this fragment in onCreateView()
- */
- @SuppressLint("ClickableViewAccessibility", "MissingPermission")
- private fun initializeUI() {
- captureViewBinding.cameraButton.apply {
- setOnClickListener {
- cameraIndex = (cameraIndex + 1) % cameraCapabilities.size
- // camera device change is in effect instantly:
- // - reset quality selection
- // - restart preview
- qualityIndex = DEFAULT_QUALITY_IDX
- initializeQualitySectionsUI()
- enableUI(false)
- viewLifecycleOwner.lifecycleScope.launch {
- bindCaptureUsecase()
- }
- }
- isEnabled = false
- }
-
- // audioEnabled by default is disabled.
- captureViewBinding.audioSelection.isChecked = audioEnabled
- captureViewBinding.audioSelection.setOnClickListener {
- audioEnabled = captureViewBinding.audioSelection.isChecked
- }
-
- // React to user touching the capture button
- captureViewBinding.captureButton.apply {
- setOnClickListener {
- if (!this@CaptureFragment::recordingState.isInitialized ||
- recordingState is VideoRecordEvent.Finalize)
- {
- enableUI(false) // Our eventListener will turn on the Recording UI.
- startRecording()
- } else {
- when (recordingState) {
- is VideoRecordEvent.Start -> {
- currentRecording?.pause()
- captureViewBinding.stopButton.visibility = View.VISIBLE
- }
- is VideoRecordEvent.Pause -> currentRecording?.resume()
- is VideoRecordEvent.Resume -> currentRecording?.pause()
- else -> throw IllegalStateException("recordingState in unknown state")
- }
- }
- }
- isEnabled = false
- }
-
- captureViewBinding.stopButton.apply {
- setOnClickListener {
- // stopping: hide it after getting a click before we go to viewing fragment
- captureViewBinding.stopButton.visibility = View.INVISIBLE
- if (currentRecording == null || recordingState is VideoRecordEvent.Finalize) {
- return@setOnClickListener
- }
-
- val recording = currentRecording
- if (recording != null) {
- recording.stop()
- currentRecording = null
- }
- captureViewBinding.captureButton.setImageResource(R.drawable.ic_start)
- }
- // ensure the stop button is initialized disabled & invisible
- visibility = View.INVISIBLE
- isEnabled = false
- }
-
- captureLiveStatus.observe(viewLifecycleOwner) {
- captureViewBinding.captureStatus.apply {
- post { text = it }
- }
- }
- captureLiveStatus.value = getString(R.string.Idle)
- }
-
- /**
- * UpdateUI according to CameraX VideoRecordEvent type:
- * - user starts capture.
- * - this app disables all UI selections.
- * - this app enables capture run-time UI (pause/resume/stop).
- * - user controls recording with run-time UI, eventually tap "stop" to end.
- * - this app informs CameraX recording to stop with recording.stop() (or recording.close()).
- * - CameraX notify this app that the recording is indeed stopped, with the Finalize event.
- * - this app starts VideoViewer fragment to view the captured result.
- */
- private fun updateUI(event: VideoRecordEvent) {
- val state = if (event is VideoRecordEvent.Status) recordingState.getNameString()
- else event.getNameString()
- when (event) {
- is VideoRecordEvent.Status -> {
- // placeholder: we update the UI with new status after this when() block,
- // nothing needs to do here.
- }
- is VideoRecordEvent.Start -> {
- showUI(UiState.RECORDING, event.getNameString())
- }
- is VideoRecordEvent.Finalize-> {
- showUI(UiState.FINALIZED, event.getNameString())
- }
- is VideoRecordEvent.Pause -> {
- captureViewBinding.captureButton.setImageResource(R.drawable.ic_resume)
- }
- is VideoRecordEvent.Resume -> {
- captureViewBinding.captureButton.setImageResource(R.drawable.ic_pause)
- }
- }
-
- val stats = event.recordingStats
- val size = stats.numBytesRecorded / 1000
- val time = java.util.concurrent.TimeUnit.NANOSECONDS.toSeconds(stats.recordedDurationNanos)
- var text = "${state}: recorded ${size}KB, in ${time}second"
- if(event is VideoRecordEvent.Finalize)
- text = "${text}\nFile saved to: ${event.outputResults.outputUri}"
-
- captureLiveStatus.value = text
- Log.i(TAG, "recording event: $text")
- }
-
- /**
- * Enable/disable UI:
- * User could select the capture parameters when recording is not in session
- * Once recording is started, need to disable able UI to avoid conflict.
- */
- private fun enableUI(enable: Boolean) {
- arrayOf(captureViewBinding.cameraButton,
- captureViewBinding.captureButton,
- captureViewBinding.stopButton,
- captureViewBinding.audioSelection,
- captureViewBinding.qualitySelection).forEach {
- it.isEnabled = enable
- }
- // disable the camera button if no device to switch
- if (cameraCapabilities.size <= 1) {
- captureViewBinding.cameraButton.isEnabled = false
- }
- // disable the resolution list if no resolution to switch
- if (cameraCapabilities[cameraIndex].qualities.size <= 1) {
- captureViewBinding.qualitySelection.apply { isEnabled = false }
- }
- }
-
- /**
- * initialize UI for recording:
- * - at recording: hide audio, qualitySelection,change camera UI; enable stop button
- * - otherwise: show all except the stop button
- */
- private fun showUI(state: UiState, status:String = "idle") {
- captureViewBinding.let {
- when(state) {
- UiState.IDLE -> {
- it.captureButton.setImageResource(R.drawable.ic_start)
- it.stopButton.visibility = View.INVISIBLE
-
- it.cameraButton.visibility= View.VISIBLE
- it.audioSelection.visibility = View.VISIBLE
- it.qualitySelection.visibility=View.VISIBLE
- }
- UiState.RECORDING -> {
- it.cameraButton.visibility = View.INVISIBLE
- it.audioSelection.visibility = View.INVISIBLE
- it.qualitySelection.visibility = View.INVISIBLE
-
- it.captureButton.setImageResource(R.drawable.ic_pause)
- it.captureButton.isEnabled = true
- it.stopButton.visibility = View.VISIBLE
- it.stopButton.isEnabled = true
- }
- UiState.FINALIZED -> {
- it.captureButton.setImageResource(R.drawable.ic_start)
- it.stopButton.visibility = View.INVISIBLE
- }
- else -> {
- val errorMsg = "Error: showUI($state) is not supported"
- Log.e(TAG, errorMsg)
- return
- }
- }
- it.captureStatus.text = status
- }
- }
-
- /**
- * ResetUI (restart):
- * in case binding failed, let's give it another change for re-try. In future cases
- * we might fail and user get notified on the status
- */
- private fun resetUIandState(reason: String) {
- enableUI(true)
- showUI(UiState.IDLE, reason)
-
- cameraIndex = 0
- qualityIndex = DEFAULT_QUALITY_IDX
- audioEnabled = false
- captureViewBinding.audioSelection.isChecked = audioEnabled
- initializeQualitySectionsUI()
- }
-
- /**
- * initializeQualitySectionsUI():
- * Populate a RecyclerView to display camera capabilities:
- * - one front facing
- * - one back facing
- * User selection is saved to qualityIndex, will be used
- * in the bindCaptureUsecase().
- */
- private fun initializeQualitySectionsUI() {
- val selectorStrings = cameraCapabilities[cameraIndex].qualities.map {
- it.getNameString()
- }
- // create the adapter to Quality selection RecyclerView
- captureViewBinding.qualitySelection.apply {
- layoutManager = LinearLayoutManager(context)
- adapter = GenericListAdapter(
- selectorStrings,
- itemLayoutId = R.layout.video_quality_item
- ) { holderView, qcString, position ->
-
- holderView.apply {
- findViewById(R.id.qualityTextView)?.text = qcString
- // select the default quality selector
- isSelected = (position == qualityIndex)
- }
-
- holderView.setOnClickListener { view ->
- if (qualityIndex == position) return@setOnClickListener
-
- captureViewBinding.qualitySelection.let {
- // deselect the previous selection on UI.
- it.findViewHolderForAdapterPosition(qualityIndex)
- ?.itemView
- ?.isSelected = false
- }
- // turn on the new selection on UI.
- view.isSelected = true
- qualityIndex = position
-
- // rebind the use cases to put the new QualitySelection in action.
- enableUI(false)
- viewLifecycleOwner.lifecycleScope.launch {
- bindCaptureUsecase()
- }
- }
- }
- isEnabled = false
- }
- }
-
- // System function implementations
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- _captureViewBinding = FragmentCaptureBinding.inflate(inflater, container, false)
- return captureViewBinding.root
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- initCameraFragment()
- }
- override fun onDestroyView() {
- _captureViewBinding = null
- super.onDestroyView()
- }
-
- companion object {
- // default Quality selection if no input from UI
- const val DEFAULT_QUALITY_IDX = 0
- val TAG:String = CaptureFragment::class.java.simpleName
- private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
- }
-}
\ No newline at end of file
diff --git a/CameraXVideo/app/src/main/java/com/example/android/camerax/video/fragments/PermissionsFragment.kt b/CameraXVideo/app/src/main/java/com/example/android/camerax/video/fragments/PermissionsFragment.kt
deleted file mode 100644
index 00150c32..00000000
--- a/CameraXVideo/app/src/main/java/com/example/android/camerax/video/fragments/PermissionsFragment.kt
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright 2021 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
- *
- * https://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.android.camerax.video.fragments
-
-import android.Manifest
-import android.content.Context
-import android.content.pm.PackageManager
-import android.os.Build
-import android.os.Bundle
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.Toast
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.core.content.ContextCompat
-import androidx.fragment.app.Fragment
-import androidx.lifecycle.lifecycleScope
-import androidx.navigation.Navigation
-import com.example.android.camerax.video.R
-import com.example.android.camerax.video.databinding.FragmentPermissionBinding
-
-private var PERMISSIONS_REQUIRED = arrayOf(
- Manifest.permission.CAMERA,
- Manifest.permission.RECORD_AUDIO)
-
-/**
- * This [Fragment] requests permissions and, once granted, it will navigate to the next fragment
- */
-class PermissionsFragment : Fragment() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // add the storage access permission request for Android 9 and below.
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
- val permissionList = PERMISSIONS_REQUIRED.toMutableList()
- permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
- PERMISSIONS_REQUIRED = permissionList.toTypedArray()
- }
-
- if (!hasPermissions(requireContext())) {
- // Request camera-related permissions
- activityResultLauncher.launch(PERMISSIONS_REQUIRED)
- }
- }
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- return FragmentPermissionBinding.inflate(inflater, container, false).also {
- if (hasPermissions(requireContext())) {
- navigateToCapture()
- } else {
- Log.e(PermissionsFragment::class.java.simpleName,
- "Re-requesting permissions ...")
- activityResultLauncher.launch(PERMISSIONS_REQUIRED)
- }
- }.root
- }
- companion object {
- /** Convenience method used to check if all permissions required by this app are granted */
- fun hasPermissions(context: Context) = PERMISSIONS_REQUIRED.all {
- ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
- }
- }
- private val activityResultLauncher =
- registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions())
- { permissions ->
- // Handle Permission granted/rejected
- var permissionGranted = true
- permissions.entries.forEach {
- if (it.key in PERMISSIONS_REQUIRED && it.value == false)
- permissionGranted = false
- }
- if (permissionGranted && !permissions.isEmpty()) {
- navigateToCapture()
- }
- if (!permissionGranted) {
- Toast.makeText(context, "Permission request denied", Toast.LENGTH_LONG).show()
- }
- }
-
- private fun navigateToCapture() {
- lifecycleScope.launchWhenStarted {
- Navigation.findNavController(requireActivity(), R.id.fragment_container).navigate(
- PermissionsFragmentDirections.actionPermissionsToCapture())
- }
- }
-}
\ No newline at end of file
diff --git a/CameraXVideo/app/src/main/java/com/example/android/camerax/video/fragments/VideoViewerFragment.kt b/CameraXVideo/app/src/main/java/com/example/android/camerax/video/fragments/VideoViewerFragment.kt
deleted file mode 100644
index 4aa8e31d..00000000
--- a/CameraXVideo/app/src/main/java/com/example/android/camerax/video/fragments/VideoViewerFragment.kt
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright 2021 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
- *
- * https://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.
- */
-
-// Simple VideoView to display the just captured video
-
-package com.example.android.camerax.video.fragments
-
-import android.content.ContentResolver
-import android.database.Cursor
-import android.database.CursorIndexOutOfBoundsException
-import android.media.MediaScannerConnection
-import android.net.Uri
-import android.os.Build
-import android.os.Bundle
-import android.provider.MediaStore
-import android.provider.OpenableColumns
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.MediaController
-import androidx.navigation.fragment.navArgs
-import com.example.android.camerax.video.databinding.FragmentVideoViewerBinding
-import android.util.TypedValue
-import androidx.lifecycle.lifecycleScope
-import androidx.navigation.Navigation
-import com.example.android.camerax.video.R
-import kotlinx.coroutines.launch
-import java.lang.RuntimeException
-
-/**
- * VideoViewerFragment:
- * Accept MediaStore URI and play it with VideoView (Also displaying file size and location)
- * Note: Might be good to retrieve the encoded file mime type (not based on file type)
- */
-class VideoViewerFragment : androidx.fragment.app.Fragment() {
- private val args: VideoViewerFragmentArgs by navArgs()
-
- // This property is only valid between onCreateView and onDestroyView.
- private var _binding: FragmentVideoViewerBinding? = null
- private val binding get() = _binding!!
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- _binding = FragmentVideoViewerBinding.inflate(inflater, container, false)
- // UI adjustment + hacking to display VideoView use tips / capture result
- val tv = TypedValue()
- if (requireActivity().theme.resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
- val actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, resources.displayMetrics)
- binding.videoViewerTips.y = binding.videoViewerTips.y - actionBarHeight
- }
-
- return binding.root
- }
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
- showVideo(args.uri)
- } else {
- // force MediaScanner to re-scan the media file.
- val path = getAbsolutePathFromUri(args.uri) ?: return
- MediaScannerConnection.scanFile(
- context, arrayOf(path), null
- ) { _, uri ->
- // playback video on main thread with VideoView
- if (uri != null) {
- lifecycleScope.launch {
- showVideo(uri)
- }
- }
- }
- }
-
- // Handle back button press
- binding.backButton.setOnClickListener {
- Navigation.findNavController(requireActivity(), R.id.fragment_container).navigateUp()
- }
- }
-
- override fun onDestroyView() {
- _binding = null
- super.onDestroyView()
- }
-
- /**
- * A helper function to play the recorded video. Note that VideoView/MediaController auto-hides
- * the play control menus, touch on the video area would bring it back for 3 second.
- * This functionality not really related to capture, provided here for convenient purpose to view:
- * - the captured video
- * - the file size and location
- */
- private fun showVideo(uri : Uri) {
- val fileSize = getFileSizeFromUri(uri)
- if (fileSize == null || fileSize <= 0) {
- Log.e("VideoViewerFragment", "Failed to get recorded file size, could not be played!")
- return
- }
-
- val filePath = getAbsolutePathFromUri(uri) ?: return
- val fileInfo = "FileSize: $fileSize\n $filePath"
- Log.i("VideoViewerFragment", fileInfo)
- binding.videoViewerTips.text = fileInfo
-
- val mc = MediaController(requireContext())
- binding.videoViewer.apply {
- setVideoURI(uri)
- setMediaController(mc)
- requestFocus()
- }.start()
- mc.show(0)
- }
-
- /**
- * A helper function to get the captured file location.
- */
- private fun getAbsolutePathFromUri(contentUri: Uri): String? {
- var cursor:Cursor? = null
- return try {
- cursor = requireContext()
- .contentResolver
- .query(contentUri, arrayOf(MediaStore.Images.Media.DATA), null, null, null)
- if (cursor == null) {
- return null
- }
- val columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
- cursor.moveToFirst()
- cursor.getString(columnIndex)
- } catch (e: RuntimeException) {
- Log.e("VideoViewerFragment", String.format(
- "Failed in getting absolute path for Uri %s with Exception %s",
- contentUri.toString(), e.toString()
- )
- )
- null
- } finally {
- cursor?.close()
- }
- }
-
- /**
- * A helper function to retrieve the captured file size.
- */
- private fun getFileSizeFromUri(contentUri: Uri): Long? {
- val cursor = requireContext()
- .contentResolver
- .query(contentUri, null, null, null, null)
- ?: return null
-
- val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
- cursor.moveToFirst()
-
- cursor.use {
- return it.getLong(sizeIndex)
- }
- }
-}
diff --git a/CameraXVideo/app/src/main/res/color/selector_button_text.xml b/CameraXVideo/app/src/main/res/color/selector_button_text.xml
deleted file mode 100644
index 5857cfd2..00000000
--- a/CameraXVideo/app/src/main/res/color/selector_button_text.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXVideo/app/src/main/res/color/selector_ic.xml b/CameraXVideo/app/src/main/res/color/selector_ic.xml
deleted file mode 100644
index 893f3839..00000000
--- a/CameraXVideo/app/src/main/res/color/selector_ic.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXVideo/app/src/main/res/drawable-hdpi/ic_back.xml b/CameraXVideo/app/src/main/res/drawable-hdpi/ic_back.xml
deleted file mode 100644
index 6994a08d..00000000
--- a/CameraXVideo/app/src/main/res/drawable-hdpi/ic_back.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
diff --git a/CameraXVideo/app/src/main/res/drawable-hdpi/ic_launcher.png b/CameraXVideo/app/src/main/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index bba1165c..00000000
Binary files a/CameraXVideo/app/src/main/res/drawable-hdpi/ic_launcher.png and /dev/null differ
diff --git a/CameraXVideo/app/src/main/res/drawable-mdpi/ic_launcher.png b/CameraXVideo/app/src/main/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 43045910..00000000
Binary files a/CameraXVideo/app/src/main/res/drawable-mdpi/ic_launcher.png and /dev/null differ
diff --git a/CameraXVideo/app/src/main/res/drawable-xhdpi/ic_launcher.png b/CameraXVideo/app/src/main/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 80c5ebaa..00000000
Binary files a/CameraXVideo/app/src/main/res/drawable-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/CameraXVideo/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/CameraXVideo/app/src/main/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index 9baac9b6..00000000
Binary files a/CameraXVideo/app/src/main/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/CameraXVideo/app/src/main/res/drawable/ic_pause.xml b/CameraXVideo/app/src/main/res/drawable/ic_pause.xml
deleted file mode 100644
index 0916a526..00000000
--- a/CameraXVideo/app/src/main/res/drawable/ic_pause.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/CameraXVideo/app/src/main/res/drawable/ic_resume.xml b/CameraXVideo/app/src/main/res/drawable/ic_resume.xml
deleted file mode 100644
index 6aff59b8..00000000
--- a/CameraXVideo/app/src/main/res/drawable/ic_resume.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraXVideo/app/src/main/res/drawable/ic_start.xml b/CameraXVideo/app/src/main/res/drawable/ic_start.xml
deleted file mode 100644
index 9e1d0918..00000000
--- a/CameraXVideo/app/src/main/res/drawable/ic_start.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraXVideo/app/src/main/res/drawable/ic_stop.xml b/CameraXVideo/app/src/main/res/drawable/ic_stop.xml
deleted file mode 100644
index c23c541f..00000000
--- a/CameraXVideo/app/src/main/res/drawable/ic_stop.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/CameraXVideo/app/src/main/res/drawable/ic_switch.xml b/CameraXVideo/app/src/main/res/drawable/ic_switch.xml
deleted file mode 100644
index a8d40481..00000000
--- a/CameraXVideo/app/src/main/res/drawable/ic_switch.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXVideo/app/src/main/res/drawable/selector_background.xml b/CameraXVideo/app/src/main/res/drawable/selector_background.xml
deleted file mode 100644
index 796b178e..00000000
--- a/CameraXVideo/app/src/main/res/drawable/selector_background.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXVideo/app/src/main/res/layout-land/fragment_capture.xml b/CameraXVideo/app/src/main/res/layout-land/fragment_capture.xml
deleted file mode 100644
index 1ea2270e..00000000
--- a/CameraXVideo/app/src/main/res/layout-land/fragment_capture.xml
+++ /dev/null
@@ -1,128 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/CameraXVideo/app/src/main/res/layout/activity_main.xml b/CameraXVideo/app/src/main/res/layout/activity_main.xml
deleted file mode 100644
index d44d34ec..00000000
--- a/CameraXVideo/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
diff --git a/CameraXVideo/app/src/main/res/layout/fragment_capture.xml b/CameraXVideo/app/src/main/res/layout/fragment_capture.xml
deleted file mode 100644
index a3d87951..00000000
--- a/CameraXVideo/app/src/main/res/layout/fragment_capture.xml
+++ /dev/null
@@ -1,126 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/CameraXVideo/app/src/main/res/layout/fragment_permission.xml b/CameraXVideo/app/src/main/res/layout/fragment_permission.xml
deleted file mode 100644
index 7a2fde15..00000000
--- a/CameraXVideo/app/src/main/res/layout/fragment_permission.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
diff --git a/CameraXVideo/app/src/main/res/layout/fragment_video_viewer.xml b/CameraXVideo/app/src/main/res/layout/fragment_video_viewer.xml
deleted file mode 100644
index 40a4b642..00000000
--- a/CameraXVideo/app/src/main/res/layout/fragment_video_viewer.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXVideo/app/src/main/res/layout/video_quality_item.xml b/CameraXVideo/app/src/main/res/layout/video_quality_item.xml
deleted file mode 100644
index 6f3b0934..00000000
--- a/CameraXVideo/app/src/main/res/layout/video_quality_item.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXVideo/app/src/main/res/navigation/nav_graph.xml b/CameraXVideo/app/src/main/res/navigation/nav_graph.xml
deleted file mode 100644
index f541d9dc..00000000
--- a/CameraXVideo/app/src/main/res/navigation/nav_graph.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/CameraXVideo/app/src/main/res/values-night/themes.xml b/CameraXVideo/app/src/main/res/values-night/themes.xml
deleted file mode 100644
index 32aa2dad..00000000
--- a/CameraXVideo/app/src/main/res/values-night/themes.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/CameraXVideo/app/src/main/res/values/attrs.xml b/CameraXVideo/app/src/main/res/values/attrs.xml
deleted file mode 100644
index e52391d2..00000000
--- a/CameraXVideo/app/src/main/res/values/attrs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXVideo/app/src/main/res/values/colors.xml b/CameraXVideo/app/src/main/res/values/colors.xml
deleted file mode 100644
index 925e2fe0..00000000
--- a/CameraXVideo/app/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
- #00000000
- #FFFFFFFF
- #FF039BE5
- #FF01579B
- #FF40C4FF
- #FF00B0FF
- #66000000
- #FFFFFFFF
- #DDFFFFFF
- #AAFFFFFF
- #FF000000
- #FF0000FF
- #FFFFFFFF
- #80FFFFFF
- #FF440044
- #FF880088
- #FFFF00FF
-
\ No newline at end of file
diff --git a/CameraXVideo/app/src/main/res/values/ids.xml b/CameraXVideo/app/src/main/res/values/ids.xml
deleted file mode 100644
index 51a85684..00000000
--- a/CameraXVideo/app/src/main/res/values/ids.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/CameraXVideo/app/src/main/res/values/strings.xml b/CameraXVideo/app/src/main/res/values/strings.xml
deleted file mode 100644
index f82c5c77..00000000
--- a/CameraXVideo/app/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
- CameraXVideo
- Audio
- Camera
- Capture
- Stop
- Pause
- Resume
- Start
- Finalize
- Idle
- Back
- Dummy Button
- DUMMY\nCONTENT
- Tips for the page:\n\t- tap anywhere to replay\n\t- tap system back button to go back
-
diff --git a/CameraXVideo/app/src/main/res/values/styles.xml b/CameraXVideo/app/src/main/res/values/styles.xml
deleted file mode 100644
index 03aedd16..00000000
--- a/CameraXVideo/app/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraXVideo/app/src/main/res/values/themes.xml b/CameraXVideo/app/src/main/res/values/themes.xml
deleted file mode 100644
index 40e178cb..00000000
--- a/CameraXVideo/app/src/main/res/values/themes.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/CameraXVideo/app/src/main/res/xml/file_paths.xml b/CameraXVideo/app/src/main/res/xml/file_paths.xml
deleted file mode 100644
index 33f725f6..00000000
--- a/CameraXVideo/app/src/main/res/xml/file_paths.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXVideo/app/src/test/java/com/example/android/camerax/video/InstrumentedTests.kt b/CameraXVideo/app/src/test/java/com/example/android/camerax/video/InstrumentedTests.kt
deleted file mode 100644
index 6adefb75..00000000
--- a/CameraXVideo/app/src/test/java/com/example/android/camerax/video/InstrumentedTests.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2021 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
- *
- * https://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.android.camerax.video
-
-import android.Manifest
-import android.content.Context
-import androidx.test.core.app.ApplicationProvider
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.rule.ActivityTestRule
-import androidx.test.rule.GrantPermissionRule
-import org.junit.Assert.assertEquals
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class MainInstrumentedTest {
-
- @get:Rule
- val permissionRule = GrantPermissionRule.grant(Manifest.permission.CAMERA)
-
- @get:Rule
- val activityRule: ActivityTestRule =
- ActivityTestRule(MainActivity::class.java)
-
- @Test
- fun useAppContext() {
- val context = ApplicationProvider.getApplicationContext() as Context
- assertEquals("com.android.example.camerax.video", context.packageName)
- }
-}
diff --git a/CameraXVideo/build.gradle b/CameraXVideo/build.gradle
deleted file mode 100644
index 6c67f327..00000000
--- a/CameraXVideo/build.gradle
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.
- */
-
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
-
-buildscript {
- // Top-level variables used for versioning
- ext.kotlin_version = '1.5.21'
- ext.java_version = JavaVersion.VERSION_1_8
-
- repositories {
- google()
- mavenCentral()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:7.2.2'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
- classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.4.1'
-
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
- }
-}
-
-allprojects {
- repositories {
- google()
- mavenCentral()
- }
-}
-
-task clean(type: Delete) {
- delete rootProject.buildDir
-}
diff --git a/CameraXVideo/gradle.properties b/CameraXVideo/gradle.properties
deleted file mode 100644
index b448ae14..00000000
--- a/CameraXVideo/gradle.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-
-# Project-wide Gradle settings.
-
-# IDE (e.g. Android Studio) users:
-# Settings specified in this file will override any Gradle settings
-# configured through the IDE.
-
-# For more details on how to configure your build environment visit
-# http://www.gradle.org/docs/current/userguide/build_environment.html
-
-# Specifies the JVM arguments used for the daemon process.
-# The setting is particularly useful for tweaking memory settings.
-# Default value: -Xmx10248m -XX:MaxPermSize=256m
-# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
-org.gradle.jvmargs=-Xmx1536m
-
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
-
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
-
-android.enableJetifier=true
-android.useAndroidX=true
diff --git a/CameraXVideo/gradle/wrapper/gradle-wrapper.jar b/CameraXVideo/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 94336fca..00000000
Binary files a/CameraXVideo/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/CameraXVideo/gradle/wrapper/gradle-wrapper.properties b/CameraXVideo/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 6e5da6db..00000000
--- a/CameraXVideo/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-#Wed Mar 31 21:04:32 PDT 2021
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
diff --git a/CameraXVideo/gradlew b/CameraXVideo/gradlew
deleted file mode 100755
index cccdd3d5..00000000
--- a/CameraXVideo/gradlew
+++ /dev/null
@@ -1,172 +0,0 @@
-#!/usr/bin/env sh
-
-##############################################################################
-##
-## Gradle start up script for UN*X
-##
-##############################################################################
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn () {
- echo "$*"
-}
-
-die () {
- echo
- echo "$*"
- echo
- exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
- else
- JAVACMD="$JAVA_HOME/bin/java"
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
- fi
- i=$((i+1))
- done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
-fi
-
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=$(save "$@")
-
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-
-# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
-if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
- cd "$(dirname "$0")"
-fi
-
-exec "$JAVACMD" "$@"
diff --git a/CameraXVideo/gradlew.bat b/CameraXVideo/gradlew.bat
deleted file mode 100644
index e95643d6..00000000
--- a/CameraXVideo/gradlew.bat
+++ /dev/null
@@ -1,84 +0,0 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/CameraXVideo/screenshots/icon-web.png b/CameraXVideo/screenshots/icon-web.png
deleted file mode 100644
index d9bd4c48..00000000
Binary files a/CameraXVideo/screenshots/icon-web.png and /dev/null differ
diff --git a/CameraXVideo/screenshots/screenshot.png b/CameraXVideo/screenshots/screenshot.png
deleted file mode 100644
index 12aeda8a..00000000
Binary files a/CameraXVideo/screenshots/screenshot.png and /dev/null differ
diff --git a/CameraXVideo/settings.gradle b/CameraXVideo/settings.gradle
deleted file mode 100644
index 73429e6f..00000000
--- a/CameraXVideo/settings.gradle
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.
- */
-
-include ':app'
-include ':utils'
-
diff --git a/CameraXVideo/utils/.gitignore b/CameraXVideo/utils/.gitignore
deleted file mode 100644
index 38c58757..00000000
--- a/CameraXVideo/utils/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/build
-.idea
\ No newline at end of file
diff --git a/CameraXVideo/utils/README.md b/CameraXVideo/utils/README.md
deleted file mode 100644
index 7a10722f..00000000
--- a/CameraXVideo/utils/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-Do not modify code under this folder outside of `CameraUtils`, it is copied
-automatically by `.github/scripts/copy_utils.sh`.
\ No newline at end of file
diff --git a/CameraXVideo/utils/build.gradle b/CameraXVideo/utils/build.gradle
deleted file mode 100644
index c5d4a883..00000000
--- a/CameraXVideo/utils/build.gradle
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.
- */
-
-apply plugin: 'com.android.library'
-apply plugin: 'kotlin-android'
-
-android {
- compileSdkVersion 29
-
- defaultConfig {
- minSdkVersion 21
- targetSdkVersion 29
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles 'consumer-rules.pro'
- }
-
- compileOptions {
- sourceCompatibility rootProject.ext.java_version
- targetCompatibility rootProject.ext.java_version
- }
-
- kotlinOptions {
- jvmTarget = "$rootProject.ext.java_version"
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
-}
-
-dependencies {
-
- // Kotlin lang
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.4'
-
- // App compat and UI things
- implementation 'androidx.appcompat:appcompat:1.1.0'
- implementation 'androidx.recyclerview:recyclerview:1.1.0'
-
- // EXIF Interface
- implementation 'androidx.exifinterface:exifinterface:1.2.0'
-
- // Unit testing
- testImplementation 'androidx.test.ext:junit:1.1.1'
- testImplementation 'androidx.test:rules:1.2.0'
- testImplementation 'androidx.test:runner:1.2.0'
- testImplementation 'androidx.test.espresso:espresso-core:3.2.0'
- testImplementation 'org.robolectric:robolectric:4.3.1'
-
- // Instrumented testing
- androidTestImplementation 'androidx.test.ext:junit:1.1.1'
- androidTestImplementation 'androidx.test:rules:1.2.0'
- androidTestImplementation 'androidx.test:runner:1.2.0'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
-}
diff --git a/CameraXVideo/utils/src/main/AndroidManifest.xml b/CameraXVideo/utils/src/main/AndroidManifest.xml
deleted file mode 100644
index 2e13c37a..00000000
--- a/CameraXVideo/utils/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
diff --git a/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/AutoFitSurfaceView.kt b/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/AutoFitSurfaceView.kt
deleted file mode 100644
index 3d900d19..00000000
--- a/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/AutoFitSurfaceView.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.content.Context
-import android.util.AttributeSet
-import android.util.Log
-import android.view.SurfaceView
-import kotlin.math.roundToInt
-
-/**
- * A [SurfaceView] that can be adjusted to a specified aspect ratio and
- * performs center-crop transformation of input frames.
- */
-class AutoFitSurfaceView @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyle: Int = 0
-) : SurfaceView(context, attrs, defStyle) {
-
- private var aspectRatio = 0f
-
- /**
- * Sets the aspect ratio for this view. The size of the view will be
- * measured based on the ratio calculated from the parameters.
- *
- * @param width Camera resolution horizontal size
- * @param height Camera resolution vertical size
- */
- fun setAspectRatio(width: Int, height: Int) {
- require(width > 0 && height > 0) { "Size cannot be negative" }
- aspectRatio = width.toFloat() / height.toFloat()
- holder.setFixedSize(width, height)
- requestLayout()
- }
-
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec)
- val width = MeasureSpec.getSize(widthMeasureSpec)
- val height = MeasureSpec.getSize(heightMeasureSpec)
- if (aspectRatio == 0f) {
- setMeasuredDimension(width, height)
- } else {
-
- // Performs center-crop transformation of the camera frames
- val newWidth: Int
- val newHeight: Int
- val actualRatio = if (width > height) aspectRatio else 1f / aspectRatio
- if (width < height * actualRatio) {
- newHeight = height
- newWidth = (height * actualRatio).roundToInt()
- } else {
- newWidth = width
- newHeight = (width / actualRatio).roundToInt()
- }
-
- Log.d(TAG, "Measured dimensions set: $newWidth x $newHeight")
- setMeasuredDimension(newWidth, newHeight)
- }
- }
-
- companion object {
- private val TAG = AutoFitSurfaceView::class.java.simpleName
- }
-}
diff --git a/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/CameraSizes.kt b/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/CameraSizes.kt
deleted file mode 100644
index 6db01d31..00000000
--- a/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/CameraSizes.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.graphics.Point
-import android.hardware.camera2.CameraCharacteristics
-import android.hardware.camera2.params.StreamConfigurationMap
-import android.util.Size
-import android.view.Display
-import kotlin.math.max
-import kotlin.math.min
-
-/** Helper class used to pre-compute shortest and longest sides of a [Size] */
-class SmartSize(width: Int, height: Int) {
- var size = Size(width, height)
- var long = max(size.width, size.height)
- var short = min(size.width, size.height)
- override fun toString() = "SmartSize(${long}x${short})"
-}
-
-/** Standard High Definition size for pictures and video */
-val SIZE_1080P: SmartSize = SmartSize(1920, 1080)
-
-/** Returns a [SmartSize] object for the given [Display] */
-fun getDisplaySmartSize(display: Display): SmartSize {
- val outPoint = Point()
- display.getRealSize(outPoint)
- return SmartSize(outPoint.x, outPoint.y)
-}
-
-/**
- * Returns the largest available PREVIEW size. For more information, see:
- * https://d.android.com/reference/android/hardware/camera2/CameraDevice and
- * https://developer.android.com/reference/android/hardware/camera2/params/StreamConfigurationMap
- */
-fun getPreviewOutputSize(
- display: Display,
- characteristics: CameraCharacteristics,
- targetClass: Class,
- format: Int? = null
-): Size {
-
- // Find which is smaller: screen or 1080p
- val screenSize = getDisplaySmartSize(display)
- val hdScreen = screenSize.long >= SIZE_1080P.long || screenSize.short >= SIZE_1080P.short
- val maxSize = if (hdScreen) SIZE_1080P else screenSize
-
- // If image format is provided, use it to determine supported sizes; else use target class
- val config = characteristics.get(
- CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
- if (format == null)
- assert(StreamConfigurationMap.isOutputSupportedFor(targetClass))
- else
- assert(config.isOutputSupportedFor(format))
- val allSizes = if (format == null)
- config.getOutputSizes(targetClass) else config.getOutputSizes(format)
-
- // Get available sizes and sort them by area from largest to smallest
- val validSizes = allSizes
- .sortedWith(compareBy { it.height * it.width })
- .map { SmartSize(it.width, it.height) }.reversed()
-
- // Then, get the largest output size that is smaller or equal than our max size
- return validSizes.first { it.long <= maxSize.long && it.short <= maxSize.short }.size
-}
\ No newline at end of file
diff --git a/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/ExifUtils.kt b/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/ExifUtils.kt
deleted file mode 100644
index 561c14b3..00000000
--- a/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/ExifUtils.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.graphics.Bitmap
-import android.graphics.Matrix
-import android.util.Log
-import androidx.exifinterface.media.ExifInterface
-
-private const val TAG: String = "ExifUtils"
-
-/** Transforms rotation and mirroring information into one of the [ExifInterface] constants */
-fun computeExifOrientation(rotationDegrees: Int, mirrored: Boolean) = when {
- rotationDegrees == 0 && !mirrored -> ExifInterface.ORIENTATION_NORMAL
- rotationDegrees == 0 && mirrored -> ExifInterface.ORIENTATION_FLIP_HORIZONTAL
- rotationDegrees == 180 && !mirrored -> ExifInterface.ORIENTATION_ROTATE_180
- rotationDegrees == 180 && mirrored -> ExifInterface.ORIENTATION_FLIP_VERTICAL
- rotationDegrees == 270 && mirrored -> ExifInterface.ORIENTATION_TRANSVERSE
- rotationDegrees == 90 && !mirrored -> ExifInterface.ORIENTATION_ROTATE_90
- rotationDegrees == 90 && mirrored -> ExifInterface.ORIENTATION_TRANSPOSE
- rotationDegrees == 270 && mirrored -> ExifInterface.ORIENTATION_ROTATE_270
- rotationDegrees == 270 && !mirrored -> ExifInterface.ORIENTATION_TRANSVERSE
- else -> ExifInterface.ORIENTATION_UNDEFINED
-}
-
-/**
- * Helper function used to convert an EXIF orientation enum into a transformation matrix
- * that can be applied to a bitmap.
- *
- * @return matrix - Transformation required to properly display [Bitmap]
- */
-fun decodeExifOrientation(exifOrientation: Int): Matrix {
- val matrix = Matrix()
-
- // Apply transformation corresponding to declared EXIF orientation
- when (exifOrientation) {
- ExifInterface.ORIENTATION_NORMAL -> Unit
- ExifInterface.ORIENTATION_UNDEFINED -> Unit
- ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90F)
- ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180F)
- ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270F)
- ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.postScale(-1F, 1F)
- ExifInterface.ORIENTATION_FLIP_VERTICAL -> matrix.postScale(1F, -1F)
- ExifInterface.ORIENTATION_TRANSPOSE -> {
- matrix.postScale(-1F, 1F)
- matrix.postRotate(270F)
- }
- ExifInterface.ORIENTATION_TRANSVERSE -> {
- matrix.postScale(-1F, 1F)
- matrix.postRotate(90F)
- }
-
- // Error out if the EXIF orientation is invalid
- else -> Log.e(TAG, "Invalid orientation: $exifOrientation")
- }
-
- // Return the resulting matrix
- return matrix
-}
diff --git a/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/GenericListAdapter.kt b/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/GenericListAdapter.kt
deleted file mode 100644
index a55af278..00000000
--- a/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/GenericListAdapter.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.recyclerview.widget.RecyclerView
-
-/** Type helper used for the callback triggered once our view has been bound */
-typealias BindCallback = (view: View, data: T, position: Int) -> Unit
-
-/** List adapter for generic types, intended used for small-medium lists of data */
-class GenericListAdapter(
- private val dataset: List,
- private val itemLayoutId: Int? = null,
- private val itemViewFactory: (() -> View)? = null,
- private val onBind: BindCallback
-) : RecyclerView.Adapter() {
-
- class GenericListViewHolder(val view: View) : RecyclerView.ViewHolder(view)
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = GenericListViewHolder(when {
- itemViewFactory != null -> itemViewFactory.invoke()
- itemLayoutId != null -> {
- LayoutInflater.from(parent.context)
- .inflate(itemLayoutId, parent, false)
- }
- else -> {
- throw IllegalStateException(
- "Either the layout ID or the view factory need to be non-null")
- }
- })
-
- override fun onBindViewHolder(holder: GenericListViewHolder, position: Int) {
- if (position < 0 || position > dataset.size) return
- onBind(holder.view, dataset[position], position)
- }
-
- override fun getItemCount() = dataset.size
-}
\ No newline at end of file
diff --git a/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt b/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt
deleted file mode 100644
index f9d9a470..00000000
--- a/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.content.Context
-import android.hardware.camera2.CameraCharacteristics
-import android.view.OrientationEventListener
-import android.view.Surface
-import androidx.lifecycle.LiveData
-
-
-/**
- * Calculates closest 90-degree orientation to compensate for the device
- * rotation relative to sensor orientation, i.e., allows user to see camera
- * frames with the expected orientation.
- */
-class OrientationLiveData(
- context: Context,
- characteristics: CameraCharacteristics
-): LiveData() {
-
- private val listener = object : OrientationEventListener(context.applicationContext) {
- override fun onOrientationChanged(orientation: Int) {
- val rotation = when {
- orientation <= 45 -> Surface.ROTATION_0
- orientation <= 135 -> Surface.ROTATION_90
- orientation <= 225 -> Surface.ROTATION_180
- orientation <= 315 -> Surface.ROTATION_270
- else -> Surface.ROTATION_0
- }
- val relative = computeRelativeRotation(characteristics, rotation)
- if (relative != value) postValue(relative)
- }
- }
-
- override fun onActive() {
- super.onActive()
- listener.enable()
- }
-
- override fun onInactive() {
- super.onInactive()
- listener.disable()
- }
-
- companion object {
-
- /**
- * Computes rotation required to transform from the camera sensor orientation to the
- * device's current orientation in degrees.
- *
- * @param characteristics the [CameraCharacteristics] to query for the sensor orientation.
- * @param surfaceRotation the current device orientation as a Surface constant
- * @return the relative rotation from the camera sensor to the current device orientation.
- */
- @JvmStatic
- private fun computeRelativeRotation(
- characteristics: CameraCharacteristics,
- surfaceRotation: Int
- ): Int {
- val sensorOrientationDegrees =
- characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!
-
- val deviceOrientationDegrees = when (surfaceRotation) {
- Surface.ROTATION_0 -> 0
- Surface.ROTATION_90 -> 90
- Surface.ROTATION_180 -> 180
- Surface.ROTATION_270 -> 270
- else -> 0
- }
-
- // Reverse device orientation for front-facing cameras
- val sign = if (characteristics.get(CameraCharacteristics.LENS_FACING) ==
- CameraCharacteristics.LENS_FACING_FRONT) 1 else -1
-
- // Calculate desired JPEG orientation relative to camera orientation to make
- // the image upright relative to the device orientation
- return (sensorOrientationDegrees - (deviceOrientationDegrees * sign) + 360) % 360
- }
- }
-}
diff --git a/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/Yuv.kt b/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/Yuv.kt
deleted file mode 100644
index c476ad0e..00000000
--- a/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/Yuv.kt
+++ /dev/null
@@ -1,191 +0,0 @@
-package com.example.android.camera.utils
-
-import android.graphics.ImageFormat
-import android.media.Image
-import androidx.annotation.IntDef
-import java.nio.ByteBuffer
-
-/*
-This file is converted from part of https://github.com/gordinmitya/yuv2buf.
-Follow the link to find demo app, performance benchmarks and unit tests.
-
-Intro to YUV image formats:
-YUV_420_888 - is a generic format that can be represented as I420, YV12, NV21, and NV12.
-420 means that for each 4 luminosity pixels we have 2 chroma pixels: U and V.
-
-* I420 format represents an image as Y plane followed by U then followed by V plane
- without chroma channels interleaving.
- For example:
- Y Y Y Y
- Y Y Y Y
- U U V V
-
-* NV21 format represents an image as Y plane followed by V and U interleaved. First V then U.
- For example:
- Y Y Y Y
- Y Y Y Y
- V U V U
-
-* YV12 and NV12 are the same as previous formats but with swapped order of V and U. (U then V)
-
-Visualization of these 4 formats:
-https://user-images.githubusercontent.com/9286092/89119601-4f6f8100-d4b8-11ea-9a51-2765f7e513c2.jpg
-
-It's guaranteed that image.getPlanes() always returns planes in order Y U V for YUV_420_888.
-https://developer.android.com/reference/android/graphics/ImageFormat#YUV_420_888
-
-Because I420 and NV21 are more widely supported (RenderScript, OpenCV, MNN)
-the conversion is done into these formats.
-
-More about each format: https://www.fourcc.org/yuv.php
-*/
-
-@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
-@IntDef(ImageFormat.NV21, ImageFormat.YUV_420_888)
-annotation class YuvType
-
-class YuvByteBuffer(image: Image, dstBuffer: ByteBuffer? = null) {
- @YuvType
- val type: Int
- val buffer: ByteBuffer
-
- init {
- val wrappedImage = ImageWrapper(image)
-
- type = if (wrappedImage.u.pixelStride == 1) {
- ImageFormat.YUV_420_888
- } else {
- ImageFormat.NV21
- }
- val size = image.width * image.height * 3 / 2
- buffer = if (
- dstBuffer == null || dstBuffer.capacity() < size ||
- dstBuffer.isReadOnly || !dstBuffer.isDirect
- ) {
- ByteBuffer.allocateDirect(size) }
- else {
- dstBuffer
- }
- buffer.rewind()
-
- removePadding(wrappedImage)
- }
-
- // Input buffers are always direct as described in
- // https://developer.android.com/reference/android/media/Image.Plane#getBuffer()
- private fun removePadding(image: ImageWrapper) {
- val sizeLuma = image.y.width * image.y.height
- val sizeChroma = image.u.width * image.u.height
- if (image.y.rowStride > image.y.width) {
- removePaddingCompact(image.y, buffer, 0)
- } else {
- buffer.position(0)
- buffer.put(image.y.buffer)
- }
- if (type == ImageFormat.YUV_420_888) {
- if (image.u.rowStride > image.u.width) {
- removePaddingCompact(image.u, buffer, sizeLuma)
- removePaddingCompact(image.v, buffer, sizeLuma + sizeChroma)
- } else {
- buffer.position(sizeLuma)
- buffer.put(image.u.buffer)
- buffer.position(sizeLuma + sizeChroma)
- buffer.put(image.v.buffer)
- }
- } else {
- if (image.u.rowStride > image.u.width * 2) {
- removePaddingNotCompact(image, buffer, sizeLuma)
- } else {
- buffer.position(sizeLuma)
- var uv = image.v.buffer
- val properUVSize = image.v.height * image.v.rowStride - 1
- if (uv.capacity() > properUVSize) {
- uv = clipBuffer(image.v.buffer, 0, properUVSize)
- }
- buffer.put(uv)
- val lastOne = image.u.buffer[image.u.buffer.capacity() - 1]
- buffer.put(buffer.capacity() - 1, lastOne)
- }
- }
- buffer.rewind()
- }
-
- private fun removePaddingCompact(
- plane: PlaneWrapper,
- dst: ByteBuffer,
- offset: Int
- ) {
- require(plane.pixelStride == 1) {
- "use removePaddingCompact with pixelStride == 1"
- }
-
- val src = plane.buffer
- val rowStride = plane.rowStride
- var row: ByteBuffer
- dst.position(offset)
- for (i in 0 until plane.height) {
- row = clipBuffer(src, i * rowStride, plane.width)
- dst.put(row)
- }
- }
-
- private fun removePaddingNotCompact(
- image: ImageWrapper,
- dst: ByteBuffer,
- offset: Int
- ) {
- require(image.u.pixelStride == 2) {
- "use removePaddingNotCompact pixelStride == 2"
- }
- val width = image.u.width
- val height = image.u.height
- val rowStride = image.u.rowStride
- var row: ByteBuffer
- dst.position(offset)
- for (i in 0 until height - 1) {
- row = clipBuffer(image.v.buffer, i * rowStride, width * 2)
- dst.put(row)
- }
- row = clipBuffer(image.u.buffer, (height - 1) * rowStride - 1, width * 2)
- dst.put(row)
- }
-
- private fun clipBuffer(buffer: ByteBuffer, start: Int, size: Int): ByteBuffer {
- val duplicate = buffer.duplicate()
- duplicate.position(start)
- duplicate.limit(start + size)
- return duplicate.slice()
- }
-
- private class ImageWrapper(image:Image) {
- val width= image.width
- val height = image.height
- val y = PlaneWrapper(width, height, image.planes[0])
- val u = PlaneWrapper(width / 2, height / 2, image.planes[1])
- val v = PlaneWrapper(width / 2, height / 2, image.planes[2])
-
- // Check this is a supported image format
- // https://developer.android.com/reference/android/graphics/ImageFormat#YUV_420_888
- init {
- require(y.pixelStride == 1) {
- "Pixel stride for Y plane must be 1 but got ${y.pixelStride} instead."
- }
- require(u.pixelStride == v.pixelStride && u.rowStride == v.rowStride) {
- "U and V planes must have the same pixel and row strides " +
- "but got pixel=${u.pixelStride} row=${u.rowStride} for U " +
- "and pixel=${v.pixelStride} and row=${v.rowStride} for V"
- }
- require(u.pixelStride == 1 || u.pixelStride == 2) {
- "Supported" + " pixel strides for U and V planes are 1 and 2"
- }
- }
- }
-
- private class PlaneWrapper(width: Int, height: Int, plane: Image.Plane) {
- val width = width
- val height = height
- val buffer: ByteBuffer = plane.buffer
- val rowStride = plane.rowStride
- val pixelStride = plane.pixelStride
- }
-}
\ No newline at end of file
diff --git a/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt b/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt
deleted file mode 100644
index 8dcd5596..00000000
--- a/CameraXVideo/utils/src/main/java/com/example/android/camera/utils/YuvToRgbConverter.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2020 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
- *
- * https://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.android.camera.utils
-
-import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.ImageFormat
-import android.media.Image
-import android.renderscript.Allocation
-import android.renderscript.Element
-import android.renderscript.RenderScript
-import android.renderscript.ScriptIntrinsicYuvToRGB
-import android.renderscript.Type
-import java.nio.ByteBuffer
-
-/**
- * Helper class used to convert a [Image] object from
- * [ImageFormat.YUV_420_888] format to an RGB [Bitmap] object, it has equivalent
- * functionality to https://github
- * .com/androidx/androidx/blob/androidx-main/camera/camera-core/src/main/java/androidx/camera/core/ImageYuvToRgbConverter.java
- *
- * NOTE: This has been tested in a limited number of devices and is not
- * considered production-ready code. It was created for illustration purposes,
- * since this is not an efficient camera pipeline due to the multiple copies
- * required to convert each frame. For example, this
- * implementation
- * (https://stackoverflow.com/questions/52726002/camera2-captured-picture-conversion-from-yuv-420-888-to-nv21/52740776#52740776)
- * might have better performance.
- */
-class YuvToRgbConverter(context: Context) {
- private val rs = RenderScript.create(context)
- private val scriptYuvToRgb =
- ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs))
-
- // Do not add getters/setters functions to these private variables
- // because yuvToRgb() assume they won't be modified elsewhere
- private var yuvBits: ByteBuffer? = null
- private var bytes: ByteArray = ByteArray(0)
- private var inputAllocation: Allocation? = null
- private var outputAllocation: Allocation? = null
-
- @Synchronized
- fun yuvToRgb(image: Image, output: Bitmap) {
- val yuvBuffer = YuvByteBuffer(image, yuvBits)
- yuvBits = yuvBuffer.buffer
-
- if (needCreateAllocations(image, yuvBuffer)) {
- val yuvType = Type.Builder(rs, Element.U8(rs))
- .setX(image.width)
- .setY(image.height)
- .setYuvFormat(yuvBuffer.type)
- inputAllocation = Allocation.createTyped(
- rs,
- yuvType.create(),
- Allocation.USAGE_SCRIPT
- )
- bytes = ByteArray(yuvBuffer.buffer.capacity())
- val rgbaType = Type.Builder(rs, Element.RGBA_8888(rs))
- .setX(image.width)
- .setY(image.height)
- outputAllocation = Allocation.createTyped(
- rs,
- rgbaType.create(),
- Allocation.USAGE_SCRIPT
- )
- }
-
- yuvBuffer.buffer.get(bytes)
- inputAllocation!!.copyFrom(bytes)
-
- // Convert NV21 or YUV_420_888 format to RGB
- inputAllocation!!.copyFrom(bytes)
- scriptYuvToRgb.setInput(inputAllocation)
- scriptYuvToRgb.forEach(outputAllocation)
- outputAllocation!!.copyTo(output)
- }
-
- private fun needCreateAllocations(image: Image, yuvBuffer: YuvByteBuffer): Boolean {
- return (inputAllocation == null || // the very 1st call
- inputAllocation!!.type.x != image.width || // image size changed
- inputAllocation!!.type.y != image.height ||
- inputAllocation!!.type.yuv != yuvBuffer.type || // image format changed
- bytes.size == yuvBuffer.buffer.capacity())
- }
-}
diff --git a/CameraXVideo/utils/src/main/res/drawable/ic_shutter.xml b/CameraXVideo/utils/src/main/res/drawable/ic_shutter.xml
deleted file mode 100644
index 9bb91ab8..00000000
--- a/CameraXVideo/utils/src/main/res/drawable/ic_shutter.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CameraXVideo/utils/src/main/res/drawable/ic_shutter_focused.xml b/CameraXVideo/utils/src/main/res/drawable/ic_shutter_focused.xml
deleted file mode 100644
index 9bf521da..00000000
--- a/CameraXVideo/utils/src/main/res/drawable/ic_shutter_focused.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraXVideo/utils/src/main/res/drawable/ic_shutter_normal.xml b/CameraXVideo/utils/src/main/res/drawable/ic_shutter_normal.xml
deleted file mode 100644
index cb50026e..00000000
--- a/CameraXVideo/utils/src/main/res/drawable/ic_shutter_normal.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/CameraXVideo/utils/src/main/res/drawable/ic_shutter_pressed.xml b/CameraXVideo/utils/src/main/res/drawable/ic_shutter_pressed.xml
deleted file mode 100644
index 9bf521da..00000000
--- a/CameraXVideo/utils/src/main/res/drawable/ic_shutter_pressed.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
diff --git a/HdrViewfinder/.google/packaging.yaml b/HdrViewfinder/.google/packaging.yaml
deleted file mode 100644
index 05c3f707..00000000
--- a/HdrViewfinder/.google/packaging.yaml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-# GOOGLE SAMPLE PACKAGING DATA
-#
-# This file is used by Google as part of our samples packaging process.
-# End users may safely ignore this file. It has no relevance to other systems.
----
-status: PUBLISHED
-technologies: [Android]
-categories: [Camera]
-languages: [Java]
-solutions: [Mobile]
-github: android/camera
-level: ADVANCED
-icon: big_icon.png
-apiRefs:
- - android:android.hardware.camera2.CameraAccessException
- - android:android.hardware.camera2.CameraCaptureSession
- - android:android.hardware.camera2.CameraCharacteristics
- - android:android.hardware.camera2.CameraDevice
- - android:android.hardware.camera2.CameraManager
- - android:android.hardware.camera2.CaptureRequest
- - android:android.hardware.camera2.CaptureResult
- - android:android.hardware.camera2.TotalCaptureResult
-license: apache2
diff --git a/HdrViewfinder/.java-version b/HdrViewfinder/.java-version
deleted file mode 100644
index 9d607966..00000000
--- a/HdrViewfinder/.java-version
+++ /dev/null
@@ -1 +0,0 @@
-11
\ No newline at end of file
diff --git a/HdrViewfinder/Application/build.gradle b/HdrViewfinder/Application/build.gradle
deleted file mode 100644
index 9391876a..00000000
--- a/HdrViewfinder/Application/build.gradle
+++ /dev/null
@@ -1,61 +0,0 @@
-
-buildscript {
- repositories {
- google()
- mavenCentral()
- }
-
- dependencies {
- classpath 'com.android.tools.build:gradle:4.2.2'
- }
-}
-
-apply plugin: 'com.android.application'
-
-repositories {
- google()
- mavenCentral()
-}
-
-dependencies {
- implementation "com.android.support:support-v4:28.0.0"
- implementation "com.android.support:support-v13:28.0.0"
- implementation "com.android.support:cardview-v7:28.0.0"
- implementation "com.android.support:appcompat-v7:28.0.0"
- implementation 'com.android.support:design:28.0.0'
-}
-
-// The sample build uses multiple directories to
-// keep boilerplate and common code separate from
-// the main sample code.
-List dirs = [
- 'main', // main sample code; look here for the interesting stuff.
- 'common', // components that are reused by multiple samples
- 'template'] // boilerplate code that is generated by the sample template process
-
-android {
- compileSdkVersion 28
-
- defaultConfig {
- minSdkVersion 21
- targetSdkVersion 28
- }
-
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_7
- targetCompatibility JavaVersion.VERSION_1_7
- }
-
- sourceSets {
- main {
- dirs.each { dir ->
- java.srcDirs "src/${dir}/java"
- res.srcDirs "src/${dir}/res"
- }
- }
- androidTest.setRoot('tests')
- androidTest.java.srcDirs = ['tests/src']
-
- }
-
-}
diff --git a/HdrViewfinder/Application/src/main/AndroidManifest.xml b/HdrViewfinder/Application/src/main/AndroidManifest.xml
deleted file mode 100644
index 63066f6d..00000000
--- a/HdrViewfinder/Application/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/HdrViewfinder/Application/src/main/java/com/example/android/hdrviewfinder/CameraOps.java b/HdrViewfinder/Application/src/main/java/com/example/android/hdrviewfinder/CameraOps.java
deleted file mode 100644
index fb82d8dc..00000000
--- a/HdrViewfinder/Application/src/main/java/com/example/android/hdrviewfinder/CameraOps.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright (C) 2014 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.android.hdrviewfinder;
-
-import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCaptureSession;
-import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraManager;
-import android.hardware.camera2.CaptureRequest;
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.support.annotation.NonNull;
-import android.util.Log;
-import android.view.Surface;
-
-import java.util.List;
-
-/**
- * Simple interface for operating the camera, with major camera operations
- * all performed on a background handler thread.
- */
-public class CameraOps {
-
- private static final String TAG = "CameraOps";
-
- public static final long CAMERA_CLOSE_TIMEOUT = 2000; // ms
-
- private final CameraManager mCameraManager;
- private CameraDevice mCameraDevice;
- private CameraCaptureSession mCameraSession;
- private List mSurfaces;
-
- private final ConditionVariable mCloseWaiter = new ConditionVariable();
-
- private HandlerThread mCameraThread;
- private Handler mCameraHandler;
-
- private final ErrorDisplayer mErrorDisplayer;
-
- private final CameraReadyListener mReadyListener;
- private final Handler mReadyHandler;
-
- /**
- * Create a new camera ops thread.
- *
- * @param errorDisplayer listener for displaying error messages
- * @param readyListener listener for notifying when camera is ready for requests
- * @param readyHandler the handler for calling readyListener methods on
- */
- CameraOps(CameraManager manager, ErrorDisplayer errorDisplayer,
- CameraReadyListener readyListener, Handler readyHandler) {
- mCameraThread = new HandlerThread("CameraOpsThread");
- mCameraThread.start();
-
- if (manager == null || errorDisplayer == null ||
- readyListener == null || readyHandler == null) {
- throw new IllegalArgumentException("Need valid displayer, listener, handler");
- }
-
- mCameraManager = manager;
- mErrorDisplayer = errorDisplayer;
- mReadyListener = readyListener;
- mReadyHandler = readyHandler;
- }
-
- /**
- * Open the first back-facing camera listed by the camera manager.
- * Displays a dialog if it cannot open a camera.
- */
- public void openCamera(final String cameraId) {
- mCameraHandler = new Handler(mCameraThread.getLooper());
-
- mCameraHandler.post(new Runnable() {
- @SuppressWarnings("MissingPermission")
- public void run() {
- if (mCameraDevice != null) {
- throw new IllegalStateException("Camera already open");
- }
- try {
- mCameraManager.openCamera(cameraId, mCameraDeviceListener, mCameraHandler);
- } catch (CameraAccessException e) {
- String errorMessage = mErrorDisplayer.getErrorString(e);
- mErrorDisplayer.showErrorDialog(errorMessage);
- }
- }
- });
- }
-
- /**
- * Close the camera and wait for the close callback to be called in the camera thread.
- * Times out after @{value CAMERA_CLOSE_TIMEOUT} ms.
- */
- public void closeCameraAndWait() {
- mCloseWaiter.close();
- mCameraHandler.post(mCloseCameraRunnable);
- boolean closed = mCloseWaiter.block(CAMERA_CLOSE_TIMEOUT);
- if (!closed) {
- Log.e(TAG, "Timeout closing camera");
- }
- }
-
- private Runnable mCloseCameraRunnable = new Runnable() {
- public void run() {
- if (mCameraDevice != null) {
- mCameraDevice.close();
- }
- mCameraDevice = null;
- mCameraSession = null;
- mSurfaces = null;
- }
- };
-
- /**
- * Set the output Surfaces, and finish configuration if otherwise ready.
- */
- public void setSurfaces(final List surfaces) {
- mCameraHandler.post(new Runnable() {
- public void run() {
- mSurfaces = surfaces;
- startCameraSession();
- }
- });
- }
-
- /**
- * Get a request builder for the current camera.
- */
- public CaptureRequest.Builder createCaptureRequest(int template) throws CameraAccessException {
- CameraDevice device = mCameraDevice;
- if (device == null) {
- throw new IllegalStateException("Can't get requests when no camera is open");
- }
- return device.createCaptureRequest(template);
- }
-
- /**
- * Set a repeating request.
- */
- public void setRepeatingRequest(final CaptureRequest request,
- final CameraCaptureSession.CaptureCallback listener,
- final Handler handler) {
- mCameraHandler.post(new Runnable() {
- public void run() {
- try {
- mCameraSession.setRepeatingRequest(request, listener, handler);
- } catch (CameraAccessException e) {
- String errorMessage = mErrorDisplayer.getErrorString(e);
- mErrorDisplayer.showErrorDialog(errorMessage);
- }
- }
- });
- }
-
- /**
- * Set a repeating request.
- */
- public void setRepeatingBurst(final List requests,
- final CameraCaptureSession.CaptureCallback listener,
- final Handler handler) {
- mCameraHandler.post(new Runnable() {
- public void run() {
- try {
- mCameraSession.setRepeatingBurst(requests, listener, handler);
- } catch (CameraAccessException e) {
- String errorMessage = mErrorDisplayer.getErrorString(e);
- mErrorDisplayer.showErrorDialog(errorMessage);
- }
- }
- });
- }
-
- /**
- * Configure the camera session.
- */
- private void startCameraSession() {
- // Wait until both the camera device is open and the SurfaceView is ready
- if (mCameraDevice == null || mSurfaces == null) return;
-
- try {
- mCameraDevice.createCaptureSession(
- mSurfaces, mCameraSessionListener, mCameraHandler);
- } catch (CameraAccessException e) {
- String errorMessage = mErrorDisplayer.getErrorString(e);
- mErrorDisplayer.showErrorDialog(errorMessage);
- mCameraDevice.close();
- mCameraDevice = null;
- }
- }
-
- /**
- * Main listener for camera session events
- * Invoked on mCameraThread
- */
- private CameraCaptureSession.StateCallback mCameraSessionListener =
- new CameraCaptureSession.StateCallback() {
-
- @Override
- public void onConfigured(@NonNull CameraCaptureSession session) {
- mCameraSession = session;
- mReadyHandler.post(new Runnable() {
- public void run() {
- // This can happen when the screen is turned off and turned back on.
- if (null == mCameraDevice) {
- return;
- }
-
- mReadyListener.onCameraReady();
- }
- });
-
- }
-
- @Override
- public void onConfigureFailed(@NonNull CameraCaptureSession session) {
- mErrorDisplayer.showErrorDialog("Unable to configure the capture session");
- mCameraDevice.close();
- mCameraDevice = null;
- }
- };
-
- /**
- * Main listener for camera device events.
- * Invoked on mCameraThread
- */
- private CameraDevice.StateCallback mCameraDeviceListener = new CameraDevice.StateCallback() {
-
- @Override
- public void onOpened(@NonNull CameraDevice camera) {
- mCameraDevice = camera;
- startCameraSession();
- }
-
- @Override
- public void onClosed(@NonNull CameraDevice camera) {
- mCloseWaiter.open();
- }
-
- @Override
- public void onDisconnected(@NonNull CameraDevice camera) {
- mErrorDisplayer.showErrorDialog("The camera device has been disconnected.");
- camera.close();
- mCameraDevice = null;
- }
-
- @Override
- public void onError(@NonNull CameraDevice camera, int error) {
- mErrorDisplayer.showErrorDialog("The camera encountered an error:" + error);
- camera.close();
- mCameraDevice = null;
- }
-
- };
-
- /**
- * Simple listener for main code to know the camera is ready for requests, or failed to
- * start.
- */
- public interface CameraReadyListener {
- void onCameraReady();
- }
-
- /**
- * Simple listener for displaying error messages
- */
- public interface ErrorDisplayer {
- void showErrorDialog(String errorMessage);
-
- String getErrorString(CameraAccessException e);
- }
-
-}
diff --git a/HdrViewfinder/Application/src/main/java/com/example/android/hdrviewfinder/FixedAspectSurfaceView.java b/HdrViewfinder/Application/src/main/java/com/example/android/hdrviewfinder/FixedAspectSurfaceView.java
deleted file mode 100644
index 30ec26ed..00000000
--- a/HdrViewfinder/Application/src/main/java/com/example/android/hdrviewfinder/FixedAspectSurfaceView.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2014 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.android.hdrviewfinder;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.SurfaceView;
-import android.view.View;
-
-/**
- * A SurfaceView that maintains its aspect ratio to be a desired target value.
- *
- *
Depending on the layout, the FixedAspectSurfaceView may not be able to maintain the
- * requested aspect ratio. This can happen if both the width and the height are exactly
- * determined by the layout. To avoid this, ensure that either the height or the width is
- * adjustable by the view; for example, by setting the layout parameters to be WRAP_CONTENT for
- * the dimension that is best adjusted to maintain the aspect ratio.
- */
-public class FixedAspectSurfaceView extends SurfaceView {
-
- /**
- * Desired width/height ratio
- */
- private float mAspectRatio;
-
- private GestureDetector mGestureDetector;
-
- public FixedAspectSurfaceView(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- // Get initial aspect ratio from custom attributes
- TypedArray a =
- context.getTheme().obtainStyledAttributes(attrs,
- R.styleable.FixedAspectSurfaceView, 0, 0);
- setAspectRatio(a.getFloat(
- R.styleable.FixedAspectSurfaceView_aspectRatio, 1.f));
- a.recycle();
- }
-
- /**
- * Set the desired aspect ratio for this view.
- *
- * @param aspect the desired width/height ratio in the current UI orientation. Must be a
- * positive value.
- */
- public void setAspectRatio(float aspect) {
- if (aspect <= 0) {
- throw new IllegalArgumentException("Aspect ratio must be positive");
- }
- mAspectRatio = aspect;
- requestLayout();
- }
-
- /**
- * Set a gesture listener to listen for touch events
- */
- public void setGestureListener(Context context, GestureDetector.OnGestureListener listener) {
- if (listener == null) {
- mGestureDetector = null;
- } else {
- mGestureDetector = new GestureDetector(context, listener);
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-
- int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- int width = MeasureSpec.getSize(widthMeasureSpec);
- int height = MeasureSpec.getSize(heightMeasureSpec);
-
- // General goal: Adjust dimensions to maintain the requested aspect ratio as much
- // as possible. Depending on the measure specs handed down, this may not be possible
-
- // Only set one of these to true
- boolean scaleWidth = false;
- boolean scaleHeight = false;
-
- // Sort out which dimension to scale, if either can be. There are 9 combinations of
- // possible measure specs; a few cases below handle multiple combinations
- //noinspection StatementWithEmptyBody
- if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
- // Can't adjust sizes at all, do nothing
- } else if (widthMode == MeasureSpec.EXACTLY) {
- // Width is fixed, heightMode either AT_MOST or UNSPECIFIED, so adjust height
- scaleHeight = true;
- } else if (heightMode == MeasureSpec.EXACTLY) {
- // Height is fixed, widthMode either AT_MOST or UNSPECIFIED, so adjust width
- scaleWidth = true;
- } else if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
- // Need to fit into box <= [width, height] in size.
- // Maximize the View's area while maintaining aspect ratio
- // This means keeping one dimension as large as possible and shrinking the other
- float boxAspectRatio = width / (float) height;
- if (boxAspectRatio > mAspectRatio) {
- // Box is wider than requested aspect; pillarbox
- scaleWidth = true;
- } else {
- // Box is narrower than requested aspect; letterbox
- scaleHeight = true;
- }
- } else if (widthMode == MeasureSpec.AT_MOST) {
- // Maximize width, heightSpec is UNSPECIFIED
- scaleHeight = true;
- } else if (heightMode == MeasureSpec.AT_MOST) {
- // Maximize height, widthSpec is UNSPECIFIED
- scaleWidth = true;
- } else {
- // Both MeasureSpecs are UNSPECIFIED. This is probably a pathological layout,
- // with width == height == 0
- // but arbitrarily scale height anyway
- scaleHeight = true;
- }
-
- // Do the scaling
- if (scaleWidth) {
- width = (int) (height * mAspectRatio);
- } else if (scaleHeight) {
- height = (int) (width / mAspectRatio);
- }
-
- // Override width/height if needed for EXACTLY and AT_MOST specs
- width = View.resolveSizeAndState(width, widthMeasureSpec, 0);
- height = View.resolveSizeAndState(height, heightMeasureSpec, 0);
-
- // Finally set the calculated dimensions
- setMeasuredDimension(width, height);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- return mGestureDetector != null && mGestureDetector.onTouchEvent(event);
- }
-}
diff --git a/HdrViewfinder/Application/src/main/java/com/example/android/hdrviewfinder/HdrViewfinderActivity.java b/HdrViewfinder/Application/src/main/java/com/example/android/hdrviewfinder/HdrViewfinderActivity.java
deleted file mode 100644
index bce6767e..00000000
--- a/HdrViewfinder/Application/src/main/java/com/example/android/hdrviewfinder/HdrViewfinderActivity.java
+++ /dev/null
@@ -1,672 +0,0 @@
-/*
- * Copyright (C) 2014 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.android.hdrviewfinder;
-
-import android.Manifest;
-import android.annotation.SuppressLint;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-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.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.Settings;
-import android.renderscript.RenderScript;
-import android.support.annotation.NonNull;
-import android.support.design.widget.Snackbar;
-import android.support.v4.app.ActivityCompat;
-import android.support.v7.app.AppCompatActivity;
-import android.util.Log;
-import android.util.Size;
-import android.view.GestureDetector;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.View;
-import android.widget.Button;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-
-/**
- * A small demo of advanced camera functionality with the Android camera2 API.
- *
- *
This demo implements a real-time high-dynamic-range camera viewfinder,
- * by alternating the sensor's exposure time between two exposure values on even and odd
- * frames, and then compositing together the latest two frames whenever a new frame is
- * captured.
- *
- *
The demo has three modes: Regular auto-exposure viewfinder, split-screen manual exposure,
- * and the fused HDR viewfinder. The latter two use manual exposure controlled by the user,
- * by swiping up/down on the right and left halves of the viewfinder. The left half controls
- * the exposure time of even frames, and the right half controls the exposure time of odd frames.
- *
- *
- *
In split-screen mode, the even frames are shown on the left and the odd frames on the right,
- * so the user can see two different exposures of the scene simultaneously. In fused HDR mode,
- * the even/odd frames are merged together into a single image. By selecting different exposure
- * values for the even/odd frames, the fused image has a higher dynamic range than the regular
- * viewfinder.
- *
- *
The HDR fusion and the split-screen viewfinder processing is done with RenderScript; as is the
- * necessary YUV->RGB conversion. The camera subsystem outputs YUV images naturally, while the GPU
- * and display subsystems generally only accept RGB data. Therefore, after the images are
- * fused/composited, a standard YUV->RGB color transform is applied before the the data is written
- * to the output Allocation. The HDR fusion algorithm is very simple, and tends to result in
- * lower-contrast scenes, but has very few artifacts and can run very fast.
- *
- *
Data is passed between the subsystems (camera, RenderScript, and display) using the
- * Android {@link android.view.Surface} class, which allows for zero-copy transport of large
- * buffers between processes and subsystems.
- */
-public class HdrViewfinderActivity extends AppCompatActivity implements
- SurfaceHolder.Callback, CameraOps.ErrorDisplayer, CameraOps.CameraReadyListener {
-
- private static final String TAG = "HdrViewfinderDemo";
-
- private static final String FRAGMENT_DIALOG = "dialog";
-
- private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
-
- /**
- * View for the camera preview.
- */
- private FixedAspectSurfaceView mPreviewView;
-
- /**
- * Root view of this activity.
- */
- private View rootView;
-
- /**
- * This shows the current mode of the app.
- */
- private TextView mModeText;
-
- // These show lengths of exposure for even frames, exposure for odd frames, and auto exposure.
- private TextView mEvenExposureText, mOddExposureText, mAutoExposureText;
-
- private Handler mUiHandler;
-
- private CameraCharacteristics mCameraInfo;
-
- private Surface mPreviewSurface;
- private Surface mProcessingHdrSurface;
- private Surface mProcessingNormalSurface;
- CaptureRequest.Builder mHdrBuilder;
- ArrayList mHdrRequests = new ArrayList<>(2);
-
- CaptureRequest mPreviewRequest;
-
- RenderScript mRS;
- ViewfinderProcessor mProcessor;
- CameraManager mCameraManager;
- CameraOps mCameraOps;
-
- private int mRenderMode = ViewfinderProcessor.MODE_NORMAL;
-
- // Durations in nanoseconds
- private static final long MICRO_SECOND = 1000;
- private static final long MILLI_SECOND = MICRO_SECOND * 1000;
- private static final long ONE_SECOND = MILLI_SECOND * 1000;
-
- private long mOddExposure = ONE_SECOND / 33;
- private long mEvenExposure = ONE_SECOND / 33;
-
- private Object mOddExposureTag = new Object();
- private Object mEvenExposureTag = new Object();
- private Object mAutoExposureTag = new Object();
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
- rootView = findViewById(R.id.panels);
-
- mPreviewView = (FixedAspectSurfaceView) findViewById(R.id.preview);
- mPreviewView.getHolder().addCallback(this);
- mPreviewView.setGestureListener(this, mViewListener);
-
- Button helpButton = (Button) findViewById(R.id.help_button);
- helpButton.setOnClickListener(mHelpButtonListener);
-
- mModeText = (TextView) findViewById(R.id.mode_label);
- mEvenExposureText = (TextView) findViewById(R.id.even_exposure);
- mOddExposureText = (TextView) findViewById(R.id.odd_exposure);
- mAutoExposureText = (TextView) findViewById(R.id.auto_exposure);
-
- mUiHandler = new Handler(Looper.getMainLooper());
-
- mRS = RenderScript.create(this);
-
- // When permissions are revoked the app is restarted so onCreate is sufficient to check for
- // permissions core to the Activity's functionality.
- if (!checkCameraPermissions()) {
- requestCameraPermissions();
- } else {
- findAndOpenCamera();
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
-
- // Wait until camera is closed to ensure the next application can open it
- if (mCameraOps != null) {
- mCameraOps.closeCameraAndWait();
- mCameraOps = null;
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.main, menu);
- return super.onCreateOptionsMenu(menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.info: {
- MessageDialogFragment.newInstance(R.string.intro_message)
- .show(getSupportFragmentManager(), FRAGMENT_DIALOG);
- break;
- }
- }
- return super.onOptionsItemSelected(item);
- }
-
- private GestureDetector.OnGestureListener mViewListener
- = new GestureDetector.SimpleOnGestureListener() {
-
- @Override
- public boolean onDown(MotionEvent e) {
- return true;
- }
-
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- switchRenderMode(1);
- return true;
- }
-
- @Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- if (mRenderMode == ViewfinderProcessor.MODE_NORMAL) return false;
-
- float xPosition = e1.getAxisValue(MotionEvent.AXIS_X);
- float width = mPreviewView.getWidth();
- float height = mPreviewView.getHeight();
-
- float xPosNorm = xPosition / width;
- float yDistNorm = distanceY / height;
-
- final float ACCELERATION_FACTOR = 8;
- double scaleFactor = Math.pow(2.f, yDistNorm * ACCELERATION_FACTOR);
-
- // Even on left, odd on right
- if (xPosNorm > 0.5) {
- mOddExposure *= scaleFactor;
- } else {
- mEvenExposure *= scaleFactor;
- }
-
- setHdrBurst();
-
- return true;
- }
- };
-
- /**
- * Show help dialogs.
- */
- private View.OnClickListener mHelpButtonListener = new View.OnClickListener() {
- public void onClick(View v) {
- MessageDialogFragment.newInstance(R.string.help_text)
- .show(getSupportFragmentManager(), FRAGMENT_DIALOG);
- }
- };
-
- /**
- * Return the current state of the camera permissions.
- */
- private boolean checkCameraPermissions() {
- int permissionState = ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
-
- // Check if the Camera permission is already available.
- if (permissionState != PackageManager.PERMISSION_GRANTED) {
- // Camera permission has not been granted.
- Log.i(TAG, "CAMERA permission has NOT been granted.");
- return false;
- } else {
- // Camera permissions are available.
- Log.i(TAG, "CAMERA permission has already been granted.");
- return true;
- }
- }
-
- /**
- * Attempt to initialize the camera.
- */
- private void initializeCamera() {
- mCameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);
- if (mCameraManager != null) {
- mCameraOps = new CameraOps(mCameraManager,
- /*errorDisplayer*/ this,
- /*readyListener*/ this,
- /*readyHandler*/ mUiHandler);
-
- mHdrRequests.add(null);
- mHdrRequests.add(null);
- } else {
- Log.e(TAG, "Couldn't initialize the camera");
- }
- }
-
- private void requestCameraPermissions() {
- // Provide an additional rationale to the user. This would happen if the user denied the
- // request previously, but didn't check the "Don't ask again" checkbox.
- if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
- Log.i(TAG, "Displaying camera permission rationale to provide additional context.");
- Snackbar.make(rootView, R.string.camera_permission_rationale, Snackbar
- .LENGTH_INDEFINITE)
- .setAction(R.string.ok, new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- // Request Camera permission
- ActivityCompat.requestPermissions(HdrViewfinderActivity.this,
- new String[]{Manifest.permission.CAMERA},
- REQUEST_PERMISSIONS_REQUEST_CODE);
- }
- })
- .show();
- } else {
- Log.i(TAG, "Requesting camera permission");
- // Request Camera permission. It's possible this can be auto answered if device policy
- // sets the permission in a given state or the user denied the permission
- // previously and checked "Never ask again".
- ActivityCompat.requestPermissions(HdrViewfinderActivity.this,
- new String[]{Manifest.permission.CAMERA},
- REQUEST_PERMISSIONS_REQUEST_CODE);
- }
- }
-
- /**
- * Callback received when a permissions request has been completed.
- */
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
- @NonNull int[] grantResults) {
- Log.i(TAG, "onRequestPermissionResult");
- if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
- if (grantResults.length <= 0) {
- // If user interaction was interrupted, the permission request is cancelled and you
- // receive empty arrays.
- Log.i(TAG, "User interaction was cancelled.");
- } else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- // Permission was granted.
- findAndOpenCamera();
- } else {
- // Permission denied.
-
- // In this Activity we've chosen to notify the user that they
- // have rejected a core permission for the app since it makes the Activity useless.
- // We're communicating this message in a Snackbar since this is a sample app, but
- // core permissions would typically be best requested during a welcome-screen flow.
-
- // Additionally, it is important to remember that a permission might have been
- // rejected without asking the user for permission (device policy or "Never ask
- // again" prompts). Therefore, a user interface affordance is typically implemented
- // when permissions are denied. Otherwise, your app could appear unresponsive to
- // touches or interactions which have required permissions.
- Snackbar.make(rootView, R.string.camera_permission_denied_explanation, Snackbar
- .LENGTH_INDEFINITE)
- .setAction(R.string.settings, new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- // Build intent that displays the App settings screen.
- Intent intent = new Intent();
- intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
- Uri uri = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null);
- intent.setData(uri);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- }
- })
- .show();
- }
- }
- }
-
- private void findAndOpenCamera() {
- boolean cameraPermissions = checkCameraPermissions();
- if (!cameraPermissions) {
- return;
- }
- String errorMessage = "Unknown error";
- boolean foundCamera = false;
- initializeCamera();
- if (mCameraOps != null) {
- try {
- // Find first back-facing camera that has necessary capability.
- String[] cameraIds = mCameraManager.getCameraIdList();
- for (String id : cameraIds) {
- CameraCharacteristics info = mCameraManager.getCameraCharacteristics(id);
- Integer facing = info.get(CameraCharacteristics.LENS_FACING);
- Integer level = info.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
- boolean hasFullLevel = Objects.equals(level,
- CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
-
- int[] capabilities = info
- .get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
- Integer syncLatency = info.get(CameraCharacteristics.SYNC_MAX_LATENCY);
- boolean hasManualControl = hasCapability(capabilities,
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR);
- boolean hasEnoughCapability = hasManualControl && Objects.equals(syncLatency,
- CameraCharacteristics.SYNC_MAX_LATENCY_PER_FRAME_CONTROL);
-
- // All these are guaranteed by
- // CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, but checking
- // for only the things we care about expands range of devices we can run on.
- // We want:
- // - Back-facing camera
- // - Manual sensor control
- // - Per-frame synchronization (so that exposure can be changed every frame)
- if (Objects.equals(facing, CameraCharacteristics.LENS_FACING_BACK) &&
- (hasFullLevel || hasEnoughCapability)) {
- // Found suitable camera - get info, open, and set up outputs
- mCameraInfo = info;
- mCameraOps.openCamera(id);
- configureSurfaces();
- foundCamera = true;
- break;
- }
- }
- if (!foundCamera) {
- errorMessage = getString(R.string.camera_no_good);
- }
- } catch (CameraAccessException e) {
- errorMessage = getErrorString(e);
- }
- if (!foundCamera) {
- showErrorDialog(errorMessage);
- }
- }
- }
-
- private boolean hasCapability(int[] capabilities, int capability) {
- for (int c : capabilities) {
- if (c == capability) return true;
- }
- return false;
- }
-
- private void switchRenderMode(int direction) {
- if (mCameraOps != null) {
- mRenderMode = (mRenderMode + direction) % 3;
-
- mModeText.setText(getResources().getStringArray(R.array.mode_label_array)[mRenderMode]);
-
- if (mProcessor != null) {
- mProcessor.setRenderMode(mRenderMode);
- }
- if (mRenderMode == ViewfinderProcessor.MODE_NORMAL) {
- mCameraOps.setRepeatingRequest(mPreviewRequest,
- mCaptureCallback, mUiHandler);
- } else {
- setHdrBurst();
- }
- }
- }
-
- /**
- * Configure the surfaceview and RS processing.
- */
- private void configureSurfaces() {
- // Find a good size for output - largest 16:9 aspect ratio that's less than 720p
- final int MAX_WIDTH = 1280;
- final float TARGET_ASPECT = 16.f / 9.f;
- final float ASPECT_TOLERANCE = 0.1f;
-
- StreamConfigurationMap configs =
- mCameraInfo.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
- if (configs == null) {
- throw new RuntimeException("Cannot get available picture/preview sizes.");
- }
- Size[] outputSizes = configs.getOutputSizes(SurfaceHolder.class);
-
- Size outputSize = outputSizes[0];
- float outputAspect = (float) outputSize.getWidth() / outputSize.getHeight();
- for (Size candidateSize : outputSizes) {
- if (candidateSize.getWidth() > MAX_WIDTH) continue;
- float candidateAspect = (float) candidateSize.getWidth() / candidateSize.getHeight();
- boolean goodCandidateAspect =
- Math.abs(candidateAspect - TARGET_ASPECT) < ASPECT_TOLERANCE;
- boolean goodOutputAspect =
- Math.abs(outputAspect - TARGET_ASPECT) < ASPECT_TOLERANCE;
- if ((goodCandidateAspect && !goodOutputAspect) ||
- candidateSize.getWidth() > outputSize.getWidth()) {
- outputSize = candidateSize;
- outputAspect = candidateAspect;
- }
- }
- Log.i(TAG, "Resolution chosen: " + outputSize);
-
- // Configure processing
- mProcessor = new ViewfinderProcessor(mRS, outputSize);
- setupProcessor();
-
- // Configure the output view - this will fire surfaceChanged
- mPreviewView.setAspectRatio(outputAspect);
- mPreviewView.getHolder().setFixedSize(outputSize.getWidth(), outputSize.getHeight());
- }
-
- /**
- * Once camera is open and output surfaces are ready, configure the RS processing
- * and the camera device inputs/outputs.
- */
- private void setupProcessor() {
- if (mProcessor == null || mPreviewSurface == null) return;
-
- mProcessor.setOutputSurface(mPreviewSurface);
- mProcessingHdrSurface = mProcessor.getInputHdrSurface();
- mProcessingNormalSurface = mProcessor.getInputNormalSurface();
-
- List cameraOutputSurfaces = new ArrayList<>();
- cameraOutputSurfaces.add(mProcessingHdrSurface);
- cameraOutputSurfaces.add(mProcessingNormalSurface);
-
- mCameraOps.setSurfaces(cameraOutputSurfaces);
- }
-
- /**
- * Start running an HDR burst on a configured camera session
- */
- public void setHdrBurst() {
-
- mHdrBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, 1600);
- mHdrBuilder.set(CaptureRequest.SENSOR_FRAME_DURATION, ONE_SECOND / 30);
-
- mHdrBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, mEvenExposure);
- mHdrBuilder.setTag(mEvenExposureTag);
- mHdrRequests.set(0, mHdrBuilder.build());
-
- mHdrBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, mOddExposure);
- mHdrBuilder.setTag(mOddExposureTag);
- mHdrRequests.set(1, mHdrBuilder.build());
-
- mCameraOps.setRepeatingBurst(mHdrRequests, mCaptureCallback, mUiHandler);
- }
-
- /**
- * Listener for completed captures
- * Invoked on UI thread
- */
- private CameraCaptureSession.CaptureCallback mCaptureCallback
- = new CameraCaptureSession.CaptureCallback() {
-
- public void onCaptureCompleted(@NonNull CameraCaptureSession session,
- @NonNull CaptureRequest request,
- @NonNull TotalCaptureResult result) {
-
- // Only update UI every so many frames
- // Use an odd number here to ensure both even and odd exposures get an occasional update
- long frameNumber = result.getFrameNumber();
- if (frameNumber % 3 != 0) return;
-
- final Long exposureTime = result.get(CaptureResult.SENSOR_EXPOSURE_TIME);
- if (exposureTime == null) {
- throw new RuntimeException("Cannot get exposure time.");
- }
-
- // Format exposure time nicely
- String exposureText;
- if (exposureTime > ONE_SECOND) {
- exposureText = String.format(Locale.US, "%.2f s", exposureTime / 1e9);
- } else if (exposureTime > MILLI_SECOND) {
- exposureText = String.format(Locale.US, "%.2f ms", exposureTime / 1e6);
- } else if (exposureTime > MICRO_SECOND) {
- exposureText = String.format(Locale.US, "%.2f us", exposureTime / 1e3);
- } else {
- exposureText = String.format(Locale.US, "%d ns", exposureTime);
- }
-
- Object tag = request.getTag();
- Log.i(TAG, "Exposure: " + exposureText);
-
- if (tag == mEvenExposureTag) {
- mEvenExposureText.setText(exposureText);
-
- mEvenExposureText.setEnabled(true);
- mOddExposureText.setEnabled(true);
- mAutoExposureText.setEnabled(false);
- } else if (tag == mOddExposureTag) {
- mOddExposureText.setText(exposureText);
-
- mEvenExposureText.setEnabled(true);
- mOddExposureText.setEnabled(true);
- mAutoExposureText.setEnabled(false);
- } else {
- mAutoExposureText.setText(exposureText);
-
- mEvenExposureText.setEnabled(false);
- mOddExposureText.setEnabled(false);
- mAutoExposureText.setEnabled(true);
- }
- }
- };
-
- /**
- * Callbacks for the FixedAspectSurfaceView
- */
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- mPreviewSurface = holder.getSurface();
-
- setupProcessor();
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- // ignored
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- mPreviewSurface = null;
- }
-
- /**
- * Callbacks for CameraOps
- */
- @Override
- public void onCameraReady() {
- // Ready to send requests in, so set them up
- try {
- CaptureRequest.Builder previewBuilder =
- mCameraOps.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
- previewBuilder.addTarget(mProcessingNormalSurface);
- previewBuilder.setTag(mAutoExposureTag);
- mPreviewRequest = previewBuilder.build();
-
- mHdrBuilder =
- mCameraOps.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
- mHdrBuilder.set(CaptureRequest.CONTROL_AE_MODE,
- CaptureRequest.CONTROL_AE_MODE_OFF);
- mHdrBuilder.addTarget(mProcessingHdrSurface);
-
- switchRenderMode(0);
-
- } catch (CameraAccessException e) {
- String errorMessage = getErrorString(e);
- showErrorDialog(errorMessage);
- }
- }
-
- /**
- * Utility methods
- */
- @Override
- public void showErrorDialog(String errorMessage) {
- MessageDialogFragment.newInstance(errorMessage)
- .show(getSupportFragmentManager(), FRAGMENT_DIALOG);
- }
-
- @SuppressLint({"SwitchIntDef", "StringFormatMatches"})
- @Override
- public String getErrorString(CameraAccessException e) {
- String errorMessage;
- switch (e.getReason()) {
- case CameraAccessException.CAMERA_DISABLED:
- errorMessage = getString(R.string.camera_disabled);
- break;
- case CameraAccessException.CAMERA_DISCONNECTED:
- errorMessage = getString(R.string.camera_disconnected);
- break;
- case CameraAccessException.CAMERA_ERROR:
- errorMessage = getString(R.string.camera_error);
- break;
- default:
- errorMessage = getString(R.string.camera_unknown, e.getReason());
- break;
- }
- return errorMessage;
- }
-
-}
diff --git a/HdrViewfinder/Application/src/main/java/com/example/android/hdrviewfinder/MessageDialogFragment.java b/HdrViewfinder/Application/src/main/java/com/example/android/hdrviewfinder/MessageDialogFragment.java
deleted file mode 100644
index 6e597f2d..00000000
--- a/HdrViewfinder/Application/src/main/java/com/example/android/hdrviewfinder/MessageDialogFragment.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2014 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.android.hdrviewfinder;
-
-import android.app.Dialog;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.AppCompatDialogFragment;
-
-public class MessageDialogFragment extends AppCompatDialogFragment {
-
- private static final String ARG_MESSAGE_INT = "message_int";
- private static final String ARG_MESSAGE_STRING = "message_string";
-
- public static MessageDialogFragment newInstance(int message) {
- MessageDialogFragment fragment = new MessageDialogFragment();
- Bundle args = new Bundle();
- args.putInt(ARG_MESSAGE_INT, message);
- fragment.setArguments(args);
- return fragment;
- }
-
- public static MessageDialogFragment newInstance(String message) {
- MessageDialogFragment fragment = new MessageDialogFragment();
- Bundle args = new Bundle();
- args.putString(ARG_MESSAGE_STRING, message);
- fragment.setArguments(args);
- return fragment;
- }
-
- @NonNull
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- builder.setPositiveButton(android.R.string.ok, null);
- Bundle args = getArguments();
- if (args.containsKey(ARG_MESSAGE_INT)) {
- builder.setMessage(args.getInt(ARG_MESSAGE_INT));
- } else if (args.containsKey(ARG_MESSAGE_STRING)) {
- builder.setMessage(args.getString(ARG_MESSAGE_STRING));
- }
- return builder.create();
- }
-
-}
diff --git a/HdrViewfinder/Application/src/main/java/com/example/android/hdrviewfinder/ViewfinderProcessor.java b/HdrViewfinder/Application/src/main/java/com/example/android/hdrviewfinder/ViewfinderProcessor.java
deleted file mode 100644
index cb0c77d3..00000000
--- a/HdrViewfinder/Application/src/main/java/com/example/android/hdrviewfinder/ViewfinderProcessor.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2014 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.android.hdrviewfinder;
-
-import android.graphics.ImageFormat;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.RenderScript;
-import android.renderscript.Type;
-import android.util.Size;
-import android.view.Surface;
-
-/**
- * Renderscript-based merger for an HDR viewfinder
- */
-public class ViewfinderProcessor {
-
- private Allocation mInputHdrAllocation;
- private Allocation mInputNormalAllocation;
- private Allocation mPrevAllocation;
- private Allocation mOutputAllocation;
-
- private Handler mProcessingHandler;
- private ScriptC_hdr_merge mHdrMergeScript;
-
- public ProcessingTask mHdrTask;
- public ProcessingTask mNormalTask;
-
- private int mMode;
-
- public final static int MODE_NORMAL = 0;
- public final static int MODE_HDR = 2;
-
- public ViewfinderProcessor(RenderScript rs, Size dimensions) {
- Type.Builder yuvTypeBuilder = new Type.Builder(rs, Element.YUV(rs));
- yuvTypeBuilder.setX(dimensions.getWidth());
- yuvTypeBuilder.setY(dimensions.getHeight());
- yuvTypeBuilder.setYuvFormat(ImageFormat.YUV_420_888);
- mInputHdrAllocation = Allocation.createTyped(rs, yuvTypeBuilder.create(),
- Allocation.USAGE_IO_INPUT | Allocation.USAGE_SCRIPT);
- mInputNormalAllocation = Allocation.createTyped(rs, yuvTypeBuilder.create(),
- Allocation.USAGE_IO_INPUT | Allocation.USAGE_SCRIPT);
-
- Type.Builder rgbTypeBuilder = new Type.Builder(rs, Element.RGBA_8888(rs));
- rgbTypeBuilder.setX(dimensions.getWidth());
- rgbTypeBuilder.setY(dimensions.getHeight());
- mPrevAllocation = Allocation.createTyped(rs, rgbTypeBuilder.create(),
- Allocation.USAGE_SCRIPT);
- mOutputAllocation = Allocation.createTyped(rs, rgbTypeBuilder.create(),
- Allocation.USAGE_IO_OUTPUT | Allocation.USAGE_SCRIPT);
-
- HandlerThread processingThread = new HandlerThread("ViewfinderProcessor");
- processingThread.start();
- mProcessingHandler = new Handler(processingThread.getLooper());
-
- mHdrMergeScript = new ScriptC_hdr_merge(rs);
-
- mHdrMergeScript.set_gPrevFrame(mPrevAllocation);
-
- mHdrTask = new ProcessingTask(mInputHdrAllocation, dimensions.getWidth()/2, true);
- mNormalTask = new ProcessingTask(mInputNormalAllocation, 0, false);
-
- setRenderMode(MODE_NORMAL);
- }
-
- public Surface getInputHdrSurface() {
- return mInputHdrAllocation.getSurface();
- }
-
- public Surface getInputNormalSurface() {
- return mInputNormalAllocation.getSurface();
- }
-
- public void setOutputSurface(Surface output) {
- mOutputAllocation.setSurface(output);
- }
-
- public void setRenderMode(int mode) {
- mMode = mode;
- }
-
- /**
- * Simple class to keep track of incoming frame count,
- * and to process the newest one in the processing thread
- */
- class ProcessingTask implements Runnable, Allocation.OnBufferAvailableListener {
- private int mPendingFrames = 0;
- private int mFrameCounter = 0;
- private int mCutPointX;
- private boolean mCheckMerge;
-
- private Allocation mInputAllocation;
-
- public ProcessingTask(Allocation input, int cutPointX, boolean checkMerge) {
- mInputAllocation = input;
- mInputAllocation.setOnBufferAvailableListener(this);
- mCutPointX = cutPointX;
- mCheckMerge = checkMerge;
- }
-
- @Override
- public void onBufferAvailable(Allocation a) {
- synchronized(this) {
- mPendingFrames++;
- mProcessingHandler.post(this);
- }
- }
-
- @Override
- public void run() {
-
- // Find out how many frames have arrived
- int pendingFrames;
- synchronized(this) {
- pendingFrames = mPendingFrames;
- mPendingFrames = 0;
-
- // Discard extra messages in case processing is slower than frame rate
- mProcessingHandler.removeCallbacks(this);
- }
-
- // Get to newest input
- for (int i = 0; i < pendingFrames; i++) {
- mInputAllocation.ioReceive();
- }
-
- mHdrMergeScript.set_gFrameCounter(mFrameCounter++);
- mHdrMergeScript.set_gCurrentFrame(mInputAllocation);
- mHdrMergeScript.set_gCutPointX(mCutPointX);
- if (mCheckMerge && mMode == MODE_HDR) {
- mHdrMergeScript.set_gDoMerge(1);
- } else {
- mHdrMergeScript.set_gDoMerge(0);
- }
-
- // Run processing pass
- mHdrMergeScript.forEach_mergeHdrFrames(mPrevAllocation, mOutputAllocation);
- mOutputAllocation.ioSend();
- }
- }
-
-}
diff --git a/HdrViewfinder/Application/src/main/res/drawable-hdpi/ic_action_info.png b/HdrViewfinder/Application/src/main/res/drawable-hdpi/ic_action_info.png
deleted file mode 100644
index 32bd1aab..00000000
Binary files a/HdrViewfinder/Application/src/main/res/drawable-hdpi/ic_action_info.png and /dev/null differ
diff --git a/HdrViewfinder/Application/src/main/res/drawable-hdpi/ic_launcher.png b/HdrViewfinder/Application/src/main/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 18f79c13..00000000
Binary files a/HdrViewfinder/Application/src/main/res/drawable-hdpi/ic_launcher.png and /dev/null differ
diff --git a/HdrViewfinder/Application/src/main/res/drawable-hdpi/tile.9.png b/HdrViewfinder/Application/src/main/res/drawable-hdpi/tile.9.png
deleted file mode 100644
index 13586288..00000000
Binary files a/HdrViewfinder/Application/src/main/res/drawable-hdpi/tile.9.png and /dev/null differ
diff --git a/HdrViewfinder/Application/src/main/res/drawable-mdpi/ic_action_info.png b/HdrViewfinder/Application/src/main/res/drawable-mdpi/ic_action_info.png
deleted file mode 100644
index 8efbbf8b..00000000
Binary files a/HdrViewfinder/Application/src/main/res/drawable-mdpi/ic_action_info.png and /dev/null differ
diff --git a/HdrViewfinder/Application/src/main/res/drawable-mdpi/ic_launcher.png b/HdrViewfinder/Application/src/main/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index e70a7dc9..00000000
Binary files a/HdrViewfinder/Application/src/main/res/drawable-mdpi/ic_launcher.png and /dev/null differ
diff --git a/HdrViewfinder/Application/src/main/res/drawable-xhdpi/ic_action_info.png b/HdrViewfinder/Application/src/main/res/drawable-xhdpi/ic_action_info.png
deleted file mode 100644
index ba143ea7..00000000
Binary files a/HdrViewfinder/Application/src/main/res/drawable-xhdpi/ic_action_info.png and /dev/null differ
diff --git a/HdrViewfinder/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/HdrViewfinder/Application/src/main/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index ed25c5a7..00000000
Binary files a/HdrViewfinder/Application/src/main/res/drawable-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/HdrViewfinder/Application/src/main/res/drawable-xxhdpi/ic_action_info.png b/HdrViewfinder/Application/src/main/res/drawable-xxhdpi/ic_action_info.png
deleted file mode 100644
index 394eb7e5..00000000
Binary files a/HdrViewfinder/Application/src/main/res/drawable-xxhdpi/ic_action_info.png and /dev/null differ
diff --git a/HdrViewfinder/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/HdrViewfinder/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index 90867ffe..00000000
Binary files a/HdrViewfinder/Application/src/main/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/HdrViewfinder/Application/src/main/res/layout/main.xml b/HdrViewfinder/Application/src/main/res/layout/main.xml
deleted file mode 100644
index 6fe56ef0..00000000
--- a/HdrViewfinder/Application/src/main/res/layout/main.xml
+++ /dev/null
@@ -1,100 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/HdrViewfinder/Application/src/main/res/menu/main.xml b/HdrViewfinder/Application/src/main/res/menu/main.xml
deleted file mode 100644
index f21299a1..00000000
--- a/HdrViewfinder/Application/src/main/res/menu/main.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
diff --git a/HdrViewfinder/Application/src/main/res/values-sw600dp/template-dimens.xml b/HdrViewfinder/Application/src/main/res/values-sw600dp/template-dimens.xml
deleted file mode 100644
index 22074a2b..00000000
--- a/HdrViewfinder/Application/src/main/res/values-sw600dp/template-dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
- @dimen/margin_huge
- @dimen/margin_medium
-
-
diff --git a/HdrViewfinder/Application/src/main/res/values-sw600dp/template-styles.xml b/HdrViewfinder/Application/src/main/res/values-sw600dp/template-styles.xml
deleted file mode 100644
index 03d19741..00000000
--- a/HdrViewfinder/Application/src/main/res/values-sw600dp/template-styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
diff --git a/HdrViewfinder/Application/src/main/res/values-v11/template-styles.xml b/HdrViewfinder/Application/src/main/res/values-v11/template-styles.xml
deleted file mode 100644
index 8c1ea66f..00000000
--- a/HdrViewfinder/Application/src/main/res/values-v11/template-styles.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/HdrViewfinder/Application/src/main/res/values-v21/base-colors.xml b/HdrViewfinder/Application/src/main/res/values-v21/base-colors.xml
deleted file mode 100644
index 8b6ec3f8..00000000
--- a/HdrViewfinder/Application/src/main/res/values-v21/base-colors.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
diff --git a/HdrViewfinder/Application/src/main/res/values-v21/base-template-styles.xml b/HdrViewfinder/Application/src/main/res/values-v21/base-template-styles.xml
deleted file mode 100644
index c778e4f9..00000000
--- a/HdrViewfinder/Application/src/main/res/values-v21/base-template-styles.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/HdrViewfinder/Application/src/main/res/values/attrs.xml b/HdrViewfinder/Application/src/main/res/values/attrs.xml
deleted file mode 100644
index ec8a5648..00000000
--- a/HdrViewfinder/Application/src/main/res/values/attrs.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
diff --git a/HdrViewfinder/Application/src/main/res/values/base-strings.xml b/HdrViewfinder/Application/src/main/res/values/base-strings.xml
deleted file mode 100644
index effcf645..00000000
--- a/HdrViewfinder/Application/src/main/res/values/base-strings.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
- HdrViewfinder
-
-
-
-
diff --git a/HdrViewfinder/Application/src/main/res/values/strings.xml b/HdrViewfinder/Application/src/main/res/values/strings.xml
deleted file mode 100644
index d910ab1a..00000000
--- a/HdrViewfinder/Application/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
-
- Help
-
-
-
- Mode: Normal
- Mode: Split
- Mode: HDR
-
-
- Auto exp. time:
- Even exp. time:
- Odd exp. time:
-
-
- HDR Viewfinder Demo:\n\n
-
- Tap viewfinder to switch modes.\n\n
-
- Normal: Standard camera preview\n
- Split: Manual exposure control\n
- HDR: Fused HDR viewfinder\n\n
-
- Swipe up/down in Split/HDR modes to change manual exposure
- values.\n\n
-
- The left half of the viewfinder controls exposure time for
- even-numbered frames, and the right half of the viewfinder
- controls exposure time for odd-numbered frames
-
-
- Info
-
- This sample app requires camera access in order to
- demo the API.
- No back-facing sufficiently capable camera available!
- Camera is disabled by device policy
- Camera was disconnected before it was opened
- Camera service reported an error
- Unknown camera error: %s
- You\'ve denied a permission that the app
- needs for core functionality. If you selected "don\'t ask again" in the past then
- you need to use Settings to re-enable the permission.
-
- OK
- Settings
-
-
diff --git a/HdrViewfinder/Application/src/main/res/values/template-dimens.xml b/HdrViewfinder/Application/src/main/res/values/template-dimens.xml
deleted file mode 100644
index 39e710b5..00000000
--- a/HdrViewfinder/Application/src/main/res/values/template-dimens.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
- 4dp
- 8dp
- 16dp
- 32dp
- 64dp
-
-
-
- @dimen/margin_medium
- @dimen/margin_medium
-
-
diff --git a/HdrViewfinder/Application/src/main/res/values/template-styles.xml b/HdrViewfinder/Application/src/main/res/values/template-styles.xml
deleted file mode 100644
index 6e7d593d..00000000
--- a/HdrViewfinder/Application/src/main/res/values/template-styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/HdrViewfinder/Application/src/main/rs/hdr_merge.rs b/HdrViewfinder/Application/src/main/rs/hdr_merge.rs
deleted file mode 100644
index f176fd71..00000000
--- a/HdrViewfinder/Application/src/main/rs/hdr_merge.rs
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-#pragma version(1)
-#pragma rs java_package_name(com.example.android.hdrviewfinder)
-#pragma rs_fp_relaxed
-
-rs_allocation gCurrentFrame;
-rs_allocation gPrevFrame;
-
-int gCutPointX = 0;
-int gDoMerge = 0;
-int gFrameCounter = 0;
-
-uchar4 __attribute__((kernel)) mergeHdrFrames(uchar4 prevPixel, uint32_t x, uint32_t y) {
-
- // Read in pixel values from latest frame - YUV color space
-
- uchar4 curPixel;
- curPixel.r = rsGetElementAtYuv_uchar_Y(gCurrentFrame, x, y);
- curPixel.g = rsGetElementAtYuv_uchar_U(gCurrentFrame, x, y);
- curPixel.b = rsGetElementAtYuv_uchar_V(gCurrentFrame, x, y);
- curPixel.a = 255;
-
- uchar4 mergedPixel;
- if (gDoMerge == 1) {
- // Complex HDR fusion technique
- mergedPixel = curPixel / 2 + prevPixel / 2;
-
- /* Experimental color saturation boosting merge
- mergedPixel.r = curPixel.r / 2 + prevPixel.r / 2;
-
- uchar saturationCurrent = abs(curPixel.g - 128) + abs(curPixel.b - 128);
- uchar saturationPrev = abs(prevPixel.g - 128) + abs(prevPixel.b - 128);
- mergedPixel.g = saturationCurrent > saturationPrev ? curPixel.g : prevPixel.g;
- mergedPixel.b = saturationCurrent > saturationPrev ? curPixel.b : prevPixel.b;
- */
- } else if (gCutPointX > 0) {
- // Composite side by side
- mergedPixel = ((x < gCutPointX) ^ (gFrameCounter & 0x1)) ?
- curPixel : prevPixel;
- } else {
- // Straight passthrough
- mergedPixel = curPixel;
- }
-
- // Convert YUV to RGB, JFIF transform with fixed-point math
- // R = Y + 1.402 * (V - 128)
- // G = Y - 0.34414 * (U - 128) - 0.71414 * (V - 128)
- // B = Y + 1.772 * (U - 128)
-
- int4 rgb;
- rgb.r = mergedPixel.r +
- mergedPixel.b * 1436 / 1024 - 179;
- rgb.g = mergedPixel.r -
- mergedPixel.g * 46549 / 131072 + 44 -
- mergedPixel.b * 93604 / 131072 + 91;
- rgb.b = mergedPixel.r +
- mergedPixel.g * 1814 / 1024 - 227;
- rgb.a = 255;
-
- // Store current pixel for next frame
- rsSetElementAt_uchar4(gPrevFrame, curPixel, x, y);
-
- // Write out merged HDR result
- uchar4 out = convert_uchar4(clamp(rgb, 0, 255));
-
- return out;
-}
diff --git a/HdrViewfinder/README.md b/HdrViewfinder/README.md
deleted file mode 100644
index 28cc4512..00000000
--- a/HdrViewfinder/README.md
+++ /dev/null
@@ -1,71 +0,0 @@
-
-Android HdrViewfinder Sample
-===================================
-
-This demo implements a real-time high-dynamic-range camera viewfinder, by alternating
-the sensor's exposure time between two exposure values on even and odd frames, and then
-compositing together the latest two frames whenever a new frame is captured.
-
-Introduction
-------------
-
-A small demo of advanced camera functionality with the Android camera2 API.
-
-This demo implements a real-time high-dynamic-range camera viewfinder,
-by alternating the sensor's exposure time between two exposure values on even and odd
-frames, and then compositing together the latest two frames whenever a new frame is
-captured.
-
-The demo has three modes: Regular auto-exposure viewfinder, split-screen manual exposure,
-and the fused HDR viewfinder. The latter two use manual exposure controlled by the user,
-by swiping up/down on the right and left halves of the viewfinder. The left half controls
-the exposure time of even frames, and the right half controls the exposure time of odd frames.
-
-In split-screen mode, the even frames are shown on the left and the odd frames on the right,
-so the user can see two different exposures of the scene simultaneously. In fused HDR mode,
-the even/odd frames are merged together into a single image. By selecting different exposure
-values for the even/odd frames, the fused image has a higher dynamic range than the regular
-viewfinder.
-
-The HDR fusion and the split-screen viewfinder processing is done with RenderScript; as is the
-necessary YUV->RGB conversion. The camera subsystem outputs YUV images naturally, while the GPU
-and display subsystems generally only accept RGB data. Therefore, after the images are
-fused/composited, a standard YUV->RGB color transform is applied before the the data is written
-to the output Allocation. The HDR fusion algorithm is very simple, and tends to result in
-lower-contrast scenes, but has very few artifacts and can run very fast.
-
-Data is passed between the subsystems (camera, RenderScript, and display) using the
-Android [android.view.Surface][1] class, which allows for zero-copy transport of large
-buffers between processes and subsystems.
-
-[1]: http://developer.android.com/reference/android/view/Surface.html
-
-Pre-requisites
---------------
-
-- Android SDK 28
-- Android Build Tools v28.0.3
-- Android Support Repository
-
-Screenshots
--------------
-
-
-
-Getting Started
----------------
-
-This sample uses the Gradle build system. To build this project, use the
-"gradlew build" command or use "Import Project" in Android Studio.
-
-Support
--------
-
-- Google+ Community: https://plus.google.com/communities/105153134372062985968
-- Stack Overflow: http://stackoverflow.com/questions/tagged/android
-
-If you've found an error in this sample, please file an issue:
-https://github.com/android/camera-samples/issues
-
-Patches are encouraged, and may be submitted by forking this project and
-submitting a pull request through GitHub. Please see CONTRIBUTING.md for more details.
diff --git a/HdrViewfinder/big_icon.png b/HdrViewfinder/big_icon.png
deleted file mode 100644
index b38019c2..00000000
Binary files a/HdrViewfinder/big_icon.png and /dev/null differ
diff --git a/HdrViewfinder/build.gradle b/HdrViewfinder/build.gradle
deleted file mode 100644
index 1dac6def..00000000
--- a/HdrViewfinder/build.gradle
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/HdrViewfinder/gradle.properties b/HdrViewfinder/gradle.properties
deleted file mode 100644
index 0bc4294e..00000000
--- a/HdrViewfinder/gradle.properties
+++ /dev/null
@@ -1,20 +0,0 @@
-
-# Project-wide Gradle settings.
-
-# IDE (e.g. Android Studio) users:
-# Settings specified in this file will override any Gradle settings
-# configured through the IDE.
-
-# For more details on how to configure your build environment visit
-# http://www.gradle.org/docs/current/userguide/build_environment.html
-
-# Specifies the JVM arguments used for the daemon process.
-# The setting is particularly useful for tweaking memory settings.
-# Default value: -Xmx10248m -XX:MaxPermSize=256m
-# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
-
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
-
diff --git a/HdrViewfinder/gradle/wrapper/gradle-wrapper.jar b/HdrViewfinder/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 94336fca..00000000
Binary files a/HdrViewfinder/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/HdrViewfinder/gradle/wrapper/gradle-wrapper.properties b/HdrViewfinder/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 5f373086..00000000
--- a/HdrViewfinder/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-#Wed Mar 31 21:17:05 PDT 2021
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
diff --git a/HdrViewfinder/gradlew b/HdrViewfinder/gradlew
deleted file mode 100755
index cccdd3d5..00000000
--- a/HdrViewfinder/gradlew
+++ /dev/null
@@ -1,172 +0,0 @@
-#!/usr/bin/env sh
-
-##############################################################################
-##
-## Gradle start up script for UN*X
-##
-##############################################################################
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn () {
- echo "$*"
-}
-
-die () {
- echo
- echo "$*"
- echo
- exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
- else
- JAVACMD="$JAVA_HOME/bin/java"
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
- fi
- i=$((i+1))
- done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
-fi
-
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=$(save "$@")
-
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-
-# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
-if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
- cd "$(dirname "$0")"
-fi
-
-exec "$JAVACMD" "$@"
diff --git a/HdrViewfinder/gradlew.bat b/HdrViewfinder/gradlew.bat
deleted file mode 100644
index e95643d6..00000000
--- a/HdrViewfinder/gradlew.bat
+++ /dev/null
@@ -1,84 +0,0 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/HdrViewfinder/screenshots/image1.png b/HdrViewfinder/screenshots/image1.png
deleted file mode 100755
index 75c8db17..00000000
Binary files a/HdrViewfinder/screenshots/image1.png and /dev/null differ
diff --git a/HdrViewfinder/settings.gradle b/HdrViewfinder/settings.gradle
deleted file mode 100644
index 9464a359..00000000
--- a/HdrViewfinder/settings.gradle
+++ /dev/null
@@ -1 +0,0 @@
-include 'Application'
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index f0a2666b..00000000
--- a/LICENSE
+++ /dev/null
@@ -1,191 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- Copyright 2020 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.
-
diff --git a/Presentations/2024/[External Share] Android Camera Droidcon NYC 2024.pdf b/Presentations/2024/[External Share] Android Camera Droidcon NYC 2024.pdf
deleted file mode 100644
index de799d28..00000000
Binary files a/Presentations/2024/[External Share] Android Camera Droidcon NYC 2024.pdf and /dev/null differ
diff --git a/Presentations/2024/[External Share] Android Camera Droidcon SF 2024.pdf b/Presentations/2024/[External Share] Android Camera Droidcon SF 2024.pdf
deleted file mode 100644
index dae57f94..00000000
Binary files a/Presentations/2024/[External Share] Android Camera Droidcon SF 2024.pdf and /dev/null differ
diff --git a/README.md b/README.md
index e31da133..f645ffa9 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,115 @@
-# Camera Samples Repository
+# Android Camera Samples Catalog
-This repository contains a set of individual Android Studio projects to help you get
-started with the [CameraX](https://developer.android.com/training/camerax) and
-[Camera2](https://developer.android.com/training/camera2) APIs in Android.
+A single, stand-alone Android app that showcases the **Camera2** and **CameraX** APIs through a
+catalog of small, self-contained, **Compose-first** samples. Each sample focuses on one feature and
+reuses a shared camera-scaffolding layer, so adding a new sample means writing the feature — not the
+boilerplate.
+
+## Screenshots
+
+The home catalog in the "Console" theme — a violet accent, monospace metadata, a bordered featured
+card, and a dense 2-column grid. The filter pills narrow the catalog by category, and samples are
+grouped CameraX-first, Camera2 below.
+
+