Skip to content

Commit 0508a98

Browse files
committed
Rollback changes for issue #1
1 parent 2c13e9e commit 0508a98

File tree

4 files changed

+110
-32
lines changed

4 files changed

+110
-32
lines changed

.idea/gradle.xml

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
android:label="@string/app_name"
1818
android:roundIcon="@mipmap/ic_launcher_round"
1919
android:supportsRtl="true"
20-
android:theme="@style/Theme.SimpleRawCamera">
20+
android:theme="@style/Theme.SimpleRawCamera"
21+
android:largeHeap="true">
2122
<activity
2223
android:name=".CameraActivity"
2324
android:screenOrientation="landscape">

app/src/main/java/com/dan/simplerawcamera/CameraActivity.kt

Lines changed: 98 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ import com.dan.simplerawcamera.databinding.ActivityMainBinding
3232
import kotlinx.coroutines.Dispatchers
3333
import kotlinx.coroutines.GlobalScope
3434
import kotlinx.coroutines.launch
35-
import java.io.OutputStream
35+
import java.io.ByteArrayOutputStream
3636
import java.text.SimpleDateFormat
3737
import java.util.*
38+
import kotlin.concurrent.schedule
3839
import kotlin.concurrent.timer
3940
import kotlin.math.abs
4041
import kotlin.math.log2
@@ -74,10 +75,22 @@ class CameraActivity : AppCompatActivity() {
7475
const val FOCUS_STATE_SEARCHING = 2
7576
const val FOCUS_STATE_LOCKED = 3
7677

78+
const val MEMORY_RETRY_TIMEOUT = 250L //ms
79+
7780
const val SELECT_CAMERA_ASYNC_DELAY = 250L //ms
7881

7982
private val FILE_NAME_DATE_FORMAT = SimpleDateFormat("yyyyMMdd_HHmmss_SSS", Locale.US)
8083

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+
8194
/** Get photo name */
8295
fun getPhotoBaseFileName(timestamp: Long): String = FILE_NAME_DATE_FORMAT.format(Date(timestamp))
8396

@@ -163,6 +176,9 @@ class CameraActivity : AppCompatActivity() {
163176

164177
private var mLocation: Location? = null
165178

179+
private val mSaveAsyncMQ = mutableListOf<Triple<String, String, ByteArray>>()
180+
private var mSaveAsyncBusy = false
181+
166182
private var mOrientationEventListener: OrientationEventListener? = null
167183
private var mScreenOrientation: Int = 0
168184
private var mPhotoExifOrientation: Int = 0
@@ -858,7 +874,7 @@ class CameraActivity : AppCompatActivity() {
858874
}
859875

860876
mBinding.switch4X.isChecked = false
861-
mBinding.switch4X.setOnCheckedChangeListener { _, isChecked ->
877+
mBinding.switch4X.setOnCheckedChangeListener { _, _ ->
862878
giveHapticFeedback(mBinding.switchSequences)
863879
setupCapturePreviewRequest()
864880
}
@@ -1109,27 +1125,61 @@ class CameraActivity : AppCompatActivity() {
11091125
}
11101126
}
11111127

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()
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()
11181161
}
11191162
}
1163+
} else if(!mSaveAsyncBusy) {
1164+
mBinding.frameView.showSavePhotosIcon(false)
11201165
}
11211166
}
11221167

