fix preview and capture resolutions

- adjust the preview so the image captured is what the user sees
- add method MySize.isSupported; current support is for full, 16:9, 4:3 and 1:1 of the highest resolution
- cleanup ImageQualityManager, add method to get the full screen resolution which is just the resolution with the largest width
- change the UI to move settings to the top and remove the auto fading
This commit is contained in:
darthpaul 2022-08-17 01:12:18 +01:00
parent f6235e3b96
commit d9193f7494
9 changed files with 249 additions and 177 deletions

View File

@ -10,7 +10,7 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import android.provider.MediaStore import android.provider.MediaStore
import android.view.* import android.view.*
import android.widget.RelativeLayout import androidx.constraintlayout.widget.ConstraintLayout
import com.bumptech.glide.Glide 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
@ -32,7 +32,6 @@ 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 FADE_DELAY = 5000L
private const val CAPTURE_ANIMATION_DURATION = 100L private const val CAPTURE_ANIMATION_DURATION = 100L
} }
@ -84,7 +83,6 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera
if (hasStorageAndCameraPermissions()) { if (hasStorageAndCameraPermissions()) {
resumeCameraItems() resumeCameraItems()
setupPreviewImage(mIsInPhotoMode) setupPreviewImage(mIsInPhotoMode)
scheduleFadeOut()
mFocusCircleView.setStrokeColor(getProperPrimaryColor()) mFocusCircleView.setStrokeColor(getProperPrimaryColor())
if (isVideoCaptureIntent() && mIsInPhotoMode) { if (isVideoCaptureIntent() && mIsInPhotoMode) {
@ -216,11 +214,11 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
initButtons() initButtons()
(btn_holder.layoutParams as RelativeLayout.LayoutParams).setMargins( (video_rec_curr_timer.layoutParams as ConstraintLayout.LayoutParams).setMargins(
0, 0,
0, 0,
0, 0,
(navigationBarHeight + resources.getDimension(R.dimen.activity_margin)).toInt() (navigationBarHeight + resources.getDimension(R.dimen.big_margin)).toInt()
) )
checkVideoCaptureIntent() checkVideoCaptureIntent()
@ -310,12 +308,8 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera
} }
private fun launchSettings() { private fun launchSettings() {
if (settings.alpha == 1f) { val intent = Intent(applicationContext, SettingsActivity::class.java)
val intent = Intent(applicationContext, SettingsActivity::class.java) startActivity(intent)
startActivity(intent)
} else {
fadeInButtons()
}
} }
private fun handleTogglePhotoVideo() { private fun handleTogglePhotoVideo() {
@ -415,34 +409,6 @@ class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener, Camera
} }
} }
private fun scheduleFadeOut() {
if (!config.keepSettingsVisible) {
mFadeHandler.postDelayed({
fadeOutButtons()
}, FADE_DELAY)
}
}
private fun fadeOutButtons() {
fadeAnim(settings, .5f)
fadeAnim(toggle_photo_video, .0f)
fadeAnim(change_resolution, .0f)
fadeAnim(last_photo_video_preview, .0f)
}
private fun fadeInButtons() {
fadeAnim(settings, 1f)
fadeAnim(toggle_photo_video, 1f)
fadeAnim(change_resolution, 1f)
fadeAnim(last_photo_video_preview, 1f)
scheduleFadeOut()
}
private fun fadeAnim(view: View, value: Float) {
view.animate().alpha(value).start()
view.isClickable = value != .0f
}
private fun showTimer() { private fun showTimer() {
video_rec_curr_timer.beVisible() video_rec_curr_timer.beVisible()
setupTimer() setupTimer()

View File

@ -47,7 +47,12 @@ class ChangeResolutionDialogX(
val items = photoResolutions.mapIndexed { index, resolution -> val items = photoResolutions.mapIndexed { index, resolution ->
val megapixels = resolution.megaPixels val megapixels = resolution.megaPixels
val aspectRatio = resolution.getAspectRatio(activity) val aspectRatio = resolution.getAspectRatio(activity)
RadioItem(index, "${resolution.width} x ${resolution.height} ($megapixels MP, $aspectRatio)") if (resolution.isFullScreen) {
//TODO: Extract to string resource
RadioItem(index, "Full")
} else {
RadioItem(index, "${resolution.width} x ${resolution.height} ($megapixels MP, $aspectRatio)")
}
} }
var selectionIndex = if (isFrontCamera) config.frontPhotoResIndex else config.backPhotoResIndex var selectionIndex = if (isFrontCamera) config.frontPhotoResIndex else config.backPhotoResIndex
selectionIndex = selectionIndex.coerceAtLeast(0) selectionIndex = selectionIndex.coerceAtLeast(0)

View File

@ -66,6 +66,7 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getInt(FRONT_VIDEO_RESOLUTION_INDEX, 0) get() = prefs.getInt(FRONT_VIDEO_RESOLUTION_INDEX, 0)
set(frontVideoResIndex) = prefs.edit().putInt(FRONT_VIDEO_RESOLUTION_INDEX, frontVideoResIndex).apply() set(frontVideoResIndex) = prefs.edit().putInt(FRONT_VIDEO_RESOLUTION_INDEX, frontVideoResIndex).apply()
//TODO: Remove keepSettingsVisible since the view has moved to the top
var keepSettingsVisible: Boolean var keepSettingsVisible: Boolean
get() = prefs.getBoolean(KEEP_SETTINGS_VISIBLE, false) get() = prefs.getBoolean(KEEP_SETTINGS_VISIBLE, false)
set(keepSettingsVisible) = prefs.edit().putBoolean(KEEP_SETTINGS_VISIBLE, keepSettingsVisible).apply() set(keepSettingsVisible) = prefs.edit().putBoolean(KEEP_SETTINGS_VISIBLE, keepSettingsVisible).apply()

View File

@ -28,13 +28,12 @@ class ImageQualityManager(
for (cameraId in cameraManager.cameraIdList) { for (cameraId in cameraManager.cameraIdList) {
try { try {
val characteristics = cameraManager.getCameraCharacteristics(cameraId) val characteristics = cameraManager.getCameraCharacteristics(cameraId)
for (lens in CAMERA_LENS) { val lensFacing = characteristics.get(CameraCharacteristics.LENS_FACING) ?: continue
if (characteristics.get(CameraCharacteristics.LENS_FACING) == lens) { if (lensFacing in CAMERA_LENS) {
val configMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) ?: continue val configMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) ?: continue
val imageSizes = configMap.getOutputSizes(ImageFormat.JPEG).map { MySize(it.width, it.height) } val imageSizes = configMap.getOutputSizes(ImageFormat.JPEG).map { MySize(it.width, it.height) }
val cameraSelector = lens.toCameraSelector() val cameraSelector = lensFacing.toCameraSelector()
imageQualities.add(CameraSelectorImageQualities(cameraSelector, imageSizes)) imageQualities.add(CameraSelectorImageQualities(cameraSelector, imageSizes))
}
} }
} catch (e: Exception) { } catch (e: Exception) {
activity.showErrorToast(e) activity.showErrorToast(e)
@ -52,19 +51,26 @@ class ImageQualityManager(
} }
fun getUserSelectedResolution(cameraSelector: CameraSelector): MySize { fun getUserSelectedResolution(cameraSelector: CameraSelector): MySize {
val index = if (cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA) config.frontPhotoResIndex else config.backPhotoResIndex val resolutions = getSupportedResolutions(cameraSelector)
return imageQualities.filter { it.camSelector == cameraSelector } var index = if (cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA) config.frontPhotoResIndex else config.backPhotoResIndex
.flatMap { it.qualities } index = index.coerceAtMost(resolutions.lastIndex)
.sortedWith(compareByDescending<MySize> { it.ratio }.thenByDescending { it.pixels }) return resolutions[index]
.distinctBy { it.pixels }
.filter { it.megaPixels != "0.0" }[index]
} }
fun getSupportedResolutions(cameraSelector: CameraSelector): List<MySize> { fun getSupportedResolutions(cameraSelector: CameraSelector): List<MySize> {
val fullScreenSize = getFullScreenResolution(cameraSelector)
return listOf(fullScreenSize) + imageQualities.filter { it.camSelector == cameraSelector }
.flatMap { it.qualities }
.sortedByDescending { it.pixels }
.distinctBy { it.getAspectRatio(activity) }
.filter { it.isSupported() }
}
private fun getFullScreenResolution(cameraSelector: CameraSelector): MySize {
return imageQualities.filter { it.camSelector == cameraSelector } return imageQualities.filter { it.camSelector == cameraSelector }
.flatMap { it.qualities } .flatMap { it.qualities }
.sortedWith(compareByDescending<MySize> { it.ratio }.thenByDescending { it.pixels }) .sortedByDescending { it.width }
.distinctBy { it.pixels } .first { it.isSupported() }
.filter { it.megaPixels != "0.0" } .copy(isFullScreen = true)
} }
} }

View File

@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.hardware.SensorManager import android.hardware.SensorManager
import android.hardware.display.DisplayManager import android.hardware.display.DisplayManager
import android.util.Rational
import android.util.Size import android.util.Size
import android.view.* import android.view.*
import android.view.GestureDetector.SimpleOnGestureListener import android.view.GestureDetector.SimpleOnGestureListener
@ -14,10 +15,12 @@ import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.video.* import androidx.camera.video.*
import androidx.camera.video.VideoCapture import androidx.camera.video.VideoCapture
import androidx.camera.view.PreviewView import androidx.camera.view.PreviewView
import androidx.camera.view.PreviewView.ScaleType
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.doOnLayout import androidx.core.view.doOnLayout
import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.window.layout.WindowMetricsCalculator
import com.bumptech.glide.load.ImageHeaderParser.UNKNOWN_ORIENTATION import com.bumptech.glide.load.ImageHeaderParser.UNKNOWN_ORIENTATION
import com.simplemobiletools.camera.R import com.simplemobiletools.camera.R
import com.simplemobiletools.camera.dialogs.ChangeResolutionDialogX import com.simplemobiletools.camera.dialogs.ChangeResolutionDialogX
@ -49,6 +52,7 @@ class CameraXPreview(
private val mainExecutor = ContextCompat.getMainExecutor(activity) private val mainExecutor = ContextCompat.getMainExecutor(activity)
private val displayManager = activity.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager private val displayManager = activity.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
private val mediaSoundHelper = MediaSoundHelper() private val mediaSoundHelper = MediaSoundHelper()
private val windowMetricsCalculator = WindowMetricsCalculator.getOrCreate()
private val videoQualityManager = VideoQualityManager(activity) private val videoQualityManager = VideoQualityManager(activity)
private val imageQualityManager = ImageQualityManager(activity) private val imageQualityManager = ImageQualityManager(activity)
private val exifRemover = ExifRemover(contentResolver) private val exifRemover = ExifRemover(contentResolver)
@ -120,7 +124,7 @@ class CameraXPreview(
private fun bindCameraUseCases() { private fun bindCameraUseCases() {
val cameraProvider = cameraProvider ?: throw IllegalStateException("Camera initialization failed.") val cameraProvider = cameraProvider ?: throw IllegalStateException("Camera initialization failed.")
val rotation = previewView.display.rotation
val resolution = if (isPhotoCapture) { val resolution = if (isPhotoCapture) {
imageQualityManager.getUserSelectedResolution(cameraSelector) imageQualityManager.getUserSelectedResolution(cameraSelector)
} else { } else {
@ -128,22 +132,94 @@ class CameraXPreview(
MySize(selectedQuality.width, selectedQuality.height) MySize(selectedQuality.width, selectedQuality.height)
} }
val isFullSize = resolution.isFullScreen
previewView.scaleType = if (isFullSize) ScaleType.FILL_CENTER else ScaleType.FIT_CENTER
val rotation = previewView.display.rotation
val rotatedResolution = getRotatedResolution(resolution, rotation) val rotatedResolution = getRotatedResolution(resolution, rotation)
preview = buildPreview(rotatedResolution, rotation) val previewUseCase = buildPreview(rotatedResolution, rotation)
val captureUseCase = getCaptureUseCase(rotatedResolution, rotation) val captureUseCase = getCaptureUseCase(rotatedResolution, rotation)
cameraProvider.unbindAll()
camera = cameraProvider.bindToLifecycle(
activity,
cameraSelector,
preview,
captureUseCase,
)
preview?.setSurfaceProvider(previewView.surfaceProvider) cameraProvider.unbindAll()
camera = if (isFullSize) {
val metrics = windowMetricsCalculator.computeCurrentWindowMetrics(activity).bounds
val screenWidth = metrics.width()
val screenHeight = metrics.height()
val viewPort = ViewPort.Builder(Rational(screenWidth, screenHeight), rotation).build()
val useCaseGroup = UseCaseGroup.Builder()
.addUseCase(previewUseCase)
.addUseCase(captureUseCase)
.setViewPort(viewPort)
.build()
cameraProvider.bindToLifecycle(
activity,
cameraSelector,
useCaseGroup,
)
} else {
cameraProvider.bindToLifecycle(
activity,
cameraSelector,
previewUseCase,
captureUseCase,
)
}
previewUseCase.setSurfaceProvider(previewView.surfaceProvider)
preview = previewUseCase
setupZoomAndFocus() setupZoomAndFocus()
} }
private fun getRotatedResolution(resolution: MySize, rotationDegrees: Int): Size {
return if (rotationDegrees == Surface.ROTATION_0 || rotationDegrees == Surface.ROTATION_180) {
Size(resolution.height, resolution.width)
} else {
Size(resolution.width, resolution.height)
}
}
private fun buildPreview(resolution: Size, rotation: Int): Preview {
return Preview.Builder()
.setTargetRotation(rotation)
.setTargetResolution(resolution)
.build()
}
private fun getCaptureUseCase(resolution: Size, rotation: Int): UseCase {
return if (isPhotoCapture) {
buildImageCapture(resolution, rotation).also {
imageCapture = it
}
} else {
buildVideoCapture().also {
videoCapture = it
}
}
}
private fun buildImageCapture(resolution: Size, rotation: Int): ImageCapture {
return Builder()
.setCaptureMode(CAPTURE_MODE_MAXIMIZE_QUALITY)
.setFlashMode(flashMode)
.setJpegQuality(config.photoQuality)
.setTargetRotation(rotation)
.setTargetResolution(resolution)
.build()
}
private fun buildVideoCapture(): VideoCapture<Recorder> {
val qualitySelector = QualitySelector.from(
videoQualityManager.getUserSelectedQuality(cameraSelector).toCameraXQuality(),
FallbackStrategy.higherQualityOrLowerThan(Quality.SD),
)
val recorder = Recorder.Builder()
.setQualitySelector(qualitySelector)
.build()
return VideoCapture.withOutput(recorder)
}
private fun setupCameraObservers() { private fun setupCameraObservers() {
listener.setFlashAvailable(camera?.cameraInfo?.hasFlashUnit() ?: false) listener.setFlashAvailable(camera?.cameraInfo?.hasFlashUnit() ?: false)
listener.onChangeCamera(isFrontCameraInUse()) listener.onChangeCamera(isFrontCameraInUse())
@ -166,56 +242,6 @@ class CameraXPreview(
} }
} }
private fun getCaptureUseCase(resolution: Size, rotation: Int): UseCase {
return if (isPhotoCapture) {
cameraProvider?.unbind(videoCapture)
buildImageCapture(resolution, rotation).also {
imageCapture = it
}
} else {
cameraProvider?.unbind(imageCapture)
buildVideoCapture().also {
videoCapture = it
}
}
}
private fun buildImageCapture(resolution: Size, rotation: Int): ImageCapture {
return Builder()
.setCaptureMode(CAPTURE_MODE_MAXIMIZE_QUALITY)
.setFlashMode(flashMode)
.setJpegQuality(config.photoQuality)
.setTargetRotation(rotation)
.setTargetResolution(resolution)
.build()
}
private fun getRotatedResolution(resolution: MySize, rotationDegrees: Int): Size {
return if (rotationDegrees == Surface.ROTATION_0 || rotationDegrees == Surface.ROTATION_180) {
Size(resolution.height, resolution.width)
} else {
Size(resolution.width, resolution.height)
}
}
private fun buildPreview(resolution: Size, rotation: Int): Preview {
return Preview.Builder()
.setTargetRotation(rotation)
.setTargetResolution(resolution)
.build()
}
private fun buildVideoCapture(): VideoCapture<Recorder> {
val qualitySelector = QualitySelector.from(
videoQualityManager.getUserSelectedQuality(cameraSelector).toCameraXQuality(),
FallbackStrategy.higherQualityOrLowerThan(Quality.SD),
)
val recorder = Recorder.Builder()
.setQualitySelector(qualitySelector)
.build()
return VideoCapture.withOutput(recorder)
}
private fun hasBackCamera(): Boolean { private fun hasBackCamera(): Boolean {
return cameraProvider?.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA) ?: false return cameraProvider?.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA) ?: false
} }

View File

@ -4,18 +4,19 @@ import android.content.Context
import android.util.Size import android.util.Size
import com.simplemobiletools.camera.R import com.simplemobiletools.camera.R
data class MySize(val width: Int, val height: Int) { data class MySize(val width: Int, val height: Int, val isFullScreen: Boolean = false) {
companion object { companion object {
private const val ONE_MEGA_PIXEL = 1000000 private const val ONE_MEGA_PIXEL = 1000000
private const val ZERO_MEGA_PIXEL = "0.0"
} }
val ratio = width / height.toFloat() private val ratio = width / height.toFloat()
val pixels: Int = width * height val pixels: Int = width * height
val megaPixels: String = String.format("%.1f", (width * height.toFloat()) / ONE_MEGA_PIXEL) val megaPixels: String = String.format("%.1f", (width * height.toFloat()) / ONE_MEGA_PIXEL)
fun isSixteenToNine() = ratio == 16 / 9f private fun isSixteenToNine() = ratio == 16 / 9f
private fun isFiveToThree() = ratio == 5 / 3f private fun isFiveToThree() = ratio == 5 / 3f
@ -37,6 +38,10 @@ data class MySize(val width: Int, val height: Int) {
private fun isSquare() = width == height private fun isSquare() = width == height
fun isSupported(): Boolean {
return (isFourToThree() || isSixteenToNine() || isSquare()) && megaPixels != ZERO_MEGA_PIXEL
}
fun getAspectRatio(context: Context) = when { fun getAspectRatio(context: Context) = when {
isSixteenToNine() -> "16:9" isSixteenToNine() -> "16:9"
isFiveToThree() -> "5:3" isFiveToThree() -> "5:3"

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="90"
android:endColor="@android:color/transparent"
android:startColor="@color/gradient_grey_start"/>
</shape>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="270"
android:endColor="@android:color/transparent"
android:startColor="@color/gradient_grey_start"/>
</shape>

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/view_holder" android:id="@+id/view_holder"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@android:color/black"> android:background="@android:color/black"
android:fitsSystemWindows="true">
<androidx.camera.view.PreviewView <androidx.camera.view.PreviewView
android:id="@+id/preview_view" android:id="@+id/preview_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent" />
app:scaleType="fitStart" />
<View <View
android:id="@+id/capture_black_screen" android:id="@+id/capture_black_screen"
@ -20,24 +20,28 @@
android:alpha="0" android:alpha="0"
android:background="@color/md_grey_black" /> android:background="@color/md_grey_black" />
<View
android:id="@+id/top_overlay"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@drawable/gradient_background_flipped"
app:layout_constraintBottom_toBottomOf="@id/settings"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView <ImageView
android:id="@+id/settings" android:id="@+id/settings"
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_alignParentEnd="true"
android:contentDescription="@string/settings" android:contentDescription="@string/settings"
android:padding="@dimen/normal_margin" android:padding="@dimen/normal_margin"
android:src="@drawable/ic_settings_cog_vector" /> android:src="@drawable/ic_settings_cog_vector"
app:layout_constraintEnd_toStartOf="@id/change_resolution"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="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_below="@+id/settings"
android:layout_alignParentEnd="true"
android:contentDescription="@string/toggle_photo_video"
android:padding="@dimen/normal_margin"
android:src="@drawable/ic_video_vector" />
<ImageView <ImageView
android:id="@+id/change_resolution" android:id="@+id/change_resolution"
@ -47,61 +51,106 @@
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:contentDescription="@string/resolution" android:contentDescription="@string/resolution"
android:padding="@dimen/normal_margin" android:padding="@dimen/normal_margin"
android:src="@drawable/ic_resolution_vector" /> android:src="@drawable/ic_resolution_vector"
app:layout_constraintEnd_toStartOf="@id/toggle_flash"
app:layout_constraintStart_toEndOf="@id/settings"
app:layout_constraintTop_toTopOf="@id/settings" />
<ImageView
android:id="@+id/toggle_flash"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_weight="1"
android:contentDescription="@string/toggle_flash"
android:padding="@dimen/normal_margin"
android:src="@drawable/ic_flash_off_vector"
app:layout_constraintEnd_toStartOf="@id/toggle_photo_video"
app:layout_constraintStart_toEndOf="@id/change_resolution"
app:layout_constraintTop_toTopOf="@id/settings" />
<ImageView
android:id="@+id/toggle_photo_video"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:contentDescription="@string/toggle_photo_video"
android:padding="@dimen/normal_margin"
android:src="@drawable/ic_video_vector"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/toggle_flash"
app:layout_constraintTop_toTopOf="@id/settings" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/top_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="settings,change_resolution,toggle_flash,toggle_photo_video" />
<View
android:id="@+id/bottom_overlay"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@drawable/gradient_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/shutter" />
<ImageView
android:id="@+id/toggle_camera"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:contentDescription="@string/toggle_camera"
android:padding="@dimen/normal_margin"
android:src="@drawable/ic_camera_front_vector"
app:layout_constraintBottom_toBottomOf="@id/shutter"
app:layout_constraintEnd_toStartOf="@id/shutter"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/shutter" />
<ImageView
android:id="@+id/shutter"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_marginBottom="@dimen/small_margin"
android:contentDescription="@string/shutter"
android:src="@drawable/ic_shutter_vector"
app:layout_constraintBottom_toTopOf="@id/video_rec_curr_timer"
app:layout_constraintEnd_toStartOf="@id/last_photo_video_preview"
app:layout_constraintStart_toEndOf="@id/toggle_camera"
app:layout_constraintVertical_bias="1"
app:layout_goneMarginBottom="@dimen/big_margin" />
<ImageView <ImageView
android:id="@+id/last_photo_video_preview" android:id="@+id/last_photo_video_preview"
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/change_resolution"
android:layout_alignParentEnd="true"
android:contentDescription="@string/view_last_media" android:contentDescription="@string/view_last_media"
android:padding="@dimen/medium_margin" /> android:padding="@dimen/medium_margin"
app:layout_constraintBottom_toBottomOf="@id/shutter"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/shutter"
app:layout_constraintTop_toTopOf="@id/shutter"
tools:src="@tools:sample/backgrounds/scenic" />
<LinearLayout
android:id="@+id/btn_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:gravity="center_horizontal|bottom">
<ImageView
android:id="@+id/toggle_camera"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_weight="1"
android:contentDescription="@string/toggle_camera"
android:padding="@dimen/normal_margin"
android:src="@drawable/ic_camera_front_vector" />
<ImageView
android:id="@+id/shutter"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_weight="1"
android:contentDescription="@string/shutter"
android:src="@drawable/ic_shutter_vector" />
<ImageView
android:id="@+id/toggle_flash"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_weight="1"
android:contentDescription="@string/toggle_flash"
android:padding="@dimen/normal_margin"
android:src="@drawable/ic_flash_off_vector" />
</LinearLayout>
<TextView <TextView
android:id="@+id/video_rec_curr_timer" android:id="@+id/video_rec_curr_timer"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_above="@+id/btn_holder"
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/small_margin"
android:layout_marginBottom="@dimen/big_margin"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:visibility="gone" android:visibility="gone"
tools:text="00:00" /> app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="00:00"
tools:visibility="gone" />
</RelativeLayout> </androidx.constraintlayout.widget.ConstraintLayout>