Aperture: Implement exposure level control

Change-Id: I9f2e5139bfbd47aaf3c94b573232e027ab9921ca
diff --git a/app/src/main/java/org/lineageos/aperture/CameraActivity.kt b/app/src/main/java/org/lineageos/aperture/CameraActivity.kt
index a7923f0..9ef0e36 100644
--- a/app/src/main/java/org/lineageos/aperture/CameraActivity.kt
+++ b/app/src/main/java/org/lineageos/aperture/CameraActivity.kt
@@ -71,6 +71,7 @@
 import org.lineageos.aperture.ui.CountDownView
 import org.lineageos.aperture.ui.GridView
 import org.lineageos.aperture.ui.LevelerView
+import org.lineageos.aperture.ui.VerticalSlider
 import org.lineageos.aperture.utils.Camera
 import org.lineageos.aperture.utils.CameraFacing
 import org.lineageos.aperture.utils.CameraMode
@@ -91,6 +92,7 @@
     private val cameraModeHighlight by lazy { findViewById<MaterialButton>(R.id.cameraModeHighlight) }
     private val countDownView by lazy { findViewById<CountDownView>(R.id.countDownView) }
     private val effectButton by lazy { findViewById<ImageButton>(R.id.effectButton) }
+    private val exposureLevel by lazy { findViewById<VerticalSlider>(R.id.exposureLevel) }
     private val flashButton by lazy { findViewById<ImageButton>(R.id.flashButton) }
     private val flipCameraButton by lazy { findViewById<ImageButton>(R.id.flipCameraButton) }
     private val galleryButton by lazy { findViewById<ImageView>(R.id.galleryButton) }
@@ -174,6 +176,9 @@
                 MSG_HIDE_FOCUS_RING -> {
                     viewFinderFocus.visibility = View.GONE
                 }
+                MSG_HIDE_EXPOSURE_SLIDER -> {
+                    exposureLevel.visibility = View.GONE
+                }
             }
         }
     }
@@ -364,6 +369,7 @@
             return@setOnTouchListener false
         }
         viewFinder.setOnClickListener { view ->
+            exposureLevel.isVisible = true
             viewFinderTouchEvent?.let {
                 viewFinderFocus.x = it.x - (viewFinderFocus.width / 2)
                 viewFinderFocus.y = it.y - (viewFinderFocus.height / 2)
@@ -371,6 +377,8 @@
                 viewFinderFocus.x = (view.width - viewFinderFocus.width) / 2f
                 viewFinderFocus.y = (view.height - viewFinderFocus.height) / 2f
             }
+            handler.removeMessages(MSG_HIDE_EXPOSURE_SLIDER)
+            handler.sendMessageDelayed(handler.obtainMessage(MSG_HIDE_EXPOSURE_SLIDER), 2000)
         }
 
         // Observe preview stream state
@@ -407,6 +415,19 @@
             "%.1fx".format(cameraController.zoomState.value?.zoomRatio)
         }
 
+        // Set expose level callback & text formatter
+        exposureLevel.onProgressChangedByUser = {
+            cameraController.cameraControl?.setExposureCompensationIndex(
+                Int.mapToRange(camera.exposureCompensationRange, it)
+            )
+
+            handler.removeMessages(MSG_HIDE_EXPOSURE_SLIDER)
+            handler.sendMessageDelayed(handler.obtainMessage(MSG_HIDE_EXPOSURE_SLIDER), 2000)
+        }
+        exposureLevel.textFormatter = {
+            Int.mapToRange(camera.exposureCompensationRange, it).toString()
+        }
+
         // Set primary bar button callbacks
         qrModeButton.setOnClickListener { changeCameraMode(CameraMode.QR) }
         photoModeButton.setOnClickListener { changeCameraMode(CameraMode.PHOTO) }
@@ -814,6 +835,11 @@
         setFlashMode(sharedPreferences.photoFlashMode)
         setMicrophoneMode(sharedPreferences.lastMicMode)
 
+        // Reset exposure level
+        exposureLevel.progress = 0.5f
+        exposureLevel.steps =
+            camera.exposureCompensationRange.upper - camera.exposureCompensationRange.lower
+
         // Update icons from last state
         updateCameraModeButtons()
         updateTimerModeIcon()
@@ -1373,5 +1399,6 @@
 
         private const val MSG_HIDE_ZOOM_SLIDER = 0
         private const val MSG_HIDE_FOCUS_RING = 1
+        private const val MSG_HIDE_EXPOSURE_SLIDER = 2
     }
 }
diff --git a/app/src/main/java/org/lineageos/aperture/IntExt.kt b/app/src/main/java/org/lineageos/aperture/IntExt.kt
index ba3187d..639341a 100644
--- a/app/src/main/java/org/lineageos/aperture/IntExt.kt
+++ b/app/src/main/java/org/lineageos/aperture/IntExt.kt
@@ -7,6 +7,7 @@
 package org.lineageos.aperture
 
 import android.content.res.Resources.getSystem
+import android.util.Range
 import kotlin.math.roundToInt
 
 val Int.px
@@ -14,3 +15,7 @@
 
 val Int.dp
     get() = (this / getSystem().displayMetrics.density).roundToInt()
+
+internal fun Int.Companion.mapToRange(range: Range<Int>, percentage: Float): Int {
+    return (((range.upper - range.lower) * percentage) + range.lower).roundToInt()
+}
diff --git a/app/src/main/java/org/lineageos/aperture/utils/Camera.kt b/app/src/main/java/org/lineageos/aperture/utils/Camera.kt
index be68bf4..9b7dffa 100644
--- a/app/src/main/java/org/lineageos/aperture/utils/Camera.kt
+++ b/app/src/main/java/org/lineageos/aperture/utils/Camera.kt
@@ -28,6 +28,7 @@
             else -> CameraFacing.UNKNOWN
         }
 
+    val exposureCompensationRange = cameraInfo.exposureState.exposureCompensationRange
     val hasFlashUnit = cameraInfo.hasFlashUnit()
 
     val supportedVideoQualities: MutableList<Quality> =
diff --git a/app/src/main/res/layout/activity_camera.xml b/app/src/main/res/layout/activity_camera.xml
index 7152ec7..cc99305 100644
--- a/app/src/main/res/layout/activity_camera.xml
+++ b/app/src/main/res/layout/activity_camera.xml
@@ -218,6 +218,20 @@
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent" />
 
+    <org.lineageos.aperture.ui.VerticalSlider
+        android:id="@+id/exposureLevel"
+        android:layout_width="32dp"
+        android:layout_height="250dp"
+        android:layout_marginEnd="8dp"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="@+id/viewFinder"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="@+id/viewFinder"
+        app:thumbColor="@color/white"
+        app:thumbTextColor="@color/black"
+        app:thumbTextSize="10dp"
+        app:trackColor="#7FCCCCCC" />
+
     <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/primaryBarLayout"
         android:layout_width="0dp"