Switching to CameraX
This commit is contained in:
parent
b23cb7b8ce
commit
eefca5ee0a
@ -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"
|
||||
|
||||
}
|
||||
|
3
app/proguard-rules.pro
vendored
3
app/proguard-rules.pro
vendored
@ -24,5 +24,4 @@
|
||||
-keep class sushi.hardcore.droidfs.SettingsActivity$** {
|
||||
*;
|
||||
}
|
||||
-keep class sushi.hardcore.droidfs.explorers.ExplorerElement
|
||||
-keep class com.otaliastudios.cameraview.markers.DefaultAutoFocusMarker
|
||||
-keep class sushi.hardcore.droidfs.explorers.ExplorerElement
|
@ -10,6 +10,7 @@
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera.any"
|
||||
|
@ -3,8 +3,15 @@ package sushi.hardcore.droidfs
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.hardware.camera2.CameraCharacteristics
|
||||
import android.hardware.camera2.CameraManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.Size
|
||||
import android.view.MotionEvent
|
||||
import android.view.ScaleGestureDetector
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.view.animation.Animation
|
||||
@ -12,35 +19,34 @@ import android.view.animation.LinearInterpolator
|
||||
import android.view.animation.RotateAnimation
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.Toast
|
||||
import androidx.camera.camera2.interop.Camera2CameraInfo
|
||||
import androidx.camera.core.*
|
||||
import androidx.camera.extensions.HdrImageCaptureExtender
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.otaliastudios.cameraview.CameraListener
|
||||
import com.otaliastudios.cameraview.PictureResult
|
||||
import com.otaliastudios.cameraview.controls.Facing
|
||||
import com.otaliastudios.cameraview.controls.Flash
|
||||
import com.otaliastudios.cameraview.controls.Grid
|
||||
import com.otaliastudios.cameraview.controls.Hdr
|
||||
import kotlinx.android.synthetic.main.activity_camera.*
|
||||
import sushi.hardcore.droidfs.adapters.DialogSingleChoiceAdapter
|
||||
import sushi.hardcore.droidfs.provider.RestrictedFileProvider
|
||||
import sushi.hardcore.droidfs.util.GocryptfsVolume
|
||||
import sushi.hardcore.droidfs.util.MiscUtils
|
||||
import sushi.hardcore.droidfs.util.PathUtils
|
||||
import sushi.hardcore.droidfs.widgets.ColoredAlertDialogBuilder
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
|
||||
companion object {
|
||||
private val flashModes = listOf(Flash.AUTO, Flash.ON, Flash.OFF)
|
||||
private val gridTitles = listOf(R.string.grid_none, R.string.grid_3x3, R.string.grid_4x4)
|
||||
private val gridValues = listOf(Grid.OFF, Grid.DRAW_3X3, Grid.DRAW_4X4)
|
||||
private const val CAMERA_PERMISSION_REQUEST_CODE = 1
|
||||
private const val fileNameRandomMin = 100000
|
||||
private const val fileNameRandomMax = 999999
|
||||
private val dateFormat = SimpleDateFormat("yyyyMMdd_HHmmss")
|
||||
private val random = Random()
|
||||
}
|
||||
private var currentFlashModeIndex = 0
|
||||
private var timerDuration = 0
|
||||
set(value) {
|
||||
field = value
|
||||
@ -57,22 +63,140 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
|
||||
private lateinit var outputDirectory: String
|
||||
private lateinit var fileName: String
|
||||
private var isFinishingIntentionally = false
|
||||
private lateinit var context: Context
|
||||
private lateinit var cameraExecutor: ExecutorService
|
||||
private var imageCapture: ImageCapture? = null
|
||||
private var resolutions: Array<Size>? = 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<String>, 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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M20,2L4,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM8,20L4,20v-4h4v4zM8,14L4,14v-4h4v4zM8,8L4,8L4,4h4v4zM14,20h-4v-4h4v4zM14,14h-4v-4h4v4zM14,8h-4L10,4h4v4zM20,20h-4v-4h4v4zM20,14h-4v-4h4v4zM20,8h-4L16,4h4v4z"/>
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,12h-2v3h-3v2h5v-5zM7,9h3L10,7L5,7v5h2L7,9zM21,3L3,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM21,19.01L3,19.01L3,4.99h18v14.02z"/>
|
||||
</vector>
|
@ -1,5 +0,0 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M8,4v1.45l2,2L10,4h4v4h-3.45l2,2L14,10v1.45l2,2L16,10h4v4h-3.45l2,2L20,16v1.45l2,2L22,4c0,-1.1 -0.9,-2 -2,-2L4.55,2l2,2L8,4zM16,4h4v4h-4L16,4zM1.27,1.27L0,2.55l2,2L2,20c0,1.1 0.9,2 2,2h15.46l2,2 1.27,-1.27L1.27,1.27zM10,12.55L11.45,14L10,14v-1.45zM4,6.55L5.45,8L4,8L4,6.55zM8,20L4,20v-4h4v4zM8,14L4,14v-4h3.45l0.55,0.55L8,14zM14,20h-4v-4h3.45l0.55,0.54L14,20zM16,20v-1.46L17.46,20L16,20z"/>
|
||||
</vector>
|
@ -1,5 +0,0 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M17.5,15v-2h1.1l0.9,2L21,15l-0.9,-2.1c0.5,-0.2 0.9,-0.8 0.9,-1.4v-1c0,-0.8 -0.7,-1.5 -1.5,-1.5L16,9v4.9l1.1,1.1h0.4zM17.5,10.5h2v1h-2v-1zM13,10.5v0.4l1.5,1.5v-1.9c0,-0.8 -0.7,-1.5 -1.5,-1.5h-1.9l1.5,1.5h0.4zM9.5,9.5l-7,-7 -1.1,1L6.9,9h-0.4v2h-2L4.5,9L3,9v6h1.5v-2.5h2L6.5,15L8,15v-4.9l1.5,1.5L9.5,15h3.4l7.6,7.6 1.1,-1.1 -12.1,-12z"/>
|
||||
</vector>
|
@ -1,5 +0,0 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M21,11.5v-1c0,-0.8 -0.7,-1.5 -1.5,-1.5L16,9v6h1.5v-2h1.1l0.9,2L21,15l-0.9,-2.1c0.5,-0.3 0.9,-0.8 0.9,-1.4zM19.5,11.5h-2v-1h2v1zM6.5,11h-2L4.5,9L3,9v6h1.5v-2.5h2L6.5,15L8,15L8,9L6.5,9v2zM13,9L9.5,9v6L13,15c0.8,0 1.5,-0.7 1.5,-1.5v-3c0,-0.8 -0.7,-1.5 -1.5,-1.5zM13,13.5h-2v-3h2v3z"/>
|
||||
</vector>
|
@ -6,17 +6,11 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:context=".CameraActivity">
|
||||
|
||||
<com.otaliastudios.cameraview.CameraView
|
||||
android:id="@+id/camera"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:cameraGesturePinch="zoom"
|
||||
app:cameraGestureTap="autoFocus"
|
||||
app:cameraAutoFocusMarker="@string/cameraview_default_autofocus_marker"
|
||||
app:cameraHdr="on"
|
||||
app:cameraPictureFormat="jpeg"
|
||||
app:cameraPlaySounds="false"
|
||||
app:cameraAudio="off"/>
|
||||
<androidx.camera.view.PreviewView
|
||||
android:id="@+id/camera_preview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_centerInParent="true"/>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -24,11 +18,11 @@
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_hdr"
|
||||
android:id="@+id/image_ratio"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:onClick="onClickHDR"
|
||||
android:src="@drawable/icon_hdr_on"
|
||||
android:onClick="onClickRatio"
|
||||
android:src="@drawable/icon_aspect_ratio"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/image_timer"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@ -41,19 +35,8 @@
|
||||
android:onClick="onClickTimer"
|
||||
android:src="@drawable/icon_timer_off"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/image_grid"
|
||||
app:layout_constraintStart_toEndOf="@id/image_hdr"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_grid"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:onClick="onClickGrid"
|
||||
android:src="@drawable/icon_grid_off"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/image_close"
|
||||
app:layout_constraintStart_toEndOf="@+id/image_timer"
|
||||
app:layout_constraintEnd_toStartOf="@id/image_close"
|
||||
app:layout_constraintStart_toEndOf="@id/image_ratio"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
@ -64,7 +47,7 @@
|
||||
android:src="@drawable/icon_close"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/image_grid"
|
||||
app:layout_constraintStart_toEndOf="@id/image_timer"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
@ -162,10 +162,6 @@
|
||||
<string name="move_success_msg">The selected items have been successfully moved.</string>
|
||||
<string name="move_success">Move successful !</string>
|
||||
<string name="enter_timer_duration">Enter the timer duration (in s)</string>
|
||||
<string name="grid_none">None</string>
|
||||
<string name="grid_3x3">3x3</string>
|
||||
<string name="grid_4x4">4x4</string>
|
||||
<string name="choose_grid">Choose grid</string>
|
||||
<string name="timer_empty_error_msg">Please enter a numeric value</string>
|
||||
<string name="path_from_uri_null_error_msg">Failed to retrieve the selected path.</string>
|
||||
<string name="create_cant_write_error_msg">DroidFS doesn\'t have write access to this path. Please try another location.</string>
|
||||
@ -193,4 +189,6 @@
|
||||
<string name="hidden_volume">Hidden Volume</string>
|
||||
<string name="error_slash_in_name">Volume name cannot contain slashes</string>
|
||||
<string name="hidden_volume_warning">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 !</string>
|
||||
<string name="camera_perm_needed">Camera permission is needed to take photo.</string>
|
||||
<string name="choose_resolution">Choose a resolution</string>
|
||||
</resources>
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user