use tabs for camera modes

This commit is contained in:
darthpaul 2022-08-19 15:24:47 +01:00
parent 4b7166e90f
commit 2ebeb5a1e4
9 changed files with 157 additions and 35 deletions

View File

@ -15,6 +15,7 @@ import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.google.android.material.tabs.TabLayout
import com.simplemobiletools.camera.BuildConfig import com.simplemobiletools.camera.BuildConfig
import com.simplemobiletools.camera.R import com.simplemobiletools.camera.R
import com.simplemobiletools.camera.extensions.config import com.simplemobiletools.camera.extensions.config
@ -33,6 +34,8 @@ import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, CameraXPreviewListener { class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, CameraXPreviewListener {
companion object { companion object {
private const val CAPTURE_ANIMATION_DURATION = 100L 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 lateinit var mTimerHandler: Handler
@ -49,6 +52,12 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera
private var mCurrVideoRecTimer = 0 private var mCurrVideoRecTimer = 0
var mLastHandledOrientation = 0 var mLastHandledOrientation = 0
private val tabSelectedListener = object : TabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
handleTogglePhotoVideo()
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
useDynamicTheme = false useDynamicTheme = false
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -81,6 +90,32 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 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() { override fun onResume() {
@ -158,9 +193,9 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera
} }
private fun hideIntentButtons() { private fun hideIntentButtons() {
toggle_photo_video.beGone() camera_mode_tab.beGone()
settings.beGone() settings.beGone()
last_photo_video_preview.beGone() last_photo_video_preview.beInvisible()
} }
private fun tryInitCamera() { 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 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 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) setContentView(R.layout.activity_main)
initButtons() initButtons()
(toggle_photo_video.layoutParams as ConstraintLayout.LayoutParams).setMargins( (toggle_flash.layoutParams as ConstraintLayout.LayoutParams).setMargins(
0, 0,
statusBarHeight, statusBarHeight,
0, 0,
@ -237,8 +274,14 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera
) )
checkVideoCaptureIntent() checkVideoCaptureIntent()
if (mIsInPhotoMode) {
selectPhotoTab()
} else {
selectVideoTab()
}
val outputUri = intent.extras?.get(MediaStore.EXTRA_OUTPUT) as? Uri val outputUri = intent.extras?.get(MediaStore.EXTRA_OUTPUT) as? Uri
val is3rdPartyIntent = isVideoCaptureIntent() || isImageCaptureIntent() val is3rdPartyIntent = is3rdPartyIntent()
mPreview = CameraXInitializer(this).createCameraXPreview( mPreview = CameraXInitializer(this).createCameraXPreview(
preview_view, preview_view,
listener = this, listener = this,
@ -271,7 +314,6 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera
toggle_flash.setOnClickListener { toggleFlash() } toggle_flash.setOnClickListener { toggleFlash() }
shutter.setOnClickListener { shutterPressed() } shutter.setOnClickListener { shutterPressed() }
settings.setOnClickListener { launchSettings() } settings.setOnClickListener { launchSettings() }
toggle_photo_video.setOnClickListener { handleTogglePhotoVideo() }
change_resolution.setOnClickListener { mPreview?.showChangeResolutionDialog() } change_resolution.setOnClickListener { mPreview?.showChangeResolutionDialog() }
} }
@ -332,6 +374,7 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera
togglePhotoVideo() togglePhotoVideo()
} else { } else {
toast(R.string.no_audio_permissions) toast(R.string.no_audio_permissions)
selectPhotoTab()
if (isVideoCaptureIntent()) { if (isVideoCaptureIntent()) {
finish() finish()
} }
@ -369,10 +412,10 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera
} }
private fun initPhotoMode() { private fun initPhotoMode() {
toggle_photo_video.setImageResource(R.drawable.ic_video_vector)
shutter.setImageResource(R.drawable.ic_shutter_vector) shutter.setImageResource(R.drawable.ic_shutter_vector)
mPreview?.initPhotoMode() mPreview?.initPhotoMode()
setupPreviewImage(true) setupPreviewImage(true)
selectPhotoTab()
} }
private fun tryInitVideoMode() { private fun tryInitVideoMode() {
@ -387,10 +430,10 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera
} }
private fun initVideoButtons() { private fun initVideoButtons() {
toggle_photo_video.setImageResource(R.drawable.ic_camera_vector)
shutter.setImageResource(R.drawable.ic_video_rec) shutter.setImageResource(R.drawable.ic_video_rec)
setupPreviewImage(false) setupPreviewImage(false)
mPreview?.checkFlashlight() mPreview?.checkFlashlight()
selectVideoTab()
} }
private fun setupPreviewImage(isPhoto: Boolean) { private fun setupPreviewImage(isPhoto: Boolean) {
@ -487,7 +530,7 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera
} }
private fun animateViews(degrees: Int) { private fun animateViews(degrees: Int) {
val views = arrayOf<View>(toggle_camera, toggle_flash, toggle_photo_video, change_resolution, shutter, settings, last_photo_video_preview) val views = arrayOf<View>(toggle_camera, toggle_flash, change_resolution, shutter, settings, last_photo_video_preview)
for (view in views) { for (view in views) {
rotate(view, degrees) rotate(view, degrees)
} }
@ -514,7 +557,7 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera
if (available) { if (available) {
toggle_flash.beVisible() toggle_flash.beVisible()
} else { } else {
toggle_flash.beGone() toggle_flash.beInvisible()
toggle_flash.setImageResource(R.drawable.ic_flash_off_vector) toggle_flash.setImageResource(R.drawable.ic_flash_off_vector)
mPreview?.setFlashlightState(FLASH_OFF) mPreview?.setFlashlightState(FLASH_OFF)
} }
@ -587,6 +630,18 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera
mFocusCircleView.drawFocusCircle(xPos, yPos) mFocusCircleView.drawFocusCircle(xPos, yPos)
} }
override fun onSwipeLeft() {
if (!is3rdPartyIntent()) {
selectPhotoTab(triggerListener = true)
}
}
override fun onSwipeRight() {
if (!is3rdPartyIntent()) {
selectVideoTab(triggerListener = true)
}
}
fun setRecordingState(isRecording: Boolean) { fun setRecordingState(isRecording: Boolean) {
runOnUiThread { runOnUiThread {
if (isRecording) { if (isRecording) {

View File

@ -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?) {}
}

View File

@ -31,6 +31,7 @@ import com.simplemobiletools.camera.models.MediaOutput
import com.simplemobiletools.camera.models.MySize import com.simplemobiletools.camera.models.MySize
import com.simplemobiletools.commons.extensions.toast import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.commons.helpers.ensureBackgroundThread import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import kotlin.math.abs
class CameraXPreview( class CameraXPreview(
private val activity: AppCompatActivity, private val activity: AppCompatActivity,
@ -45,6 +46,7 @@ class CameraXPreview(
// Auto focus is 1/6 of the area. // Auto focus is 1/6 of the area.
private const val AF_SIZE = 1.0f / 6.0f private const val AF_SIZE = 1.0f / 6.0f
private const val AE_SIZE = AF_SIZE * 1.5f private const val AE_SIZE = AF_SIZE * 1.5f
private const val MIN_SWIPE_DISTANCE_X = 100
} }
private val config = activity.config private val config = activity.config
@ -278,11 +280,26 @@ class CameraXPreview(
true true
} ?: false } ?: 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 -> previewView.setOnTouchListener { _, event ->
gestureDetector.onTouchEvent(event) val handledGesture = gestureDetector.onTouchEvent(event)
scaleGesture?.onTouchEvent(event) val handledScaleGesture = scaleGesture?.onTouchEvent(event)
true handledGesture || handledScaleGesture ?: false
} }
} }

View File

@ -16,4 +16,6 @@ interface CameraXPreviewListener {
fun onVideoRecordingStopped() fun onVideoRecordingStopped()
fun onVideoDurationChanged(durationNanos: Long) fun onVideoDurationChanged(durationNanos: Long)
fun onFocusCamera(xPos: Float, yPos: Float) fun onFocusCamera(xPos: Float, yPos: Float)
fun onSwipeLeft()
fun onSwipeRight()
} }

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/tab_indicator_selected" android:state_selected="true" />
<item android:drawable="@drawable/tab_indicator_unselected" />
</selector>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:bottom="@dimen/tab_indicator_margin"
android:end="@dimen/tab_indicator_margin"
android:start="@dimen/tab_indicator_margin"
android:top="@dimen/tab_indicator_margin">
<shape>
<corners android:radius="@dimen/tab_indicator_margin" />
<solid android:color="@color/md_grey_white" />
</shape>
</item>
</layer-list>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/transparent" />
</shape>

View File

@ -30,21 +30,6 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/toggle_photo_video"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_marginStart="@dimen/normal_margin"
android:layout_marginEnd="@dimen/normal_margin"
android:contentDescription="@string/toggle_photo_video"
android:padding="@dimen/normal_margin"
android:src="@drawable/ic_video_vector"
app:layout_constraintEnd_toStartOf="@id/toggle_flash"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView <ImageView
android:id="@+id/toggle_flash" android:id="@+id/toggle_flash"
android:layout_width="@dimen/icon_size" android:layout_width="@dimen/icon_size"
@ -56,15 +41,15 @@
android:padding="@dimen/normal_margin" android:padding="@dimen/normal_margin"
android:src="@drawable/ic_flash_off_vector" android:src="@drawable/ic_flash_off_vector"
app:layout_constraintEnd_toStartOf="@id/change_resolution" app:layout_constraintEnd_toStartOf="@id/change_resolution"
app:layout_constraintStart_toEndOf="@id/toggle_photo_video" app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintTop_toTopOf="@id/toggle_photo_video" /> app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView <ImageView
android:id="@+id/change_resolution" android:id="@+id/change_resolution"
android:layout_width="@dimen/icon_size" android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size" android:layout_height="@dimen/icon_size"
android:layout_below="@+id/toggle_photo_video"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_marginStart="@dimen/normal_margin" android:layout_marginStart="@dimen/normal_margin"
android:layout_marginEnd="@dimen/normal_margin" android:layout_marginEnd="@dimen/normal_margin"
@ -73,7 +58,7 @@
android:src="@drawable/ic_resolution_vector" android:src="@drawable/ic_resolution_vector"
app:layout_constraintEnd_toStartOf="@id/settings" app:layout_constraintEnd_toStartOf="@id/settings"
app:layout_constraintStart_toEndOf="@id/toggle_flash" app:layout_constraintStart_toEndOf="@id/toggle_flash"
app:layout_constraintTop_toTopOf="@id/toggle_photo_video" /> app:layout_constraintTop_toTopOf="@id/toggle_flash" />
<ImageView <ImageView
android:id="@+id/settings" android:id="@+id/settings"
@ -86,7 +71,7 @@
android:src="@drawable/ic_settings_cog_vector" android:src="@drawable/ic_settings_cog_vector"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/change_resolution" app:layout_constraintStart_toEndOf="@id/change_resolution"
app:layout_constraintTop_toTopOf="@id/toggle_photo_video" /> app:layout_constraintTop_toTopOf="@id/toggle_flash" />
<androidx.constraintlayout.widget.Barrier <androidx.constraintlayout.widget.Barrier
@ -94,8 +79,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:barrierDirection="bottom" app:barrierDirection="bottom"
app:constraint_referenced_ids="settings,change_resolution,toggle_flash,toggle_photo_video" /> app:constraint_referenced_ids="settings,change_resolution,toggle_flash" />
<View <View
android:id="@+id/bottom_overlay" android:id="@+id/bottom_overlay"
@ -105,7 +89,38 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/shutter" /> app:layout_constraintTop_toTopOf="@id/camera_mode_tab" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/camera_mode_tab"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/big_margin"
android:background="@android:color/transparent"
app:layout_constraintBottom_toTopOf="@id/shutter"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:tabBackground="@drawable/tab_indicator"
app:tabIndicator="@null"
app:tabMode="auto"
app:tabRippleColor="@color/md_grey_600_dark"
app:tabSelectedTextColor="@color/md_grey_600_dark"
app:tabTextColor="@color/md_grey_white">
<com.google.android.material.tabs.TabItem
android:id="@+id/camera_mode_tab_video"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/video" />
<com.google.android.material.tabs.TabItem
android:id="@+id/camera_mode_tab_photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/photo" />
</com.google.android.material.tabs.TabLayout>
<ImageView <ImageView
android:id="@+id/toggle_camera" android:id="@+id/toggle_camera"

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<dimen name="icon_size">56dp</dimen> <dimen name="icon_size">56dp</dimen>
<dimen name="tab_indicator_margin">10dp</dimen>
</resources> </resources>