diff --git a/app/build.gradle b/app/build.gradle
index 86b956d..b80e54f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -47,7 +47,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "androidx.core:core-ktx:1.3.2"
implementation "androidx.appcompat:appcompat:1.2.0"
- implementation "androidx.constraintlayout:constraintlayout:2.0.3"
+ implementation "androidx.constraintlayout:constraintlayout:2.0.4"
implementation "androidx.sqlite:sqlite-ktx:2.1.0"
implementation "androidx.preference:preference-ktx:1.1.1"
@@ -56,6 +56,12 @@ dependencies {
implementation "com.github.bumptech.glide:glide:4.11.0"
implementation "com.google.android.exoplayer:exoplayer-core:2.11.7"
implementation "com.google.android.exoplayer:exoplayer-ui:2.11.7"
- implementation "com.otaliastudios:cameraview:2.6.4"
implementation "androidx.biometric:biometric:1.0.1"
+
+ def camerax_version = "1.0.0-rc01"
+ implementation "androidx.camera:camera-camera2:$camerax_version"
+ implementation "androidx.camera:camera-lifecycle:$camerax_version"
+ implementation "androidx.camera:camera-view:1.0.0-alpha20"
+ implementation "androidx.camera:camera-extensions:1.0.0-alpha20"
+
}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 01533fb..af15bf1 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -24,5 +24,4 @@
-keep class sushi.hardcore.droidfs.SettingsActivity$** {
*;
}
--keep class sushi.hardcore.droidfs.explorers.ExplorerElement
--keep class com.otaliastudios.cameraview.markers.DefaultAutoFocusMarker
\ No newline at end of file
+-keep class sushi.hardcore.droidfs.explorers.ExplorerElement
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e8d1dd2..78e6687 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -10,6 +10,7 @@
+
? = null
+ private var currentResolutionIndex: Int = 0
+ private var isBackCamera = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
gocryptfsVolume = GocryptfsVolume(intent.getIntExtra("sessionID", -1))
outputDirectory = intent.getStringExtra("path")!!
- context = this
- camera.setLifecycleOwner(this)
- camera.addCameraListener(object: CameraListener(){
- override fun onPictureTaken(result: PictureResult) {
- take_photo_button.onPhotoTaken()
- val inputStream = ByteArrayInputStream(result.data)
- if (gocryptfsVolume.importFile(inputStream, PathUtils.pathJoin(outputDirectory, fileName))){
- Toast.makeText(context, getString(R.string.picture_save_success, fileName), Toast.LENGTH_SHORT).show()
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){
+ setupCamera()
+ } else {
+ requestPermissions(arrayOf(Manifest.permission.CAMERA), CAMERA_PERMISSION_REQUEST_CODE)
+ }
+ } else {
+ setupCamera()
+ }
+
+ cameraExecutor = Executors.newSingleThreadExecutor()
+
+ take_photo_button.onClick = ::onClickTakePhoto
+ orientedIcons = listOf(image_ratio, image_timer, image_close, image_flash, image_camera_switch)
+ sensorOrientationListener = SensorOrientationListener(this)
+
+ val scaleGestureDetector = ScaleGestureDetector(this, object : ScaleGestureDetector.SimpleOnScaleGestureListener(){
+ override fun onScale(detector: ScaleGestureDetector): Boolean {
+ val currentZoomRatio = imageCapture?.camera?.cameraInfo?.zoomState?.value?.zoomRatio ?: 0F
+ imageCapture?.camera?.cameraControl?.setZoomRatio(currentZoomRatio*detector.scaleFactor)
+ return true
+ }
+ })
+ camera_preview.setOnTouchListener { _, motionEvent: MotionEvent ->
+ when (motionEvent.action) {
+ MotionEvent.ACTION_DOWN -> true
+ MotionEvent.ACTION_UP -> {
+ val factory = camera_preview.meteringPointFactory
+ val point = factory.createPoint(motionEvent.x, motionEvent.y)
+ val action = FocusMeteringAction.Builder(point).build()
+ imageCapture?.camera?.cameraControl?.startFocusAndMetering(action)
+ true
+ }
+ MotionEvent.ACTION_MOVE -> scaleGestureDetector.onTouchEvent(motionEvent)
+ else -> false
+ }
+ }
+ }
+
+ override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
+ when (requestCode) {
+ CAMERA_PERMISSION_REQUEST_CODE -> if (grantResults.size == 1) {
+ if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
+ ColoredAlertDialogBuilder(this)
+ .setTitle(R.string.error)
+ .setMessage(R.string.camera_perm_needed)
+ .setCancelable(false)
+ .setPositiveButton(R.string.ok) { _, _ ->
+ isFinishingIntentionally = true
+ finish()
+ }.show()
} else {
- ColoredAlertDialogBuilder(context)
+ setupCamera()
+ }
+ }
+ }
+ }
+
+ private fun adaptPreviewSize(resolution: Size){
+ val metrics = DisplayMetrics()
+ windowManager.defaultDisplay.getMetrics(metrics)
+ //resolution.width and resolution.height seem to be inverted
+ val width = resolution.height
+ val height = resolution.width
+ camera_preview.layoutParams = if (metrics.widthPixels < width){
+ RelativeLayout.LayoutParams(
+ metrics.widthPixels,
+ (height * (metrics.widthPixels.toFloat() / width)).toInt()
+ )
+ } else {
+ RelativeLayout.LayoutParams(width, height)
+ }
+ (camera_preview.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.CENTER_IN_PARENT)
+ }
+
+ private fun setupCamera(resolution: Size? = null){
+ val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
+ cameraProviderFuture.addListener({
+ val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
+ val preview = Preview.Builder()
+ .build()
+ .also {
+ it.setSurfaceProvider(camera_preview.surfaceProvider)
+ }
+ val builder = ImageCapture.Builder()
+ .setFlashMode(ImageCapture.FLASH_MODE_AUTO)
+ resolution?.let {
+ builder.setTargetResolution(it)
+ }
+ val hdrImageCapture = HdrImageCaptureExtender.create(builder)
+ val cameraSelector = if (isBackCamera){ CameraSelector.DEFAULT_BACK_CAMERA } else { CameraSelector.DEFAULT_FRONT_CAMERA }
+
+ if (hdrImageCapture.isExtensionAvailable(cameraSelector)){
+ hdrImageCapture.enableExtension(cameraSelector)
+ }
+
+ imageCapture = builder.build()
+
+ cameraProvider.unbindAll()
+ val camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
+
+ adaptPreviewSize(imageCapture!!.attachedSurfaceResolution!!)
+
+ val info = Camera2CameraInfo.from(camera.cameraInfo)
+ val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
+ val characteristics = cameraManager.getCameraCharacteristics(info.cameraId)
+ characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)?.let { streamConfigurationMap ->
+ resolutions = streamConfigurationMap.getOutputSizes(imageCapture!!.imageFormat)
+ }
+ }, ContextCompat.getMainExecutor(this))
+ }
+
+ private fun takePhoto() {
+ val imageCapture = imageCapture ?: return
+ val outputBuff = ByteArrayOutputStream()
+ val outputOptions = ImageCapture.OutputFileOptions.Builder(outputBuff).build()
+ imageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
+ override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
+ take_photo_button.onPhotoTaken()
+ if (gocryptfsVolume.importFile(ByteArrayInputStream(outputBuff.toByteArray()), PathUtils.pathJoin(outputDirectory, fileName))){
+ Toast.makeText(applicationContext, getString(R.string.picture_save_success, fileName), Toast.LENGTH_SHORT).show()
+ } else {
+ ColoredAlertDialogBuilder(applicationContext)
.setTitle(R.string.error)
.setMessage(R.string.picture_save_failed)
.setCancelable(false)
@@ -83,10 +207,11 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
.show()
}
}
+ override fun onError(exception: ImageCaptureException) {
+ take_photo_button.onPhotoTaken()
+ Toast.makeText(applicationContext, exception.message, Toast.LENGTH_SHORT).show()
+ }
})
- take_photo_button.onClick = ::onClickTakePhoto
- orientedIcons = listOf(image_hdr, image_timer, image_grid, image_close, image_flash, image_camera_switch)
- sensorOrientationListener = SensorOrientationListener(this)
}
private fun onClickTakePhoto() {
@@ -102,49 +227,41 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
Thread.sleep(1000)
}
runOnUiThread {
- camera.takePicture()
+ takePhoto()
text_timer.visibility = View.GONE
}
}.start()
} else {
- camera.takePicture()
+ takePhoto()
}
}
fun onClickFlash(view: View) {
- currentFlashModeIndex = MiscUtils.incrementIndex(currentFlashModeIndex, flashModes)
- camera.flash = flashModes[currentFlashModeIndex]
- image_flash.setImageResource(when (camera.flash) {
- Flash.AUTO -> R.drawable.icon_flash_auto
- Flash.ON -> R.drawable.icon_flash_on
- else -> R.drawable.icon_flash_off
+ image_flash.setImageResource(when (imageCapture?.flashMode) {
+ ImageCapture.FLASH_MODE_AUTO -> {
+ imageCapture?.flashMode = ImageCapture.FLASH_MODE_ON
+ R.drawable.icon_flash_on
+ }
+ ImageCapture.FLASH_MODE_ON -> {
+ imageCapture?.flashMode = ImageCapture.FLASH_MODE_OFF
+ R.drawable.icon_flash_off
+ }
+ else -> {
+ imageCapture?.flashMode = ImageCapture.FLASH_MODE_AUTO
+ R.drawable.icon_flash_auto
+ }
})
}
fun onClickCameraSwitch(view: View) {
- camera.toggleFacing()
- if (camera.facing == Facing.FRONT){
- image_camera_switch.setImageResource(R.drawable.icon_camera_back)
- } else {
+ isBackCamera = if (isBackCamera) {
image_camera_switch.setImageResource(R.drawable.icon_camera_front)
- Thread {
- Thread.sleep(25)
- camera.flash = flashModes[currentFlashModeIndex] //refresh flash mode after switching camera
- }.start()
- }
- }
-
- fun onClickHDR(view: View) {
- camera.hdr = when (camera.hdr){
- Hdr.ON -> {
- image_hdr.setImageResource(R.drawable.icon_hdr_off)
- Hdr.OFF
- }
- Hdr.OFF -> {
- image_hdr.setImageResource(R.drawable.icon_hdr_on)
- Hdr.ON
- }
+ false
+ } else {
+ image_camera_switch.setImageResource(R.drawable.icon_camera_back)
+ true
}
+ setupCamera()
}
fun onClickTimer(view: View) {
@@ -173,16 +290,18 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
dialog.show()
}
- fun onClickGrid(view: View) {
- ColoredAlertDialogBuilder(this)
- .setTitle(getString(R.string.choose_grid))
- .setSingleChoiceItems(gridTitles.map { getString(it) }.toTypedArray(), gridValues.indexOf(camera.grid)){ dialog, which ->
- camera.grid = gridValues[which]
- image_grid.setImageResource(if (camera.grid == Grid.OFF){ R.drawable.icon_grid_off } else { R.drawable.icon_grid_on })
- dialog.dismiss()
- }
- .setNegativeButton(R.string.cancel, null)
- .show()
+ fun onClickRatio(view: View) {
+ resolutions?.let {
+ ColoredAlertDialogBuilder(this)
+ .setTitle(R.string.choose_resolution)
+ .setSingleChoiceItems(DialogSingleChoiceAdapter(this, it.map { size -> size.toString() }.toTypedArray()), currentResolutionIndex) { dialog, which ->
+ setupCamera(resolutions!![which])
+ dialog.dismiss()
+ currentResolutionIndex = which
+ }
+ .setNegativeButton(R.string.cancel, null)
+ .show()
+ }
}
fun onClickClose(view: View) {
@@ -192,6 +311,7 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
override fun onDestroy() {
super.onDestroy()
+ cameraExecutor.shutdown()
if (!isFinishingIntentionally) {
gocryptfsVolume.close()
RestrictedFileProvider.wipeAll(this)
diff --git a/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt b/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt
index 1c51d84..9cf57c3 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt
+++ b/app/src/main/java/sushi/hardcore/droidfs/explorers/BaseExplorerActivity.kt
@@ -419,7 +419,7 @@ open class BaseExplorerActivity : BaseActivity() {
setCurrentPath(currentDirectoryPath)
dialog.dismiss()
}
- .setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
+ .setNegativeButton(R.string.cancel, null)
.show()
true
}
diff --git a/app/src/main/res/drawable/icon_grid_on.xml b/app/src/main/res/drawable/icon_aspect_ratio.xml
similarity index 50%
rename from app/src/main/res/drawable/icon_grid_on.xml
rename to app/src/main/res/drawable/icon_aspect_ratio.xml
index 7bfc825..c7b8b5f 100644
--- a/app/src/main/res/drawable/icon_grid_on.xml
+++ b/app/src/main/res/drawable/icon_aspect_ratio.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/app/src/main/res/drawable/icon_grid_off.xml b/app/src/main/res/drawable/icon_grid_off.xml
deleted file mode 100644
index e247223..0000000
--- a/app/src/main/res/drawable/icon_grid_off.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/icon_hdr_off.xml b/app/src/main/res/drawable/icon_hdr_off.xml
deleted file mode 100644
index 550f633..0000000
--- a/app/src/main/res/drawable/icon_hdr_off.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/icon_hdr_on.xml b/app/src/main/res/drawable/icon_hdr_on.xml
deleted file mode 100644
index 4e10da5..0000000
--- a/app/src/main/res/drawable/icon_hdr_on.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/app/src/main/res/layout/activity_camera.xml b/app/src/main/res/layout/activity_camera.xml
index 17a4491..dc63ec0 100644
--- a/app/src/main/res/layout/activity_camera.xml
+++ b/app/src/main/res/layout/activity_camera.xml
@@ -6,17 +6,11 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".CameraActivity">
-
+
-
-
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index fe606da..8ffe31e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -162,10 +162,6 @@
The selected items have been successfully moved.
Move successful !
Enter the timer duration (in s)
- None
- 3x3
- 4x4
- Choose grid
Please enter a numeric value
Failed to retrieve the selected path.
DroidFS doesn\'t have write access to this path. Please try another location.
@@ -193,4 +189,6 @@
Hidden Volume
Volume name cannot contain slashes
Hidden volumes are stored in the app\'s internal storage. Other apps can\'t see these volumes without root access. However, if you uninstall DroidFS or clear data of the app, all your hidden volumes will be LOST. Be sure to make backups !
+ Camera permission is needed to take photo.
+ Choose a resolution
diff --git a/build.gradle b/build.gradle
index d5f6eb6..1590fed 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,7 +6,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.1.0'
+ classpath 'com.android.tools.build:gradle:4.1.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}