From 9c021c655b02eb14983c53b2ec086f4022aa71c2 Mon Sep 17 00:00:00 2001 From: darthpaul Date: Wed, 13 Jul 2022 16:39:18 +0100 Subject: [PATCH] do some optimisations for pre SDK 29 devices --- .../camera/activities/MainActivity.kt | 44 ++++++++++----- .../camera/activities/SettingsActivity.kt | 14 +++-- .../camera/dialogs/ChangeResolutionDialogX.kt | 2 +- .../camera/helpers/MediaOutputHelper.kt | 55 ++++++++++++++++--- .../camera/helpers/VideoQualityManager.kt | 2 +- .../camera/implementations/CameraXPreview.kt | 6 +- .../camera/models/MediaOutput.kt | 6 ++ 7 files changed, 99 insertions(+), 30 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt index d5679f65..cd06b2da 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt @@ -7,6 +7,7 @@ import android.hardware.SensorManager import android.net.Uri import android.os.Bundle import android.os.Handler +import android.os.Looper import android.provider.MediaStore import android.util.Log import android.view.* @@ -31,9 +32,11 @@ import java.util.concurrent.TimeUnit import kotlinx.android.synthetic.main.activity_main.* class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, CameraXPreviewListener { - private val TAG = "MainActivity" - private val FADE_DELAY = 5000L - private val CAPTURE_ANIMATION_DURATION = 100L + companion object { + private const val TAG = "MainActivity" + private const val FADE_DELAY = 5000L + private const val CAPTURE_ANIMATION_DURATION = 100L + } lateinit var mTimerHandler: Handler private lateinit var mOrientationEventListener: OrientationEventListener @@ -49,14 +52,8 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera private var mCurrVideoRecTimer = 0 var mLastHandledOrientation = 0 + @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { - window.addFlags( - WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or - WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or - WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or - WindowManager.LayoutParams.FLAG_FULLSCREEN - ) - useDynamicTheme = false super.onCreate(savedInstanceState) appLaunched(BuildConfig.APPLICATION_ID) @@ -67,6 +64,22 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera supportActionBar?.hide() checkWhatsNewDialog() setupOrientationEventListener() + if (isRPlus()) { + setShowWhenLocked(true) + setTurnScreenOn(true) + window.insetsController?.hide(WindowInsets.Type.statusBars()) + } else if (isOreoMr1Plus()) { + setShowWhenLocked(true) + setTurnScreenOn(true) + window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN) + } else { + window.addFlags( + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or + WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or + WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or + WindowManager.LayoutParams.FLAG_FULLSCREEN + ) + } } override fun onResume() { @@ -238,8 +251,8 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera mFocusCircleView = FocusCircleView(applicationContext) view_holder.addView(mFocusCircleView) - mTimerHandler = Handler() - mFadeHandler = Handler() + mTimerHandler = Handler(Looper.getMainLooper()) + mFadeHandler = Handler(Looper.getMainLooper()) setupPreviewImage(true) val initialFlashlightState = FLASH_OFF @@ -439,8 +452,13 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera view.isClickable = value != .0f } + @Suppress("DEPRECATION") private fun hideNavigationBarIcons() { - window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE + if (isRPlus()) { + window.insetsController?.hide(WindowInsets.Type.systemBars()) + } else { + window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE + } } private fun showTimer() { diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/activities/SettingsActivity.kt b/app/src/main/kotlin/com/simplemobiletools/camera/activities/SettingsActivity.kt index a7114ca3..5b7596e9 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/activities/SettingsActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/activities/SettingsActivity.kt @@ -1,8 +1,10 @@ package com.simplemobiletools.camera.activities +import android.annotation.SuppressLint import android.os.Bundle import android.view.Menu import android.view.MenuItem +import androidx.core.content.res.ResourcesCompat import com.simplemobiletools.camera.BuildConfig import com.simplemobiletools.camera.R import com.simplemobiletools.camera.extensions.config @@ -14,6 +16,7 @@ import com.simplemobiletools.commons.helpers.NavigationIcon import com.simplemobiletools.commons.models.FAQItem import com.simplemobiletools.commons.models.RadioItem import java.util.Locale +import kotlin.system.exitProcess import kotlinx.android.synthetic.main.activity_settings.* class SettingsActivity : SimpleActivity() { @@ -77,7 +80,7 @@ class SettingsActivity : SimpleActivity() { // make sure the corners at ripple fit the stroke rounded corners if (settings_purchase_thank_you_holder.isGone()) { - settings_use_english_holder.background = resources.getDrawable(R.drawable.ripple_top_corners, theme) + settings_use_english_holder.background = ResourcesCompat.getDrawable(resources, R.drawable.ripple_top_corners, theme) } settings_purchase_thank_you_holder.setOnClickListener { @@ -97,13 +100,13 @@ class SettingsActivity : SimpleActivity() { settings_use_english.isChecked = config.useEnglish if (settings_use_english_holder.isGone() && settings_purchase_thank_you_holder.isGone()) { - settings_keep_settings_visible_holder.background = resources.getDrawable(R.drawable.ripple_all_corners, theme) + settings_keep_settings_visible_holder.background = ResourcesCompat.getDrawable(resources, R.drawable.ripple_all_corners, theme) } settings_use_english_holder.setOnClickListener { settings_use_english.toggle() config.useEnglish = settings_use_english.isChecked - System.exit(0) + exitProcess(0) } } @@ -172,8 +175,8 @@ class SettingsActivity : SimpleActivity() { settings_save_photos_holder.setOnClickListener { FilePickerDialog(this, config.savePhotosFolder, false, showFAB = true) { val path = it - handleSAFDialog(it) { - if (it) { + handleSAFDialog(it) { success -> + if (success) { config.savePhotosFolder = path settings_save_photos.text = getLastPart(config.savePhotosFolder) } @@ -206,6 +209,7 @@ class SettingsActivity : SimpleActivity() { } } + @SuppressLint("SetTextI18n") private fun updatePhotoQuality(quality: Int) { settings_photo_quality.text = "$quality%" } diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/dialogs/ChangeResolutionDialogX.kt b/app/src/main/kotlin/com/simplemobiletools/camera/dialogs/ChangeResolutionDialogX.kt index 2e3c8a19..05e25133 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/dialogs/ChangeResolutionDialogX.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/dialogs/ChangeResolutionDialogX.kt @@ -90,7 +90,7 @@ class ChangeResolutionDialogX( if (isFrontCamera) { config.frontVideoResIndex = selectionIndex } else { - config.backPhotoResIndex = selectionIndex + config.backVideoResIndex = selectionIndex } dialog?.dismiss() callback.invoke() diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/MediaOutputHelper.kt b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/MediaOutputHelper.kt index 62b29dfd..2e790450 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/MediaOutputHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/MediaOutputHelper.kt @@ -12,6 +12,8 @@ import com.simplemobiletools.camera.extensions.getRandomMediaName import com.simplemobiletools.camera.models.MediaOutput import com.simplemobiletools.commons.activities.BaseSimpleActivity import com.simplemobiletools.commons.extensions.* +import com.simplemobiletools.commons.helpers.isOreoPlus +import com.simplemobiletools.commons.helpers.isQPlus import java.io.File import java.io.OutputStream @@ -25,6 +27,7 @@ class MediaOutputHelper( companion object { private const val TAG = "MediaOutputHelper" private const val MODE = "rw" + private const val EXTERNAL_VOLUME = "external" private const val IMAGE_MIME_TYPE = "image/jpeg" private const val VIDEO_MIME_TYPE = "video/mp4" } @@ -53,37 +56,56 @@ class MediaOutputHelper( fun getVideoMediaOutput(): MediaOutput { return if (is3rdPartyIntent) { if (outputUri != null) { - val fileDescriptor = openFileDescriptor(outputUri) - if (fileDescriptor != null) { - MediaOutput.FileDescriptorMediaOutput(fileDescriptor, outputUri) + if (isOreoPlus()) { + val fileDescriptor = openFileDescriptor(outputUri) + if (fileDescriptor != null) { + MediaOutput.FileDescriptorMediaOutput(fileDescriptor, outputUri) + } else { + errorHandler.showSaveToInternalStorage() + getMediaStoreOutput(isPhoto = false) + } } else { - errorHandler.showSaveToInternalStorage() - getMediaStoreOutput(isPhoto = false) + val path = activity.getRealPathFromURI(outputUri) + if (path != null) { + MediaOutput.FileMediaOutput(File(path), outputUri) + } else { + errorHandler.showSaveToInternalStorage() + getMediaStoreOutput(isPhoto = false) + } } } else { getMediaStoreOutput(isPhoto = false) } } else { - getFileDescriptorMediaOutput() ?: getMediaStoreOutput(isPhoto = false) + if (isOreoPlus()) { + getFileDescriptorMediaOutput() ?: getMediaStoreOutput(isPhoto = false) + } else { + getFileMediaOutput() ?: getMediaStoreOutput(isPhoto = false) + } } } private fun getMediaStoreOutput(isPhoto: Boolean): MediaOutput.MediaStoreOutput { val contentValues = getContentValues(isPhoto) val contentUri = if (isPhoto) { - MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) + MediaStore.Images.Media.getContentUri(EXTERNAL_VOLUME) } else { - MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) + MediaStore.Video.Media.getContentUri(EXTERNAL_VOLUME) } return MediaOutput.MediaStoreOutput(contentValues, contentUri) } + @Suppress("DEPRECATION") private fun getContentValues(isPhoto: Boolean): ContentValues { val mimeType = if (isPhoto) IMAGE_MIME_TYPE else VIDEO_MIME_TYPE return ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, getRandomMediaName(isPhoto)) put(MediaStore.MediaColumns.MIME_TYPE, mimeType) - put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM) + if (isQPlus()) { + put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM) + } else { + put(MediaStore.MediaColumns.DATA, Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString()) + } } } @@ -131,6 +153,21 @@ class MediaOutputHelper( return mediaOutput } + private fun getFileMediaOutput(): MediaOutput.FileMediaOutput? { + var mediaOutput: MediaOutput.FileMediaOutput? = null + val canWrite = canWriteToFilePath(mediaStorageDir) + Log.i(TAG, "getMediaOutput: canWrite=${canWrite}") + if (canWrite) { + val path = activity.getOutputMediaFile(false) + val uri = getUriForFilePath(path) + if (uri != null) { + mediaOutput = MediaOutput.FileMediaOutput(File(path), uri) + } + } + Log.i(TAG, "FileDescriptorMediaOutput: $mediaOutput") + return mediaOutput + } + private fun openFileDescriptor(uri: Uri): ParcelFileDescriptor? { return try { Log.i(TAG, "uri: $uri") diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/VideoQualityManager.kt b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/VideoQualityManager.kt index a2a6948e..d667f1d1 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/VideoQualityManager.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/VideoQualityManager.kt @@ -51,7 +51,7 @@ class VideoQualityManager( fun getUserSelectedQuality(cameraSelector: CameraSelector): Quality { var selectionIndex = if (cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA) config.frontVideoResIndex else config.backVideoResIndex selectionIndex = selectionIndex.coerceAtLeast(0) - return getSupportedQualities(cameraSelector)[selectionIndex].toCameraXQuality() + return getSupportedQualities(cameraSelector).getOrElse(selectionIndex) { VideoQuality.HD }.toCameraXQuality() } fun getSupportedQualities(cameraSelector: CameraSelector): List { diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreview.kt b/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreview.kt index 8a12169b..6ebde5ae 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreview.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreview.kt @@ -431,7 +431,7 @@ class CameraXPreview( } } - @SuppressLint("MissingPermission") + @SuppressLint("MissingPermission", "NewApi") private fun startRecording() { val videoCapture = videoCapture ?: throw IllegalStateException("Camera initialization failed.") @@ -441,6 +441,10 @@ class CameraXPreview( FileDescriptorOutputOptions.Builder(mediaOutput.fileDescriptor).build() .let { videoCapture.output.prepareRecording(activity, it) } } + is MediaOutput.FileMediaOutput -> { + FileOutputOptions.Builder(mediaOutput.file).build() + .let { videoCapture.output.prepareRecording(activity, it) } + } is MediaOutput.MediaStoreOutput -> { MediaStoreOutputOptions.Builder(contentResolver, mediaOutput.contentUri).setContentValues(mediaOutput.contentValues).build() .let { videoCapture.output.prepareRecording(activity, it) } diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/models/MediaOutput.kt b/app/src/main/kotlin/com/simplemobiletools/camera/models/MediaOutput.kt index c4e1e8d0..2bb2edd5 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/models/MediaOutput.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/models/MediaOutput.kt @@ -3,6 +3,7 @@ package com.simplemobiletools.camera.models import android.content.ContentValues import android.net.Uri import android.os.ParcelFileDescriptor +import java.io.File import java.io.OutputStream sealed class MediaOutput( @@ -23,5 +24,10 @@ sealed class MediaOutput( override val uri: Uri, ) : MediaOutput(uri) + data class FileMediaOutput( + val file: File, + override val uri: Uri, + ) : MediaOutput(uri) + object BitmapOutput : MediaOutput(null) }