Loading tests/SilkFX/res/layout/activity_glass.xml +13 −4 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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" Loading Loading @@ -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" Loading @@ -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" /> Loading tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt +8 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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) Loading @@ -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) } } Loading Loading @@ -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?) {} Loading tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt +90 −10 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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) Loading @@ -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) Loading @@ -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 Loading
tests/SilkFX/res/layout/activity_glass.xml +13 −4 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -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" Loading Loading @@ -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" Loading @@ -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" /> Loading
tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt +8 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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) Loading @@ -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) } } Loading Loading @@ -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?) {} Loading
tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt +90 −10 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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) Loading @@ -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) Loading @@ -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