From 2ebeb5a1e4d6e9488a8c34e372bbd4f8af621af2 Mon Sep 17 00:00:00 2001 From: darthpaul Date: Fri, 19 Aug 2022 15:24:47 +0100 Subject: [PATCH] use tabs for camera modes --- .../camera/activities/MainActivity.kt | 73 ++++++++++++++++--- .../camera/helpers/TabSelectedListener.kt | 9 +++ .../camera/implementations/CameraXPreview.kt | 23 +++++- .../implementations/CameraXPreviewListener.kt | 2 + app/src/main/res/drawable/tab_indicator.xml | 5 ++ .../res/drawable/tab_indicator_selected.xml | 14 ++++ .../res/drawable/tab_indicator_unselected.xml | 4 + app/src/main/res/layout/activity_main.xml | 61 ++++++++++------ app/src/main/res/values/dimens.xml | 1 + 9 files changed, 157 insertions(+), 35 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/camera/helpers/TabSelectedListener.kt create mode 100644 app/src/main/res/drawable/tab_indicator.xml create mode 100644 app/src/main/res/drawable/tab_indicator_selected.xml create mode 100644 app/src/main/res/drawable/tab_indicator_unselected.xml 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 51208744..ac24fcce 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt @@ -15,6 +15,7 @@ import com.bumptech.glide.Glide import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions import com.bumptech.glide.request.RequestOptions +import com.google.android.material.tabs.TabLayout import com.simplemobiletools.camera.BuildConfig import com.simplemobiletools.camera.R import com.simplemobiletools.camera.extensions.config @@ -33,6 +34,8 @@ import kotlinx.android.synthetic.main.activity_main.* class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, CameraXPreviewListener { companion object { private const val CAPTURE_ANIMATION_DURATION = 100L + private const val PHOTO_MODE_INDEX = 1 + private const val VIDEO_MODE_INDEX = 0 } lateinit var mTimerHandler: Handler @@ -49,6 +52,12 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera private var mCurrVideoRecTimer = 0 var mLastHandledOrientation = 0 + private val tabSelectedListener = object : TabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab) { + handleTogglePhotoVideo() + } + } + override fun onCreate(savedInstanceState: Bundle?) { useDynamicTheme = false super.onCreate(savedInstanceState) @@ -81,6 +90,32 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS ) } + + selectPhotoTab() + } + + private fun selectPhotoTab(triggerListener: Boolean = false) { + if (!triggerListener) { + removeTabListener() + } + camera_mode_tab.getTabAt(PHOTO_MODE_INDEX)?.select() + setTabListener() + } + + private fun selectVideoTab(triggerListener: Boolean = false) { + if (!triggerListener) { + removeTabListener() + } + camera_mode_tab.getTabAt(VIDEO_MODE_INDEX)?.select() + setTabListener() + } + + private fun setTabListener() { + camera_mode_tab.addOnTabSelectedListener(tabSelectedListener) + } + + private fun removeTabListener() { + camera_mode_tab.removeOnTabSelectedListener(tabSelectedListener) } override fun onResume() { @@ -158,9 +193,9 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera } private fun hideIntentButtons() { - toggle_photo_video.beGone() + camera_mode_tab.beGone() settings.beGone() - last_photo_video_preview.beGone() + last_photo_video_preview.beInvisible() } private fun tryInitCamera() { @@ -193,6 +228,8 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera } } + private fun is3rdPartyIntent() = isVideoCaptureIntent() || isImageCaptureIntent() + private fun isImageCaptureIntent(): Boolean = intent?.action == MediaStore.ACTION_IMAGE_CAPTURE || intent?.action == MediaStore.ACTION_IMAGE_CAPTURE_SECURE private fun isVideoCaptureIntent(): Boolean = intent?.action == MediaStore.ACTION_VIDEO_CAPTURE @@ -219,7 +256,7 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera setContentView(R.layout.activity_main) initButtons() - (toggle_photo_video.layoutParams as ConstraintLayout.LayoutParams).setMargins( + (toggle_flash.layoutParams as ConstraintLayout.LayoutParams).setMargins( 0, statusBarHeight, 0, @@ -237,8 +274,14 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera ) checkVideoCaptureIntent() + if (mIsInPhotoMode) { + selectPhotoTab() + } else { + selectVideoTab() + } + val outputUri = intent.extras?.get(MediaStore.EXTRA_OUTPUT) as? Uri - val is3rdPartyIntent = isVideoCaptureIntent() || isImageCaptureIntent() + val is3rdPartyIntent = is3rdPartyIntent() mPreview = CameraXInitializer(this).createCameraXPreview( preview_view, listener = this, @@ -271,7 +314,6 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera toggle_flash.setOnClickListener { toggleFlash() } shutter.setOnClickListener { shutterPressed() } settings.setOnClickListener { launchSettings() } - toggle_photo_video.setOnClickListener { handleTogglePhotoVideo() } change_resolution.setOnClickListener { mPreview?.showChangeResolutionDialog() } } @@ -332,6 +374,7 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera togglePhotoVideo() } else { toast(R.string.no_audio_permissions) + selectPhotoTab() if (isVideoCaptureIntent()) { finish() } @@ -369,10 +412,10 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera } private fun initPhotoMode() { - toggle_photo_video.setImageResource(R.drawable.ic_video_vector) shutter.setImageResource(R.drawable.ic_shutter_vector) mPreview?.initPhotoMode() setupPreviewImage(true) + selectPhotoTab() } private fun tryInitVideoMode() { @@ -387,10 +430,10 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera } private fun initVideoButtons() { - toggle_photo_video.setImageResource(R.drawable.ic_camera_vector) shutter.setImageResource(R.drawable.ic_video_rec) setupPreviewImage(false) mPreview?.checkFlashlight() + selectVideoTab() } private fun setupPreviewImage(isPhoto: Boolean) { @@ -487,7 +530,7 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera } private fun animateViews(degrees: Int) { - val views = arrayOf(toggle_camera, toggle_flash, toggle_photo_video, change_resolution, shutter, settings, last_photo_video_preview) + val views = arrayOf(toggle_camera, toggle_flash, change_resolution, shutter, settings, last_photo_video_preview) for (view in views) { rotate(view, degrees) } @@ -514,7 +557,7 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera if (available) { toggle_flash.beVisible() } else { - toggle_flash.beGone() + toggle_flash.beInvisible() toggle_flash.setImageResource(R.drawable.ic_flash_off_vector) mPreview?.setFlashlightState(FLASH_OFF) } @@ -587,6 +630,18 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera mFocusCircleView.drawFocusCircle(xPos, yPos) } + override fun onSwipeLeft() { + if (!is3rdPartyIntent()) { + selectPhotoTab(triggerListener = true) + } + } + + override fun onSwipeRight() { + if (!is3rdPartyIntent()) { + selectVideoTab(triggerListener = true) + } + } + fun setRecordingState(isRecording: Boolean) { runOnUiThread { if (isRecording) { diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/helpers/TabSelectedListener.kt b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/TabSelectedListener.kt new file mode 100644 index 00000000..361729ec --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/camera/helpers/TabSelectedListener.kt @@ -0,0 +1,9 @@ +package com.simplemobiletools.camera.helpers + +import com.google.android.material.tabs.TabLayout + +interface TabSelectedListener : TabLayout.OnTabSelectedListener { + override fun onTabReselected(tab: TabLayout.Tab?){} + + override fun onTabUnselected(tab: TabLayout.Tab?) {} +} 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 4b7ecbbc..1260e0d0 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreview.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreview.kt @@ -31,6 +31,7 @@ import com.simplemobiletools.camera.models.MediaOutput import com.simplemobiletools.camera.models.MySize import com.simplemobiletools.commons.extensions.toast import com.simplemobiletools.commons.helpers.ensureBackgroundThread +import kotlin.math.abs class CameraXPreview( private val activity: AppCompatActivity, @@ -45,6 +46,7 @@ class CameraXPreview( // Auto focus is 1/6 of the area. private const val AF_SIZE = 1.0f / 6.0f private const val AE_SIZE = AF_SIZE * 1.5f + private const val MIN_SWIPE_DISTANCE_X = 100 } private val config = activity.config @@ -278,11 +280,26 @@ class CameraXPreview( true } ?: false } + + override fun onFling(event1: MotionEvent, event2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { + val deltaX = event1.x - event2.x + val deltaXAbs = abs(deltaX) + + if (deltaXAbs >= MIN_SWIPE_DISTANCE_X) { + if (deltaX > 0) { + listener.onSwipeLeft() + } else { + listener.onSwipeRight() + } + } + + return true + } }) previewView.setOnTouchListener { _, event -> - gestureDetector.onTouchEvent(event) - scaleGesture?.onTouchEvent(event) - true + val handledGesture = gestureDetector.onTouchEvent(event) + val handledScaleGesture = scaleGesture?.onTouchEvent(event) + handledGesture || handledScaleGesture ?: false } } diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreviewListener.kt b/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreviewListener.kt index 73bf5db5..85657e43 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreviewListener.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/implementations/CameraXPreviewListener.kt @@ -16,4 +16,6 @@ interface CameraXPreviewListener { fun onVideoRecordingStopped() fun onVideoDurationChanged(durationNanos: Long) fun onFocusCamera(xPos: Float, yPos: Float) + fun onSwipeLeft() + fun onSwipeRight() } diff --git a/app/src/main/res/drawable/tab_indicator.xml b/app/src/main/res/drawable/tab_indicator.xml new file mode 100644 index 00000000..925ef230 --- /dev/null +++ b/app/src/main/res/drawable/tab_indicator.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/tab_indicator_selected.xml b/app/src/main/res/drawable/tab_indicator_selected.xml new file mode 100644 index 00000000..e9b77dcf --- /dev/null +++ b/app/src/main/res/drawable/tab_indicator_selected.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/tab_indicator_unselected.xml b/app/src/main/res/drawable/tab_indicator_unselected.xml new file mode 100644 index 00000000..76589c0c --- /dev/null +++ b/app/src/main/res/drawable/tab_indicator_unselected.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 27cc7530..3ab53b46 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -30,21 +30,6 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - - - + app:layout_constraintHorizontal_chainStyle="spread_inside" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="@id/toggle_flash" /> + app:layout_constraintTop_toTopOf="@id/toggle_flash" /> - + app:constraint_referenced_ids="settings,change_resolution,toggle_flash" /> + app:layout_constraintTop_toTopOf="@id/camera_mode_tab" /> + + + + + + + + + 56dp + 10dp