Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bf95b70f authored by Lucas Dupin's avatar Lucas Dupin Committed by Android (Google) Code Review
Browse files

Merge "Add gyro effect to glass prototype"

parents bf97afb5 28927270
Loading
Loading
Loading
Loading
+13 −4
Original line number Diff line number Diff line
@@ -41,7 +41,16 @@
        app:layout_constraintBottom_toTopOf="@+id/bottomPanel"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
        app:layout_constraintTop_toTopOf="parent">
        <TextView
            android:id="@+id/textOverlay"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18dp"
            android:layout_gravity="center"
            android:textColor="#ffffff"
            android:text="Lorem Ipsum dolor sit amet." />
    </com.android.test.silkfx.materials.GlassView>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/bottomPanel"
@@ -90,7 +99,7 @@
        android:layout_marginEnd="12dp"
        android:layout_marginStart="12dp"
        android:max="150"
        android:progress="20"
        android:progress="40"
        app:layout_constraintBottom_toTopOf="@+id/materialOpacityTitle"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
@@ -118,7 +127,7 @@
        android:layout_marginEnd="12dp"
        android:layout_marginBottom="24dp"
        android:max="100"
        android:progress="5"
        android:progress="15"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
@@ -141,7 +150,7 @@
        android:layout_height="wrap_content"
        android:layout_marginStart="24dp"
        android:layout_marginBottom="8dp"
        android:text="Material Opacity"
        android:text="Soft light Opacity"
        android:textColor="@android:color/white"
        app:layout_constraintBottom_toTopOf="@+id/materialOpacity"
        app:layout_constraintStart_toStartOf="parent" />