1168+
private fun saveAsync(fileName: String, mimeType: String, byteArray: ByteArray) {
1169+
mSaveAsyncMQ.add(Triple(fileName, mimeType, byteArray))
1170+
saveAsyncNextItem()
1171+
}
1172+
11231173
private fun saveDng(image: Image, captureResult: TotalCaptureResult) {
11241174
Log.i("TAKE_PHOTO", "DNG: Save starts")
11251175
try {
1176+
val outputStream = ByteArrayOutputStream()
11261177
val dngCreator = DngCreator(mCameraInfo.cameraCharacteristics, captureResult)
11271178
mLocation?.let { dngCreator.setLocation(it) }
11281179
dngCreator.setOrientation(mPhotoExifOrientation)
1129-
createAndSave( "$mPhotoFileNameBase.dng", "image/x-adobe-dng" ) { outputStream ->
1130-
dngCreator.writeImage(outputStream, image)
1131-
}
1132-
} catch (e: Exception) {
1180+
dngCreator.writeImage(outputStream, image)
1181+
saveAsync("$mPhotoFileNameBase.dng", "image/x-adobe-dng", outputStream.toByteArray())
1182+
} catch (e: Exception) {
11331183
e.printStackTrace()
11341184
}
11351185
Log.i("TAKE_PHOTO", "DNG: Save ends")
@@ -1138,12 +1188,12 @@ class CameraActivity : AppCompatActivity() {
11381188
private fun saveJpeg(image: Image) {
11391189
Log.i("TAKE_PHOTO", "JPEG: Save starts")
11401190
try {
1191+
val outputStream = ByteArrayOutputStream()
11411192
val buffer = image.planes[0].buffer
11421193
val bytes = ByteArray(buffer.remaining())
11431194
buffer.get(bytes)
1144-
createAndSave( "$mPhotoFileNameBase.jpg", "image/jpeg" ) { outputStream ->
1145-
outputStream.write(bytes)
1146-
}
1195+
outputStream.write(bytes)
1196+
saveAsync("$mPhotoFileNameBase.jpg", "image/jpeg", outputStream.toByteArray())
11471197
} catch (e: Exception) {
11481198
e.printStackTrace()
11491199
}
@@ -1169,6 +1219,8 @@ class CameraActivity : AppCompatActivity() {
11691219
/** Start taking a photo */
11701220
private fun takePhoto(newFile: Boolean = false, start: Boolean = false) {
11711221
runOnUiThread {
1222+
mBinding.frameView.updateDebugMemInfo()
1223+
11721224
if (start) {
11731225
if (!mSequenceStarted) {
11741226
mPhotoCounter = 0
@@ -1215,24 +1267,41 @@ class CameraActivity : AppCompatActivity() {
12151267
val cameraCaptureSession = mCameraCaptureSession
12161268

12171269
if (takeNewPhoto && null != captureRequestPhoto && null != cameraCaptureSession) {
1218-
Log.i("TAKE_PHOTO", "New photo")
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")
12191287

1220-
mPhotoInProgress = true
1288+
mPhotoInProgress = true
12211289

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-
}
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+
}
12271295

1228-
mPhotoTimestamp = System.currentTimeMillis()
1229-
mPhotoFileNameBase = getPhotoBaseFileName(mPhotoTimestamp)
1296+
mPhotoTimestamp = System.currentTimeMillis()
1297+
mPhotoFileNameBase = getPhotoBaseFileName(mPhotoTimestamp)
12301298

1231-
cameraCaptureSession.capture(
1232-
captureRequestPhoto,
1233-
mCameraCaptureSessionPhotoCaptureCallback,
1234-
getWorkerHandler()
1235-
)
1299+
cameraCaptureSession.capture(
1300+
captureRequestPhoto,
1301+
mCameraCaptureSessionPhotoCaptureCallback,
1302+
getWorkerHandler()
1303+
)
1304+
}
12361305
} else {
12371306
mPhotoInProgress = false
12381307
setupCapturePreviewRequest()

app/src/main/java/com/dan/simplerawcamera/FrameView.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ class FrameView : View {
131131
mSavePhotoErrorIcon.bounds = Rect(PHOTO_ICON_X + PHOTO_ICON_WIDTH, PHOTO_ICON_X, PHOTO_ICON_X + 2 * PHOTO_ICON_WIDTH, PHOTO_ICON_Y + PHOTO_ICON_HEIGHT)
132132

133133
DrawableCompat.setTint(mSavePhotoErrorIcon, Color.RED)
134+
135+
timer(null, false, 1000, 1000) {
136+
updateDebugMemInfo()
137+
}
134138
}
135139

136140
/** Sequence: show the delay until the next sequence photo */
@@ -255,6 +259,11 @@ class FrameView : View {
255259
}
256260
}
257261

262+
fun updateDebugMemInfo() {
263+
val memInfo = CameraActivity.getMemInfo()
264+
setDebugInfo(DEBUG_INFO_MEM, "Mem: ${memInfo.first} MB / ${memInfo.second} MB")
265+
}
266+
258267
@SuppressLint("DrawAllocation", "UseCompatLoadingForDrawables")
259268
override fun onDraw(canvas: Canvas?) {
260269
super.onDraw(canvas)

0 commit comments

Comments
 (0)