@@ -32,10 +32,9 @@ import com.dan.simplerawcamera.databinding.ActivityMainBinding
3232import kotlinx.coroutines.Dispatchers
3333import kotlinx.coroutines.GlobalScope
3434import kotlinx.coroutines.launch
35- import java.io.ByteArrayOutputStream
35+ import java.io.OutputStream
3636import java.text.SimpleDateFormat
3737import java.util.*
38- import kotlin.concurrent.schedule
3938import kotlin.concurrent.timer
4039import kotlin.math.abs
4140import kotlin.math.log2
@@ -75,22 +74,10 @@ class CameraActivity : AppCompatActivity() {
7574 const val FOCUS_STATE_SEARCHING = 2
7675 const val FOCUS_STATE_LOCKED = 3
7776
78- const val MEMORY_RETRY_TIMEOUT = 250L // ms
79-
8077 const val SELECT_CAMERA_ASYNC_DELAY = 250L // ms
8178
8279 private val FILE_NAME_DATE_FORMAT = SimpleDateFormat (" yyyyMMdd_HHmmss_SSS" , Locale .US )
8380
84- fun getMemInfo (): Pair <Long , Long > {
85- val info = Runtime .getRuntime()
86- val usedSize = (info.totalMemory() - info.freeMemory()) / (1024L * 1024L )
87- val maxSize = info.maxMemory() / (1024L * 1024L )
88- val freeSize = maxSize - usedSize
89- return Pair (freeSize, maxSize)
90- }
91-
92- fun getFreeMemInfo (): Long = getMemInfo().first
93-
9481 /* * Get photo name */
9582 fun getPhotoBaseFileName (timestamp : Long ): String = FILE_NAME_DATE_FORMAT .format(Date (timestamp))
9683
@@ -176,9 +163,6 @@ class CameraActivity : AppCompatActivity() {
176163
177164 private var mLocation: Location ? = null
178165
179- private val mSaveAsyncMQ = mutableListOf<Triple <String , String , ByteArray >>()
180- private var mSaveAsyncBusy = false
181-
182166 private var mOrientationEventListener: OrientationEventListener ? = null
183167 private var mScreenOrientation: Int = 0
184168 private var mPhotoExifOrientation: Int = 0
@@ -1125,61 +1109,27 @@ class CameraActivity : AppCompatActivity() {
11251109 }
11261110 }
11271111
1128- private fun saveAsyncNextItem () {
1129- mBinding.frameView.updateDebugMemInfo()
1130-
1131- if (! mSaveAsyncBusy && mSaveAsyncMQ.isNotEmpty()) {
1132- mSaveAsyncBusy = true
1133- mBinding.frameView.showSavePhotosIcon(true )
1134- val item = mSaveAsyncMQ.get(0 )
1135- mSaveAsyncMQ.removeAt(0 )
1136-
1137- GlobalScope .launch(Dispatchers .IO ) {
1138- val fileName = item.first
1139- val mimeType = item.second
1140- val byteArray = item.third
1141- var failed = true
1142-
1143- try {
1144- mSaveFolder?.let { saveFolder ->
1145- saveFolder.createFile(mimeType, fileName)?.let { newFile ->
1146- contentResolver.openOutputStream(newFile.uri)?.let { outputStream ->
1147- outputStream.write(byteArray)
1148- outputStream.close()
1149- failed = false
1150- }
1151- }
1152- }
1153- } catch (e: Exception ) {
1154- e.printStackTrace()
1155- }
1156-
1157- mSaveAsyncBusy = false
1158- runOnUiThread {
1159- if (failed) mBinding.frameView.showSaveError()
1160- saveAsyncNextItem()
1112+ private fun createAndSave ( fileName : String , mimeType : String , saveCallback : (outputStream: OutputStream ) -> Unit ) {
1113+ mSaveFolder?.let { saveFolder ->
1114+ saveFolder.createFile(mimeType, fileName)?.let { newFile ->
1115+ contentResolver.openOutputStream(newFile.uri)?.let { outputStream ->
1116+ saveCallback.invoke( outputStream )
1117+ outputStream.close()
11611118 }
11621119 }
1163- } else if (! mSaveAsyncBusy) {
1164- mBinding.frameView.showSavePhotosIcon(false )
11651120 }
11661121 }
11671122
1168- private fun saveAsync (fileName : String , mimeType : String , byteArray : ByteArray ) {
1169- mSaveAsyncMQ.add(Triple (fileName, mimeType, byteArray))
1170- saveAsyncNextItem()
1171- }
1172-
11731123 private fun saveDng (image : Image , captureResult : TotalCaptureResult ) {
11741124 Log .i(" TAKE_PHOTO" , " DNG: Save starts" )
11751125 try {
1176- val outputStream = ByteArrayOutputStream ()
11771126 val dngCreator = DngCreator (mCameraInfo.cameraCharacteristics, captureResult)
11781127 mLocation?.let { dngCreator.setLocation(it) }
11791128 dngCreator.setOrientation(mPhotoExifOrientation)
1180- dngCreator.writeImage(outputStream, image)
1181- saveAsync(" $mPhotoFileNameBase .dng" , " image/x-adobe-dng" , outputStream.toByteArray())
1182- } catch (e: Exception ) {
1129+ createAndSave( " $mPhotoFileNameBase .dng" , " image/x-adobe-dng" ) { outputStream ->
1130+ dngCreator.writeImage(outputStream, image)
1131+ }
1132+ } catch (e: Exception ) {
11831133 e.printStackTrace()
11841134 }
11851135 Log .i(" TAKE_PHOTO" , " DNG: Save ends" )
@@ -1188,12 +1138,12 @@ class CameraActivity : AppCompatActivity() {
11881138 private fun saveJpeg (image : Image ) {
11891139 Log .i(" TAKE_PHOTO" , " JPEG: Save starts" )
11901140 try {
1191- val outputStream = ByteArrayOutputStream ()
11921141 val buffer = image.planes[0 ].buffer
11931142 val bytes = ByteArray (buffer.remaining())
11941143 buffer.get(bytes)
1195- outputStream.write(bytes)
1196- saveAsync(" $mPhotoFileNameBase .jpg" , " image/jpeg" , outputStream.toByteArray())
1144+ createAndSave( " $mPhotoFileNameBase .jpg" , " image/jpeg" ) { outputStream ->
1145+ outputStream.write(bytes)
1146+ }
11971147 } catch (e: Exception ) {
11981148 e.printStackTrace()
11991149 }
@@ -1219,8 +1169,6 @@ class CameraActivity : AppCompatActivity() {
12191169 /* * Start taking a photo */
12201170 private fun takePhoto (newFile : Boolean = false, start : Boolean = false) {
12211171 runOnUiThread {
1222- mBinding.frameView.updateDebugMemInfo()
1223-
12241172 if (start) {
12251173 if (! mSequenceStarted) {
12261174 mPhotoCounter = 0
@@ -1267,41 +1215,24 @@ class CameraActivity : AppCompatActivity() {
12671215 val cameraCaptureSession = mCameraCaptureSession
12681216
12691217 if (takeNewPhoto && null != captureRequestPhoto && null != cameraCaptureSession) {
1270- var minMem =
1271- when (settings.takePhotoModes) {
1272- Settings .PHOTO_TYPE_DNG -> mCameraInfo.estimatedDngSize * 2
1273- Settings .PHOTO_TYPE_JPEG -> mCameraInfo.estimatedJpegSize
1274- else -> mCameraInfo.estimatedDngSize * 2 + mCameraInfo.estimatedJpegSize
1275- }
1276- minMem = 1 + minMem / (1024 * 1024 ) // convert to MB
1277-
1278- if (mSaveAsyncMQ.isNotEmpty() && minMem > getFreeMemInfo()) {
1279- Log .i(" TAKE_PHOTO" , " Not enough memory" )
1280- mPhotoTakeMask = PHOTO_TAKE_OUT_OF_MEMORY
1281- Timer (" Out of memory" , false ).schedule(MEMORY_RETRY_TIMEOUT ) {
1282- mPhotoTakeMask = 0
1283- takePhoto(true )
1284- }
1285- } else {
1286- Log .i(" TAKE_PHOTO" , " New photo" )
1218+ Log .i(" TAKE_PHOTO" , " New photo" )
12871219
1288- mPhotoInProgress = true
1220+ mPhotoInProgress = true
12891221
1290- mPhotoTakeMask = when (settings.takePhotoModes) {
1291- Settings .PHOTO_TYPE_DNG -> PHOTO_TAKE_DNG or PHOTO_TAKE_COMPLETED
1292- Settings .PHOTO_TYPE_JPEG -> PHOTO_TAKE_JPEG or PHOTO_TAKE_COMPLETED
1293- else -> PHOTO_TAKE_JPEG or PHOTO_TAKE_DNG or PHOTO_TAKE_COMPLETED
1294- }
1222+ mPhotoTakeMask = when (settings.takePhotoModes) {
1223+ Settings .PHOTO_TYPE_DNG -> PHOTO_TAKE_DNG or PHOTO_TAKE_COMPLETED
1224+ Settings .PHOTO_TYPE_JPEG -> PHOTO_TAKE_JPEG or PHOTO_TAKE_COMPLETED
1225+ else -> PHOTO_TAKE_JPEG or PHOTO_TAKE_DNG or PHOTO_TAKE_COMPLETED
1226+ }
12951227
1296- mPhotoTimestamp = System .currentTimeMillis()
1297- mPhotoFileNameBase = getPhotoBaseFileName(mPhotoTimestamp)
1228+ mPhotoTimestamp = System .currentTimeMillis()
1229+ mPhotoFileNameBase = getPhotoBaseFileName(mPhotoTimestamp)
12981230
1299- cameraCaptureSession.capture(
1300- captureRequestPhoto,
1301- mCameraCaptureSessionPhotoCaptureCallback,
1302- getWorkerHandler()
1303- )
1304- }
1231+ cameraCaptureSession.capture(
1232+ captureRequestPhoto,
1233+ mCameraCaptureSessionPhotoCaptureCallback,
1234+ getWorkerHandler()
1235+ )
13051236 } else {
13061237 mPhotoInProgress = false
13071238 setupCapturePreviewRequest()
0 commit comments