Update dependencies & Add camera capture mode settings

This commit is contained in:
Matéo Duparc 2021-09-01 19:31:25 +02:00
parent ba42938f5a
commit b65ac79175
Signed by untrusted user: hardcoresushi
GPG Key ID: 007F84120107191E
14 changed files with 143 additions and 83 deletions

View File

@ -56,8 +56,8 @@ dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"]) implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.core:core-ktx:1.6.0" implementation "androidx.core:core-ktx:1.6.0"
implementation "androidx.appcompat:appcompat:1.3.0" implementation "androidx.appcompat:appcompat:1.3.1"
implementation "androidx.constraintlayout:constraintlayout:2.0.4" implementation "androidx.constraintlayout:constraintlayout:2.1.0"
implementation "androidx.sqlite:sqlite-ktx:2.1.0" implementation "androidx.sqlite:sqlite-ktx:2.1.0"
implementation "androidx.preference:preference-ktx:1.1.1" implementation "androidx.preference:preference-ktx:1.1.1"
@ -66,14 +66,14 @@ dependencies {
implementation "com.github.bumptech.glide:glide:4.12.0" implementation "com.github.bumptech.glide:glide:4.12.0"
implementation "androidx.biometric:biometric:1.1.0" implementation "androidx.biometric:biometric:1.1.0"
def exoplayer_version = "2.14.1" def exoplayer_version = "2.15.0"
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version" implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
implementation "com.google.android.exoplayer:exoplayer-ui:$exoplayer_version" implementation "com.google.android.exoplayer:exoplayer-ui:$exoplayer_version"
def camerax_v1 = "1.1.0-alpha06" def camerax_v1 = "1.1.0-alpha08"
implementation "androidx.camera:camera-camera2:$camerax_v1" implementation "androidx.camera:camera-camera2:$camerax_v1"
implementation "androidx.camera:camera-lifecycle:$camerax_v1" implementation "androidx.camera:camera-lifecycle:$camerax_v1"
def camerax_v2 = "1.0.0-alpha26" def camerax_v2 = "1.0.0-alpha28"
implementation "androidx.camera:camera-view:$camerax_v2" implementation "androidx.camera:camera-view:$camerax_v2"
implementation "androidx.camera:camera-extensions:$camerax_v2" implementation "androidx.camera:camera-extensions:$camerax_v2"
} }

View File

