forked from hardcoresushi/DroidFS
Update dependencies & Add camera capture mode settings
This commit is contained in:
parent
ba42938f5a
commit
b65ac79175
@ -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"
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
@ -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)
|
||||||
|
@ -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()
|
||||||
|
@ -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){
|
||||||
|
@ -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())
|
||||||
|
5
app/src/main/res/drawable/icon_high_quality.xml
Normal file
5
app/src/main/res/drawable/icon_high_quality.xml
Normal 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>
|
5
app/src/main/res/drawable/icon_speed.xml
Normal file
5
app/src/main/res/drawable/icon_speed.xml
Normal 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>
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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()
|
||||||
|
5
gradle/wrapper/gradle-wrapper.properties
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user