+8 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ class GlassActivity : Activity(), SeekBar.OnSeekBarChangeListener {
    lateinit var scrimOpacityValue: TextView
    lateinit var blurRadiusValue: TextView
    lateinit var zoomValue: TextView
    lateinit var textOverlay: TextView

    var background: Bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
    set(value) {
@@ -74,6 +75,7 @@ class GlassActivity : Activity(), SeekBar.OnSeekBarChangeListener {
        scrimOpacityValue = requireViewById(R.id.scrimOpacityValue)
        blurRadiusValue = requireViewById(R.id.blurRadiusValue)
        zoomValue = requireViewById(R.id.zoomValue)
        textOverlay = requireViewById(R.id.textOverlay)

        background = BitmapFactory.decodeResource(resources, R.drawable.background1)

@@ -90,6 +92,7 @@ class GlassActivity : Activity(), SeekBar.OnSeekBarChangeListener {

        lightMaterialSwitch.setOnCheckedChangeListener { _, isChecked ->
            materialView.color = if (isChecked) Color.WHITE else Color.BLACK
            textOverlay.setTextColor(if (isChecked) Color.BLACK else Color.WHITE)
        }
    }

@@ -120,6 +123,11 @@ class GlassActivity : Activity(), SeekBar.OnSeekBarChangeListener {
        }
    }

    override fun onStop() {
        super.onStop()
        materialView.resetGyroOffsets()
    }

    override fun onStartTrackingTouch(seekBar: SeekBar?) {}
    override fun onStopTrackingTouch(seekBar: SeekBar?) {}

+90 −10
Original line number Diff line number Diff line
@@ -25,23 +25,83 @@ import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Outline
import android.graphics.Paint
import android.graphics.RadialGradient
import android.graphics.Rect
import android.graphics.Shader
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.util.AttributeSet
import android.view.View
import android.view.ViewOutlineProvider
import android.widget.FrameLayout
import com.android.internal.graphics.ColorUtils
import com.android.test.silkfx.R
import kotlin.math.sin
import kotlin.math.sqrt

class GlassView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
class GlassView(context: Context, attributeSet: AttributeSet) : FrameLayout(context, attributeSet) {

    var noise = BitmapFactory.decodeResource(resources, R.drawable.noise)
    var materialPaint = Paint()
    var scrimPaint = Paint()
    var noisePaint = Paint()
    var blurPaint = Paint()
    private val textureTranslationMultiplier = 200f

    val src = Rect()
    val dst = Rect()
    private var gyroXRotation = 0f
    private var gyroYRotation = 0f

    private var noise = BitmapFactory.decodeResource(resources, R.drawable.noise)
    private var materialPaint = Paint()
    private var scrimPaint = Paint()
    private var noisePaint = Paint()
    private var blurPaint = Paint()

    private val src = Rect()
    private val dst = Rect()

    private val sensorManager = context.getSystemService(SensorManager::class.java)
    private val sensorListener = object : SensorEventListener {

        // Constant to convert nanoseconds to seconds.
        private val NS2S = 1.0f / 1000000000.0f
        private val EPSILON = 0.000001f
        private var timestamp: Float = 0f

        override fun onSensorChanged(event: SensorEvent?) {
            // This timestep's delta rotation to be multiplied by the current rotation
            // after computing it from the gyro sample data.
            if (timestamp != 0f && event != null) {
                val dT = (event.timestamp - timestamp) * NS2S
                // Axis of the rotation sample, not normalized yet.
                var axisX: Float = event.values[0]
                var axisY: Float = event.values[1]
                var axisZ: Float = event.values[2]

                // Calculate the angular speed of the sample
                val omegaMagnitude: Float = sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ)

                // Normalize the rotation vector if it's big enough to get the axis
                // (that is, EPSILON should represent your maximum allowable margin of error)
                if (omegaMagnitude > EPSILON) {
                    axisX /= omegaMagnitude
                    axisY /= omegaMagnitude
                    axisZ /= omegaMagnitude
                }

                // Integrate around this axis with the angular speed by the timestep
                // in order to get a delta rotation from this sample over the timestep
                // We will convert this axis-angle representation of the delta rotation
                // into a quaternion before turning it into the rotation matrix.
                val thetaOverTwo: Float = omegaMagnitude * dT / 2.0f
                val sinThetaOverTwo: Float = sin(thetaOverTwo)
                gyroXRotation += sinThetaOverTwo * axisX
                gyroYRotation += sinThetaOverTwo * axisY

                invalidate()
            }
            timestamp = event?.timestamp?.toFloat() ?: 0f
        }

        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) { }
    }

    var backgroundBitmap: Bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
    set(value) {
@@ -97,6 +157,7 @@ class GlassView(context: Context, attributeSet: AttributeSet) : View(context, at
    }

    init {
        setWillNotDraw(false)
        materialPaint.blendMode = BlendMode.SOFT_LIGHT
        noisePaint.blendMode = BlendMode.SOFT_LIGHT
        noisePaint.shader = BitmapShader(noise, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT)
@@ -112,12 +173,25 @@ class GlassView(context: Context, attributeSet: AttributeSet) : View(context, at
        clipToOutline = true
    }

    override fun onAttachedToWindow() {
        sensorManager?.getSensorList(Sensor.TYPE_GYROSCOPE)?.firstOrNull().let {
            sensorManager?.registerListener(sensorListener, it, SensorManager.SENSOR_DELAY_GAME)
        }
    }

    override fun onDetachedFromWindow() {
        sensorManager?.unregisterListener(sensorListener)
    }

    override fun onDraw(canvas: Canvas?) {
        src.set(-width / 2, -height / 2, width / 2, height / 2)
        src.scale(1.0f + zoom)
        val centerX = left + width / 2
        val centerY = top + height / 2
        src.set(src.left + centerX, src.top + centerY, src.right + centerX, src.bottom + centerY)
        val textureXOffset = (textureTranslationMultiplier * gyroYRotation).toInt()
        val textureYOffset = (textureTranslationMultiplier * gyroXRotation).toInt()
        src.set(src.left + centerX + textureXOffset, src.top + centerY + textureYOffset,
                src.right + centerX + textureXOffset, src.bottom + centerY + textureYOffset)

        dst.set(0, 0, width, height)
        canvas?.drawBitmap(backgroundBitmap, src, dst, blurPaint)
@@ -125,4 +199,10 @@ class GlassView(context: Context, attributeSet: AttributeSet) : View(context, at
        canvas?.drawRect(dst, noisePaint)
        canvas?.drawRect(dst, scrimPaint)
    }

    fun resetGyroOffsets() {
        gyroXRotation = 0f
        gyroYRotation = 0f
        invalidate()
    }
}
 No newline at end of file