DroidFS/app/src/main/java/sushi/hardcore/droidfs/CameraActivity.kt

485 lines
21 KiB
Kotlin
Raw Normal View History

2020-08-05 14:06:54 +02:00
package sushi.hardcore.droidfs
2020-08-12 16:14:26 +02:00
import android.Manifest
2021-10-03 14:36:06 +02:00
import android.annotation.SuppressLint
import android.content.Context
2020-08-12 16:14:26 +02:00
import android.content.pm.PackageManager
2020-12-19 19:55:54 +01:00
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.os.Build
2020-08-05 14:06:54 +02:00
import android.os.Bundle
2020-08-09 15:27:31 +02:00
import android.text.InputType
2020-12-19 19:55:54 +01:00
import android.util.Size
import android.view.MotionEvent
import android.view.ScaleGestureDetector
2020-08-05 14:06:54 +02:00
import android.view.View
2020-08-09 15:27:31 +02:00
import android.view.WindowManager
2020-09-04 04:00:19 +02:00
import android.view.animation.Animation
import android.view.animation.LinearInterpolator
import android.view.animation.RotateAnimation
2020-08-09 15:27:31 +02:00
import android.widget.EditText
2020-09-04 04:00:19 +02:00
import android.widget.ImageView
2020-12-19 19:55:54 +01:00
import android.widget.RelativeLayout
2020-08-05 14:06:54 +02:00
import android.widget.Toast
2021-10-03 14:36:06 +02:00
import androidx.annotation.RequiresApi
2020-12-19 19:55:54 +01:00
import androidx.camera.camera2.interop.Camera2CameraInfo
import androidx.camera.core.*
2021-10-03 14:36:06 +02:00
//import androidx.camera.core.VideoCapture
import androidx.camera.extensions.ExtensionMode
import androidx.camera.extensions.ExtensionsManager
2020-12-19 19:55:54 +01:00
import androidx.camera.lifecycle.ProcessCameraProvider
2021-10-03 14:36:06 +02:00
import androidx.core.app.ActivityCompat
2020-08-12 16:14:26 +02:00
import androidx.core.content.ContextCompat
2020-12-19 19:55:54 +01:00
import sushi.hardcore.droidfs.adapters.DialogSingleChoiceAdapter
import sushi.hardcore.droidfs.content_providers.RestrictedFileProvider
2021-06-11 20:23:54 +02:00
import sushi.hardcore.droidfs.databinding.ActivityCameraBinding
2020-08-05 14:06:54 +02:00
import sushi.hardcore.droidfs.util.PathUtils
2021-10-03 14:36:06 +02:00
import sushi.hardcore.droidfs.video_recording.SeekableWriter
import sushi.hardcore.droidfs.video_recording.VideoCapture
2021-11-09 11:12:09 +01:00
import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
2021-10-03 14:36:06 +02:00
import java.io.*
2020-08-05 14:06:54 +02:00
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.Executor
2020-08-05 14:06:54 +02:00
2020-09-04 04:00:19 +02:00
class CameraActivity : BaseActivity(), SensorOrientationListener.Listener {
2020-08-05 14:06:54 +02:00
companion object {
2021-10-03 14:36:06 +02:00
private const val CAMERA_PERMISSION_REQUEST_CODE = 0
private const val AUDIO_PERMISSION_REQUEST_CODE = 1
2020-08-05 14:06:54 +02:00
private const val fileNameRandomMin = 100000
private const val fileNameRandomMax = 999999
2021-06-07 16:34:50 +02:00
private val dateFormat = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US)
2020-08-05 14:06:54 +02:00
private val random = Random()
}
2021-06-11 20:23:54 +02:00
2020-08-09 15:27:31 +02:00
private var timerDuration = 0
set(value) {
field = value
if (value > 0){
2021-06-11 20:23:54 +02:00
binding.imageTimer.setImageResource(R.drawable.icon_timer_on)
2020-08-09 15:27:31 +02:00
} else {
2021-06-11 20:23:54 +02:00
binding.imageTimer.setImageResource(R.drawable.icon_timer_off)
2020-08-09 15:27:31 +02:00
}
}
private var usf_keep_open = false
2020-09-04 04:00:19 +02:00
private lateinit var sensorOrientationListener: SensorOrientationListener
private var previousOrientation: Float = 0f
private lateinit var orientedIcons: List<ImageView>
2020-08-05 14:06:54 +02:00
private lateinit var gocryptfsVolume: GocryptfsVolume
private lateinit var outputDirectory: String
private var isFinishingIntentionally = false
2021-10-03 14:36:06 +02:00
private var isAskingPermissions = false
private var permissionsGranted = false
private lateinit var executor: Executor
private lateinit var cameraProvider: ProcessCameraProvider
private lateinit var extensionsManager: ExtensionsManager
2021-10-03 14:36:06 +02:00
private lateinit var cameraSelector: CameraSelector
private val cameraPreview = Preview.Builder().build()
2020-12-19 19:55:54 +01:00
private var imageCapture: ImageCapture? = null
2021-10-03 14:36:06 +02:00
private var videoCapture: VideoCapture? = null
private var camera: Camera? = null
private var resolutions: List<Size>? = null
2020-12-19 19:55:54 +01:00
private var currentResolutionIndex: Int = 0
private var captureMode = ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY
2020-12-19 19:55:54 +01:00
private var isBackCamera = true
2021-10-03 14:36:06 +02:00
private var isInVideoMode = false
private var isRecording = false
2021-06-11 20:23:54 +02:00
private lateinit var binding: ActivityCameraBinding
2020-08-05 14:06:54 +02:00
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
usf_keep_open = sharedPrefs.getBoolean("usf_keep_open", false)
2021-06-11 20:23:54 +02:00
binding = ActivityCameraBinding.inflate(layoutInflater)
setContentView(binding.root)
2021-11-11 15:05:33 +01:00
gocryptfsVolume = GocryptfsVolume(applicationContext, intent.getIntExtra("sessionID", -1))
2020-08-05 14:06:54 +02:00
outputDirectory = intent.getStringExtra("path")!!
2020-12-19 19:55:54 +01:00
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){
permissionsGranted = true
2020-12-19 19:55:54 +01:00
} else {
2021-10-03 14:36:06 +02:00
isAskingPermissions = true
2020-12-19 19:55:54 +01:00
requestPermissions(arrayOf(Manifest.permission.CAMERA), CAMERA_PERMISSION_REQUEST_CODE)
}
} else {
permissionsGranted = true
2020-12-19 19:55:54 +01:00
}
executor = ContextCompat.getMainExecutor(this)
cameraPreview.setSurfaceProvider(binding.cameraPreview.surfaceProvider)
ProcessCameraProvider.getInstance(this).apply {
addListener({
cameraProvider = get()
2021-12-20 14:44:58 +01:00
ExtensionsManager.getInstanceAsync(this@CameraActivity, cameraProvider).apply {
addListener({
extensionsManager = get()
setupCamera()
}, executor)
}
}, executor)
}
2020-12-19 19:55:54 +01:00
binding.imageCaptureMode.setOnClickListener {
val currentIndex = if (captureMode == ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY) {
0
} else {
1
}
2021-11-09 11:12:09 +01:00
CustomAlertDialogBuilder(this, themeValue)
.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()
}
2021-06-11 20:23:54 +02:00
binding.imageRatio.setOnClickListener {
2021-06-07 16:34:50 +02:00
resolutions?.let {
2021-11-09 11:12:09 +01:00
CustomAlertDialogBuilder(this, themeValue)
2021-06-07 16:34:50 +02:00
.setTitle(R.string.choose_resolution)
.setSingleChoiceItems(DialogSingleChoiceAdapter(this, it.map { size -> size.toString() }), currentResolutionIndex) { dialog, which ->
2021-06-07 16:34:50 +02:00
setupCamera(resolutions!![which])
dialog.dismiss()
currentResolutionIndex = which
}
.setNegativeButton(R.string.cancel, null)
.show()
}
}
2021-06-11 20:23:54 +02:00
binding.imageTimer.setOnClickListener {
2021-06-07 16:34:50 +02:00
val dialogEditTextView = layoutInflater.inflate(R.layout.dialog_edit_text, null)
val dialogEditText = dialogEditTextView.findViewById<EditText>(R.id.dialog_edit_text)
dialogEditText.inputType = InputType.TYPE_CLASS_NUMBER
2021-11-09 11:12:09 +01:00
val dialog = CustomAlertDialogBuilder(this, themeValue)
2021-06-07 16:34:50 +02:00
.setView(dialogEditTextView)
.setTitle(getString(R.string.enter_timer_duration))
.setPositiveButton(R.string.ok) { _, _ ->
val enteredValue = dialogEditText.text.toString()
if (enteredValue.isEmpty()){
Toast.makeText(this, getString(R.string.timer_empty_error_msg), Toast.LENGTH_SHORT).show()
} else {
timerDuration = enteredValue.toInt()
}
}
.setNegativeButton(R.string.cancel, null)
.create()
dialogEditText.setOnEditorActionListener { _, _, _ ->
timerDuration = dialogEditText.text.toString().toInt()
dialog.dismiss()
true
}
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
dialog.show()
}
2021-06-11 20:23:54 +02:00
binding.imageFlash.setOnClickListener {
binding.imageFlash.setImageResource(when (imageCapture?.flashMode) {
2021-06-07 16:34:50 +02:00
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
}
})
}
2021-10-03 14:36:06 +02:00
binding.imageModeSwitch.setOnClickListener {
isInVideoMode = !isInVideoMode
if (isInVideoMode) {
binding.recordVideoButton.visibility = View.VISIBLE
binding.takePhotoButton.visibility = View.GONE
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
isAskingPermissions = true
requestPermissions(arrayOf(Manifest.permission.RECORD_AUDIO), AUDIO_PERMISSION_REQUEST_CODE)
}
}
} else {
binding.recordVideoButton.visibility = View.GONE
binding.takePhotoButton.visibility = View.VISIBLE
}
}
2021-06-11 20:23:54 +02:00
binding.imageCameraSwitch.setOnClickListener {
2021-06-07 16:34:50 +02:00
isBackCamera = if (isBackCamera) {
binding.imageCameraSwitch.setImageResource(R.drawable.icon_camera_back)
2021-06-07 16:34:50 +02:00
false
} else {
binding.imageCameraSwitch.setImageResource(R.drawable.icon_camera_front)
2021-06-07 16:34:50 +02:00
true
}
setupCamera()
}
2021-06-11 20:23:54 +02:00
binding.takePhotoButton.onClick = ::onClickTakePhoto
2021-10-03 14:36:06 +02:00
binding.recordVideoButton.setOnClickListener { onClickRecordVideo() }
orientedIcons = listOf(binding.imageRatio, binding.imageTimer, binding.imageCaptureMode, binding.imageFlash, binding.imageModeSwitch, binding.imageCameraSwitch)
2020-12-19 19:55:54 +01:00
sensorOrientationListener = SensorOrientationListener(this)
val scaleGestureDetector = ScaleGestureDetector(this, object : ScaleGestureDetector.SimpleOnScaleGestureListener(){
override fun onScale(detector: ScaleGestureDetector): Boolean {
val currentZoomRatio = camera?.cameraInfo?.zoomState?.value?.zoomRatio ?: 0F
camera?.cameraControl?.setZoomRatio(currentZoomRatio*detector.scaleFactor)
2020-12-19 19:55:54 +01:00
return true
}
})
2021-06-11 20:23:54 +02:00
binding.cameraPreview.setOnTouchListener { view, event ->
2021-06-07 16:34:50 +02:00
view.performClick()
when (event.action) {
2020-12-19 19:55:54 +01:00
MotionEvent.ACTION_DOWN -> true
MotionEvent.ACTION_UP -> {
2021-06-11 20:23:54 +02:00
val factory = binding.cameraPreview.meteringPointFactory
2021-06-07 16:34:50 +02:00
val point = factory.createPoint(event.x, event.y)
2020-12-19 19:55:54 +01:00
val action = FocusMeteringAction.Builder(point).build()
camera?.cameraControl?.startFocusAndMetering(action)
2020-12-19 19:55:54 +01:00
true
}
2021-06-07 16:34:50 +02:00
MotionEvent.ACTION_MOVE -> scaleGestureDetector.onTouchEvent(event)
2020-12-19 19:55:54 +01:00
else -> false
}
}
}
2021-10-03 14:36:06 +02:00
@RequiresApi(Build.VERSION_CODES.M)
2020-12-19 19:55:54 +01:00
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
2021-06-07 16:34:50 +02:00
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
2021-10-03 14:36:06 +02:00
isAskingPermissions = false
if (grantResults.size == 1) {
when (requestCode) {
CAMERA_PERMISSION_REQUEST_CODE -> if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
permissionsGranted = true
setupCamera()
} else {
2021-11-09 11:12:09 +01:00
CustomAlertDialogBuilder(this, themeValue)
2020-12-19 19:55:54 +01:00
.setTitle(R.string.error)
.setMessage(R.string.camera_perm_needed)
.setCancelable(false)
.setPositiveButton(R.string.ok) { _, _ ->
isFinishingIntentionally = true
finish()
}.show()
}
2021-10-03 14:36:06 +02:00
AUDIO_PERMISSION_REQUEST_CODE -> if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (videoCapture != null) {
cameraProvider.unbind(videoCapture)
camera = cameraProvider.bindToLifecycle(this, cameraSelector, videoCapture)
}
}
2020-12-19 19:55:54 +01:00
}
}
}
2021-06-07 16:34:50 +02:00
private fun adaptPreviewSize(resolution: Size) {
val screenWidth = resources.displayMetrics.widthPixels
binding.cameraPreview.layoutParams = if (screenWidth < resolution.width) {
2020-12-19 19:55:54 +01:00
RelativeLayout.LayoutParams(
screenWidth,
(resolution.height * (screenWidth.toFloat() / resolution.width)).toInt()
2020-12-19 19:55:54 +01:00
)
} else {
RelativeLayout.LayoutParams(resolution.width, resolution.height)
2020-12-19 19:55:54 +01:00
}
2021-06-11 20:23:54 +02:00
(binding.cameraPreview.layoutParams as RelativeLayout.LayoutParams).addRule(RelativeLayout.CENTER_IN_PARENT)
2020-12-19 19:55:54 +01:00
}
2021-10-03 14:36:06 +02:00
@SuppressLint("RestrictedApi")
2020-12-19 19:55:54 +01:00
private fun setupCamera(resolution: Size? = null){
if (permissionsGranted && ::extensionsManager.isInitialized && ::cameraProvider.isInitialized) {
imageCapture = ImageCapture.Builder()
.setCaptureMode(captureMode)
.setFlashMode(imageCapture?.flashMode ?: ImageCapture.FLASH_MODE_AUTO)
.apply {
resolution?.let {
setTargetResolution(it)
}
2020-12-19 19:55:54 +01:00
}
.build()
2020-12-19 19:55:54 +01:00
2021-10-03 14:36:06 +02:00
videoCapture = VideoCapture.Builder().apply {
resolution?.let {
setTargetResolution(it)
}
}.build()
cameraSelector = if (isBackCamera){ CameraSelector.DEFAULT_BACK_CAMERA } else { CameraSelector.DEFAULT_FRONT_CAMERA }
2021-12-20 14:44:58 +01:00
if (extensionsManager.isExtensionAvailable(cameraSelector, ExtensionMode.HDR)) {
cameraSelector = extensionsManager.getExtensionEnabledCameraSelector(cameraSelector, ExtensionMode.HDR)
2020-12-19 19:55:54 +01:00
}
cameraProvider.unbindAll()
2021-10-03 14:36:06 +02:00
camera = cameraProvider.bindToLifecycle(this, cameraSelector, cameraPreview, imageCapture, videoCapture)
2020-12-19 19:55:54 +01:00
adaptPreviewSize(resolution ?: imageCapture!!.attachedSurfaceResolution!!.swap())
2020-12-19 19:55:54 +01:00
if (resolutions == null) {
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).map { it.swap() }
}
2020-12-19 19:55:54 +01:00
}
}
2020-12-19 19:55:54 +01:00
}
2021-10-03 14:36:06 +02:00
private fun takePhoto(outputPath: String) {
2020-12-19 19:55:54 +01:00
val imageCapture = imageCapture ?: return
val outputBuff = ByteArrayOutputStream()
val outputOptions = ImageCapture.OutputFileOptions.Builder(outputBuff).build()
imageCapture.takePicture(outputOptions, executor, object : ImageCapture.OnImageSavedCallback {
2020-12-19 19:55:54 +01:00
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
2021-06-11 20:23:54 +02:00
binding.takePhotoButton.onPhotoTaken()
2021-10-03 14:36:06 +02:00
if (gocryptfsVolume.importFile(ByteArrayInputStream(outputBuff.toByteArray()), outputPath)){
Toast.makeText(applicationContext, getString(R.string.picture_save_success, outputPath), Toast.LENGTH_SHORT).show()
2020-08-05 14:06:54 +02:00
} else {
2021-11-09 11:12:09 +01:00
CustomAlertDialogBuilder(this@CameraActivity, themeValue)
2020-08-05 14:06:54 +02:00
.setTitle(R.string.error)
.setMessage(R.string.picture_save_failed)
.setCancelable(false)
.setPositiveButton(R.string.ok) { _, _ ->
isFinishingIntentionally = true
finish()
}
2020-08-05 14:06:54 +02:00
.show()
}
}
2020-12-19 19:55:54 +01:00
override fun onError(exception: ImageCaptureException) {
2021-06-11 20:23:54 +02:00
binding.takePhotoButton.onPhotoTaken()
2020-12-19 19:55:54 +01:00
Toast.makeText(applicationContext, exception.message, Toast.LENGTH_SHORT).show()
}
2020-08-05 14:06:54 +02:00
})
}
2021-10-03 14:36:06 +02:00
private fun getOutputPath(isVideo: Boolean): String {
val baseName = if (isVideo) {"VID"} else {"IMG"}+'_'+dateFormat.format(Date())+'_'
var fileName: String
2020-08-05 14:06:54 +02:00
do {
2021-10-03 14:36:06 +02:00
fileName = baseName+(random.nextInt(fileNameRandomMax-fileNameRandomMin)+fileNameRandomMin)+'.'+ if (isVideo) {"mp4"} else {"jpg"}
2020-08-05 14:06:54 +02:00
} while (gocryptfsVolume.pathExists(fileName))
2021-10-03 14:36:06 +02:00
return PathUtils.pathJoin(outputDirectory, fileName)
}
private fun onClickTakePhoto() {
val path = getOutputPath(false)
2020-08-09 15:27:31 +02:00
if (timerDuration > 0){
2021-06-11 20:23:54 +02:00
binding.textTimer.visibility = View.VISIBLE
2020-08-09 15:27:31 +02:00
Thread{
for (i in timerDuration downTo 1){
2021-06-11 20:23:54 +02:00
runOnUiThread { binding.textTimer.text = i.toString() }
2020-08-09 15:27:31 +02:00
Thread.sleep(1000)
}
runOnUiThread {
2021-10-03 14:36:06 +02:00
takePhoto(path)
2021-06-11 20:23:54 +02:00
binding.textTimer.visibility = View.GONE
2020-08-09 15:27:31 +02:00
}
}.start()
} else {
2021-10-03 14:36:06 +02:00
takePhoto(path)
}
}
@SuppressLint("MissingPermission")
private fun onClickRecordVideo() {
isRecording = if (isRecording) {
videoCapture?.stopRecording()
false
} else {
val path = getOutputPath(true)
val handleId = gocryptfsVolume.openWriteMode(path)
videoCapture?.startRecording(VideoCapture.OutputFileOptions(object : SeekableWriter {
var offset = 0L
override fun write(byteArray: ByteArray) {
offset += gocryptfsVolume.writeFile(handleId, offset, byteArray, byteArray.size)
}
override fun seek(offset: Long) {
this.offset = offset
}
override fun close() {
gocryptfsVolume.closeFile(handleId)
}
}), executor, object : VideoCapture.OnVideoSavedCallback {
override fun onVideoSaved() {
Toast.makeText(applicationContext, getString(R.string.video_save_success, path), Toast.LENGTH_SHORT).show()
binding.recordVideoButton.setImageResource(R.drawable.record_video_button)
}
override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) {
Toast.makeText(applicationContext, message, Toast.LENGTH_SHORT).show()
cause?.printStackTrace()
binding.recordVideoButton.setImageResource(R.drawable.record_video_button)
}
})
binding.recordVideoButton.setImageResource(R.drawable.stop_recording_video_button)
true
2020-08-09 15:27:31 +02:00
}
2020-08-05 14:06:54 +02:00
}
override fun onDestroy() {
super.onDestroy()
if (!isFinishingIntentionally) {
gocryptfsVolume.close()
RestrictedFileProvider.wipeAll(this)
}
}
2020-08-12 16:14:26 +02:00
override fun onStop() {
super.onStop()
if (!isFinishing && !usf_keep_open){
2020-08-12 16:14:26 +02:00
finish()
}
}
override fun onPause() {
super.onPause()
2020-09-04 04:00:19 +02:00
sensorOrientationListener.remove(this)
2021-10-03 14:36:06 +02:00
if (!isAskingPermissions && !usf_keep_open) {
2020-08-12 16:14:26 +02:00
finish()
}
}
2020-09-04 04:00:19 +02:00
override fun onResume() {
super.onResume()
sensorOrientationListener.addListener(this)
}
override fun onBackPressed() {
super.onBackPressed()
isFinishingIntentionally = true
}
2020-09-04 04:00:19 +02:00
override fun onOrientationChange(newOrientation: Int) {
val reversedOrientation = when (newOrientation){
90 -> 270
270 -> 90
else -> newOrientation
}.toFloat()
val rotateAnimation = RotateAnimation(previousOrientation, when {
reversedOrientation - previousOrientation > 180 -> reversedOrientation - 360
reversedOrientation - previousOrientation < -180 -> reversedOrientation + 360
else -> reversedOrientation
}, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)
rotateAnimation.duration = 300
rotateAnimation.interpolator = LinearInterpolator()
rotateAnimation.fillAfter = true
orientedIcons.map { it.startAnimation(rotateAnimation) }
previousOrientation = reversedOrientation
}
}
private fun Size.swap(): Size {
return Size(height, width)
2020-08-05 14:06:54 +02:00
}