@ -8,7 +8,6 @@ import android.hardware.camera2.CameraManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.InputType import android.text.InputType
import android.util.DisplayMetrics
import android.util.Size import android.util.Size
import android.view.MotionEvent import android.view.MotionEvent
import android.view.ScaleGestureDetector import android.view.ScaleGestureDetector
@ -23,7 +22,8 @@ import android.widget.RelativeLayout
import android.widget.Toast import android.widget.Toast
import androidx.camera.camera2.interop.Camera2CameraInfo import androidx.camera.camera2.interop.Camera2CameraInfo
import androidx.camera.core.* import androidx.camera.core.*
import androidx.camera.extensions.HdrImageCaptureExtender import androidx.camera.extensions.ExtensionMode
import androidx.camera.extensions.ExtensionsManager
import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import sushi.hardcore.droidfs.adapters.DialogSingleChoiceAdapter import sushi.hardcore.droidfs.adapters.DialogSingleChoiceAdapter
@ -35,8 +35,7 @@ import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import java.util.concurrent.ExecutorService import java.util.concurrent.Executor
import java.util.concurrent.Executors
class CameraActivity : BaseActivity(), SensorOrientationListener.Listener { class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
companion object { companion object {
@ -64,10 +63,16 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
private lateinit var outputDirectory: String private lateinit var outputDirectory: String
private lateinit var fileName: String private lateinit var fileName: String
private var isFinishingIntentionally = false private var isFinishingIntentionally = false
private lateinit var cameraExecutor: ExecutorService private var permissionsGranted = false
private lateinit var executor: Executor
private lateinit var cameraProvider: ProcessCameraProvider
private lateinit var extensionsManager: ExtensionsManager
private val cameraPreview = Preview.Builder().build()
private var imageCapture: ImageCapture? = null private var imageCapture: ImageCapture? = null
private var resolutions: Array<Size>? = null private var camera: Camera? = null
private var resolutions: List<Size>? = null
private var currentResolutionIndex: Int = 0 private var currentResolutionIndex: Int = 0
private var captureMode = ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY
private var isBackCamera = true private var isBackCamera = true
private lateinit var binding: ActivityCameraBinding private lateinit var binding: ActivityCameraBinding
@ -81,21 +86,61 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){ if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){
setupCamera() permissionsGranted = true
} else { } else {
requestPermissions(arrayOf(Manifest.permission.CAMERA), CAMERA_PERMISSION_REQUEST_CODE) requestPermissions(arrayOf(Manifest.permission.CAMERA), CAMERA_PERMISSION_REQUEST_CODE)
} }
} else { } else {
setupCamera() permissionsGranted = true
} }
cameraExecutor = Executors.newSingleThreadExecutor() executor = ContextCompat.getMainExecutor(this)
cameraPreview.setSurfaceProvider(binding.cameraPreview.surfaceProvider)
ProcessCameraProvider.getInstance(this).apply {
addListener({
cameraProvider = get()
setupCamera()
}, executor)
}
ExtensionsManager.getInstance(this).apply {
addListener({
extensionsManager = get()
setupCamera()
}, executor)
}
binding.imageCaptureMode.setOnClickListener {
val currentIndex = if (captureMode == ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY) {
0
} else {
1
}
ColoredAlertDialogBuilder(this)
.setTitle(R.string.camera_optimization)
.setSingleChoiceItems(DialogSingleChoiceAdapter(this, arrayOf(R.string.maximize_quality, R.string.minimize_latency).map { getString(it) }), currentIndex) { dialog, which ->
val resId: Int
val newCaptureMode = if (which == 0) {
resId = R.drawable.icon_high_quality
ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY
} else {
resId = R.drawable.icon_speed
ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY
}
if (newCaptureMode != captureMode) {
captureMode = newCaptureMode
binding.imageCaptureMode.setImageResource(resId)
setupCamera()
}
dialog.dismiss()
}
.setNegativeButton(R.string.cancel, null)
.show()
}
binding.imageRatio.setOnClickListener { binding.imageRatio.setOnClickListener {
resolutions?.let { resolutions?.let {
ColoredAlertDialogBuilder(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.choose_resolution) .setTitle(R.string.choose_resolution)
.setSingleChoiceItems(DialogSingleChoiceAdapter(this, it.map { size -> size.toString() }.toTypedArray()), currentResolutionIndex) { dialog, which -> .setSingleChoiceItems(DialogSingleChoiceAdapter(this, it.map { size -> size.toString() }), currentResolutionIndex) { dialog, which ->
setupCamera(resolutions!![which]) setupCamera(resolutions!![which])
dialog.dismiss() dialog.dismiss()
currentResolutionIndex = which currentResolutionIndex = which
@ -151,10 +196,10 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
} }
binding.imageCameraSwitch.setOnClickListener { binding.imageCameraSwitch.setOnClickListener {
isBackCamera = if (isBackCamera) { isBackCamera = if (isBackCamera) {
binding.imageCameraSwitch.setImageResource(R.drawable.icon_camera_front) binding.imageCameraSwitch.setImageResource(R.drawable.icon_camera_back)
false false
} else { } else {
binding.imageCameraSwitch.setImageResource(R.drawable.icon_camera_back) binding.imageCameraSwitch.setImageResource(R.drawable.icon_camera_front)
true true
} }
setupCamera() setupCamera()
@ -165,8 +210,8 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
val scaleGestureDetector = ScaleGestureDetector(this, object : ScaleGestureDetector.SimpleOnScaleGestureListener(){ val scaleGestureDetector = ScaleGestureDetector(this, object : ScaleGestureDetector.SimpleOnScaleGestureListener(){
override fun onScale(detector: ScaleGestureDetector): Boolean { override fun onScale(detector: ScaleGestureDetector): Boolean {
val currentZoomRatio = imageCapture?.camera?.cameraInfo?.zoomState?.value?.zoomRatio ?: 0F val currentZoomRatio = camera?.cameraInfo?.zoomState?.value?.zoomRatio ?: 0F
imageCapture?.camera?.cameraControl?.setZoomRatio(currentZoomRatio*detector.scaleFactor) camera?.cameraControl?.setZoomRatio(currentZoomRatio*detector.scaleFactor)
return true return true
} }
}) })
@ -178,7 +223,7 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
val factory = binding.cameraPreview.meteringPointFactory val factory = binding.cameraPreview.meteringPointFactory
val point = factory.createPoint(event.x, event.y) val point = factory.createPoint(event.x, event.y)
val action = FocusMeteringAction.Builder(point).build() val action = FocusMeteringAction.Builder(point).build()
imageCapture?.camera?.cameraControl?.startFocusAndMetering(action) camera?.cameraControl?.startFocusAndMetering(action)
true true
} }
MotionEvent.ACTION_MOVE -> scaleGestureDetector.onTouchEvent(event) MotionEvent.ACTION_MOVE -> scaleGestureDetector.onTouchEvent(event)
@ -191,7 +236,10 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
super.onRequestPermissionsResult(requestCode, permissions, grantResults) super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) { when (requestCode) {
CAMERA_PERMISSION_REQUEST_CODE -> if (grantResults.size == 1) { CAMERA_PERMISSION_REQUEST_CODE -> if (grantResults.size == 1) {
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
permissionsGranted = true
setupCamera()
} else {
ColoredAlertDialogBuilder(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.error) .setTitle(R.string.error)
.setMessage(R.string.camera_perm_needed) .setMessage(R.string.camera_perm_needed)
@ -200,72 +248,62 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
isFinishingIntentionally = true isFinishingIntentionally = true
finish() finish()
}.show() }.show()
} else {
setupCamera()
} }
} }
} }
} }
private fun adaptPreviewSize(resolution: Size) { private fun adaptPreviewSize(resolution: Size) {
val metrics = DisplayMetrics() val screenWidth = resources.displayMetrics.widthPixels
windowManager.defaultDisplay.getMetrics(metrics) binding.cameraPreview.layoutParams = if (screenWidth < resolution.width) {
//resolution.width and resolution.height seem to be inverted
val width = resolution.height
val height = resolution.width
binding.cameraPreview.layoutParams = if (metrics.widthPixels < width){
RelativeLayout.LayoutParams( RelativeLayout.LayoutParams(
metrics.widthPixels, screenWidth,
(height * (metrics.widthPixels.toFloat() / width)).toInt() (resolution.height * (screenWidth.toFloat() / resolution.width)).toInt()
) )
} else { } else {
RelativeLayout.LayoutParams(width, height) RelativeLayout.LayoutParams(resolution.width, resolution.height)
} }
(binding.cameraPreview.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.CENTER_IN_PARENT) (binding.cameraPreview.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.CENTER_IN_PARENT)
} }
private fun setupCamera(resolution: Size? = null){ private fun setupCamera(resolution: Size? = null){
val cameraProviderFuture = ProcessCameraProvider.getInstance(this) if (permissionsGranted && ::extensionsManager.isInitialized && ::cameraProvider.isInitialized) {
cameraProviderFuture.addListener({ imageCapture = ImageCapture.Builder()
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get() .setCaptureMode(captureMode)
val preview = Preview.Builder() .setFlashMode(imageCapture?.flashMode ?: ImageCapture.FLASH_MODE_AUTO)
.build() .apply {
.also { resolution?.let {
it.setSurfaceProvider(binding.cameraPreview.surfaceProvider) setTargetResolution(it)
}
} }
val builder = ImageCapture.Builder() .build()
.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)){ var cameraSelector = if (isBackCamera){ CameraSelector.DEFAULT_BACK_CAMERA } else { CameraSelector.DEFAULT_FRONT_CAMERA }
hdrImageCapture.enableExtension(cameraSelector) if (extensionsManager.isExtensionAvailable(cameraProvider, cameraSelector, ExtensionMode.HDR)) {
cameraSelector = extensionsManager.getExtensionEnabledCameraSelector(cameraProvider, cameraSelector, ExtensionMode.HDR)
} }
imageCapture = builder.build()
cameraProvider.unbindAll() cameraProvider.unbindAll()
val camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture) camera = cameraProvider.bindToLifecycle(this, cameraSelector, cameraPreview, imageCapture)
adaptPreviewSize(imageCapture!!.attachedSurfaceResolution!!) adaptPreviewSize(resolution ?: imageCapture!!.attachedSurfaceResolution!!.swap())
val info = Camera2CameraInfo.from(camera.cameraInfo) if (resolutions == null) {
val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager val info = Camera2CameraInfo.from(camera!!.cameraInfo)
val characteristics = cameraManager.getCameraCharacteristics(info.cameraId) val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)?.let { streamConfigurationMap -> val characteristics = cameraManager.getCameraCharacteristics(info.cameraId)
resolutions = streamConfigurationMap.getOutputSizes(imageCapture!!.imageFormat) characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)?.let { streamConfigurationMap ->
resolutions = streamConfigurationMap.getOutputSizes(imageCapture!!.imageFormat).map { it.swap() }
}
} }
}, ContextCompat.getMainExecutor(this)) }
} }
private fun takePhoto() { private fun takePhoto() {
val imageCapture = imageCapture ?: return val imageCapture = imageCapture ?: return
val outputBuff = ByteArrayOutputStream() val outputBuff = ByteArrayOutputStream()
val outputOptions = ImageCapture.OutputFileOptions.Builder(outputBuff).build() val outputOptions = ImageCapture.OutputFileOptions.Builder(outputBuff).build()
imageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback { imageCapture.takePicture(outputOptions, executor, object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
binding.takePhotoButton.onPhotoTaken() binding.takePhotoButton.onPhotoTaken()
if (gocryptfsVolume.importFile(ByteArrayInputStream(outputBuff.toByteArray()), PathUtils.pathJoin(outputDirectory, fileName))){ if (gocryptfsVolume.importFile(ByteArrayInputStream(outputBuff.toByteArray()), PathUtils.pathJoin(outputDirectory, fileName))){
@ -313,7 +351,6 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
cameraExecutor.shutdown()
if (!isFinishingIntentionally) { if (!isFinishingIntentionally) {
gocryptfsVolume.close() gocryptfsVolume.close()
RestrictedFileProvider.wipeAll(this) RestrictedFileProvider.wipeAll(this)
@ -365,4 +402,8 @@ class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
orientedIcons.map { it.startAnimation(rotateAnimation) } orientedIcons.map { it.startAnimation(rotateAnimation) }
previousOrientation = reversedOrientation previousOrientation = reversedOrientation
} }
}
private fun Size.swap(): Size {
return Size(height, width)
} }

View File

@ -9,7 +9,7 @@ import android.widget.CheckedTextView
import sushi.hardcore.droidfs.R import sushi.hardcore.droidfs.R
import sushi.hardcore.droidfs.widgets.ThemeColor import sushi.hardcore.droidfs.widgets.ThemeColor
class DialogSingleChoiceAdapter(private val context: Context, private val entries: Array<String>): BaseAdapter() { class DialogSingleChoiceAdapter(private val context: Context, private val entries: List<String>): BaseAdapter() {
private val inflater: LayoutInflater = LayoutInflater.from(context) private val inflater: LayoutInflater = LayoutInflater.from(context)
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val view: View = convertView ?: inflater.inflate(R.layout.adapter_colored_dialog_single_choice, parent, false) val view: View = convertView ?: inflater.inflate(R.layout.adapter_colored_dialog_single_choice, parent, false)

View File

@ -486,7 +486,7 @@ open class BaseExplorerActivity : BaseActivity() {
R.id.sort -> { R.id.sort -> {
ColoredAlertDialogBuilder(this) ColoredAlertDialogBuilder(this)
.setTitle(R.string.sort_order) .setTitle(R.string.sort_order)
.setSingleChoiceItems(DialogSingleChoiceAdapter(this, sortOrderEntries), currentSortOrderIndex) { dialog, which -> .setSingleChoiceItems(DialogSingleChoiceAdapter(this, sortOrderEntries.toList()), currentSortOrderIndex) { dialog, which ->
currentSortOrderIndex = which currentSortOrderIndex = which
setCurrentPath(currentDirectoryPath) setCurrentPath(currentDirectoryPath)
dialog.dismiss() dialog.dismiss()

View File

@ -1,10 +1,7 @@
package sushi.hardcore.droidfs.file_viewers package sushi.hardcore.droidfs.file_viewers
import android.view.WindowManager import android.view.WindowManager
import com.google.android.exoplayer2.ExoPlaybackException import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.extractor.ExtractorsFactory import com.google.android.exoplayer2.extractor.ExtractorsFactory
import com.google.android.exoplayer2.extractor.flac.FlacExtractor import com.google.android.exoplayer2.extractor.flac.FlacExtractor
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor
@ -58,15 +55,13 @@ abstract class MediaPlayer: FileViewerActivity() {
onPlayerReady() onPlayerReady()
} }
} }
override fun onPlayerError(error: ExoPlaybackException) { override fun onPlayerError(error: PlaybackException) {
if (error.type == ExoPlaybackException.TYPE_SOURCE){ ColoredAlertDialogBuilder(this@MediaPlayer)
ColoredAlertDialogBuilder(this@MediaPlayer) .setTitle(R.string.error)
.setTitle(R.string.error) .setMessage(getString(R.string.playing_failed, error.errorCodeName))
.setMessage(R.string.playing_failed) .setCancelable(false)
.setCancelable(false) .setPositiveButton(R.string.ok) { _, _ -> goBackToExplorer()}
.setPositiveButton(R.string.ok) { _, _ -> goBackToExplorer()} .show()
.show()
}
} }
override fun onIsPlayingChanged(isPlaying: Boolean) { override fun onIsPlayingChanged(isPlaying: Boolean) {
if (isPlaying){ if (isPlaying){

View File

@ -19,7 +19,7 @@ class ColoredListPreference: ListPreference {
override fun onClick() { override fun onClick() {
ColoredAlertDialogBuilder(context) ColoredAlertDialogBuilder(context)
.setTitle(title) .setTitle(title)
.setSingleChoiceItems(DialogSingleChoiceAdapter(context, entries.map { s -> s.toString() }.toTypedArray()), entryValues.indexOf(getPersistedString(value))) { dialog, which -> .setSingleChoiceItems(DialogSingleChoiceAdapter(context, entries.map { s -> s.toString() }), entryValues.indexOf(getPersistedString(value))) { dialog, which ->
dialog.dismiss() dialog.dismiss()
summary = entries[which].toString() summary = entries[which].toString()
persistString(entryValues[which].toString()) persistString(entryValues[which].toString())

View File

@ -0,0 +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="M19,4L5,4c-1.11,0 -2,0.9 -2,2v12c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,6c0,-1.1 -0.9,-2 -2,-2zM11,15L9.5,15v-2h-2v2L6,15L6,9h1.5v2.5h2L9.5,9L11,9v6zM18,14c0,0.55 -0.45,1 -1,1h-0.75v1.5h-1.5L14.75,15L14,15c-0.55,0 -1,-0.45 -1,-1v-4c0,-0.55 0.45,-1 1,-1h3c0.55,0 1,0.45 1,1v4zM14.5,13.5h2v-3h-2v3z"/>
</vector>

View File

@ -0,0 +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.38,8.57l-1.23,1.85a8,8 0,0 1,-0.22 7.58L5.07,18A8,8 0,0 1,15.58 6.85l1.85,-1.23A10,10 0,0 0,3.35 19a2,2 0,0 0,1.72 1h13.85a2,2 0,0 0,1.74 -1,10 10,0 0,0 -0.27,-10.44zM10.59,15.41a2,2 0,0 0,2.83 0l5.66,-8.49 -8.49,5.66a2,2 0,0 0,0 2.83z"/>
</vector>

View File

@ -17,6 +17,16 @@
android:layout_height="40dp" android:layout_height="40dp"
android:layout_marginTop="20dp"> android:layout_marginTop="20dp">
<ImageView
android:id="@+id/image_capture_mode"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/icon_high_quality"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/image_ratio"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<ImageView <ImageView
android:id="@+id/image_ratio" android:id="@+id/image_ratio"
android:layout_width="30dp" android:layout_width="30dp"
@ -24,7 +34,7 @@
android:src="@drawable/icon_aspect_ratio" android:src="@drawable/icon_aspect_ratio"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/image_timer" app:layout_constraintEnd_toStartOf="@id/image_timer"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toEndOf="@id/image_capture_mode"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<ImageView <ImageView

View File

@ -116,7 +116,7 @@
<string name="video">Vídeo</string> <string name="video">Vídeo</string>
<string name="audio">Áudio</string> <string name="audio">Áudio</string>
<string name="media_player_prepare_failed">Falha ao abrir o reprodutor de mídia.</string> <string name="media_player_prepare_failed">Falha ao abrir o reprodutor de mídia.</string>
<string name="playing_failed">Falha ao reproduzir este arquivo.</string> <string name="playing_failed">Falha ao reproduzir este arquivo: %s</string>
<string name="text">Texto</string> <string name="text">Texto</string>
<string name="save_failed">Falha ao salvar</string> <string name="save_failed">Falha ao salvar</string>
<string name="file_saved">Arquivo salvo!</string> <string name="file_saved">Arquivo salvo!</string>

View File

@ -117,7 +117,7 @@
<string name="video">Видео</string> <string name="video">Видео</string>
<string name="audio">Аудио</string> <string name="audio">Аудио</string>
<string name="media_player_prepare_failed">Невозможно инициализировать медиапроигрыватель.</string> <string name="media_player_prepare_failed">Невозможно инициализировать медиапроигрыватель.</string>
<string name="playing_failed">Невозможно воспроизвести файл.</string> <string name="playing_failed">Невозможно воспроизвести файл: %s</string>
<string name="text">Текст</string> <string name="text">Текст</string>
<string name="save_failed">Невозможно сохранить.</string> <string name="save_failed">Невозможно сохранить.</string>
<string name="file_saved">Файл сохранён!</string> <string name="file_saved">Файл сохранён!</string>

View File

@ -118,7 +118,7 @@
<string name="video">Video</string> <string name="video">Video</string>
<string name="audio">Audio</string> <string name="audio">Audio</string>
<string name="media_player_prepare_failed">Failed to initialize the media player.</string> <string name="media_player_prepare_failed">Failed to initialize the media player.</string>
<string name="playing_failed">Failed to play this file.</string> <string name="playing_failed">Failed to play this file: %s</string>
<string name="text">Text</string> <string name="text">Text</string>
<string name="save_failed">Save failed</string> <string name="save_failed">Save failed</string>
<string name="file_saved">File saved !</string> <string name="file_saved">File saved !</string>
@ -202,4 +202,7 @@
<string name="cut">Cut</string> <string name="cut">Cut</string>
<string name="map_folders">Map folders</string> <string name="map_folders">Map folders</string>
<string name="map_folders_summary">Recursively map folders to calculate their sizes (you should disable this when opening large volumes)</string> <string name="map_folders_summary">Recursively map folders to calculate their sizes (you should disable this when opening large volumes)</string>
<string name="camera_optimization">Camera optimization</string>
<string name="maximize_quality">Maximize quality</string>
<string name="minimize_latency">Minimize latency</string>
</resources> </resources>

View File

@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = "1.5.20" ext.kotlin_version = "1.5.30"
repositories { repositories {
google() google()
mavenCentral() mavenCentral()

View File

@ -1,5 +1,6 @@
#Wed Sep 01 11:25:55 UTC 2021
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME