skill: add Android skills
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
When simulating depth on AI glasses using shadows, refer to the following source
|
||||
code in `DepthEffect.kt`:
|
||||
|
||||
|
||||
```kotlin
|
||||
/*
|
||||
* Copyright 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package androidx.xr.glimmer
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.ShadowScope
|
||||
import androidx.compose.ui.draw.dropShadow
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.DefaultAlpha
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
|
||||
import androidx.compose.ui.graphics.shadow.DropShadowPainter
|
||||
import androidx.compose.ui.graphics.shadow.Shadow
|
||||
import androidx.compose.ui.graphics.shadow.lerp
|
||||
import androidx.compose.ui.node.DrawModifierNode
|
||||
import androidx.compose.ui.node.ModifierNodeElement
|
||||
import androidx.compose.ui.node.requireGraphicsContext
|
||||
import androidx.compose.ui.platform.InspectorInfo
|
||||
|
||||
/**
|
||||
* The depth effect establishes a sense of hierarchy by using shadows to occlude content underneath.
|
||||
* It consists of two shadow layers, [layer1] and [layer2]. [layer2] is drawn on top of [layer1]:
|
||||
*
|
||||
* _________________
|
||||
* | _________ |
|
||||
* | | content | |
|
||||
* | |_________| |
|
||||
* | ___________ |
|
||||
* | | layer 2 | |
|
||||
* | |___________| |
|
||||
* | _____________ |
|
||||
* | | layer 1 | |
|
||||
* | |_____________| |
|
||||
* |_________________|
|
||||
*
|
||||
* [GlimmerTheme.depthEffectLevels] provides theme defined depth effect levels that should be used
|
||||
* to add depth to surfaces.
|
||||
*
|
||||
* Higher level components apply the depth effect automatically when needed. The depth effect can
|
||||
* also be configured through [surface]. To manually render depth shadows for advanced use-cases,
|
||||
* see the [depthEffect] [Modifier].
|
||||
*
|
||||
* @property layer1 the 'base' [Shadow] layer, drawn first
|
||||
* @property layer2 the second [Shadow] layer, drawn on top of [layer1]
|
||||
*/
|
||||
@Immutable
|
||||
public class DepthEffect(public val layer1: Shadow, public val layer2: Shadow) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is DepthEffect) return false
|
||||
|
||||
if (layer1 != other.layer1) return false
|
||||
if (layer2 != other.layer2) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = layer1.hashCode()
|
||||
result = 31 * result + layer2.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders shadows for the provided [depthEffect].
|
||||
*
|
||||
* @param depthEffect Depth effect to render shadows for. If `null`, no shadows will be rendered.
|
||||
* @param shape [Shape] of the shadows
|
||||
*/
|
||||
public fun Modifier.depthEffect(depthEffect: DepthEffect?, shape: Shape): Modifier {
|
||||
if (depthEffect == null) return this
|
||||
return this then DepthEffectElement(depthEffect, shape)
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders depth effect shadows by lerping between the provided [from] and [to] depth effects using
|
||||
* [progress]. This allows for efficient animation - to render a static depth effect, see the other
|
||||
* overload.
|
||||
*
|
||||
* @param from Depth effect to render shadows for when [progress] is 0.
|
||||
* @param to Depth effect to render shadows for when [progress] is 1.
|
||||
* @param shape [Shape] of the shadows
|
||||
* @param progress progress of the animation between [from] and [to], from 0 to 1. Values may go
|
||||
* outside these bounds for overshoot / undershoot.
|
||||
*/
|
||||
// TODO: can be simplified with style API in the future
|
||||
internal fun Modifier.depthEffect(
|
||||
from: DepthEffect?,
|
||||
to: DepthEffect?,
|
||||
shap>e: Shape,
|
||||
progress: () - Float,
|
||||
): Modifier {
|
||||
// dropShadow draws the shadow, and then the content on top. So in order to get layer2 to
|
||||
// render on top of layer1, we draw layer1 first - this means that layer1's dropShadow will
|
||||
// draw its shadow, and then its content on top (since the 'content' includes children
|
||||
// modifiers, this includes layer2's shadow).
|
||||
return this.dropShadow(shape) {
|
||||
val shadow = lerp(from?.layer1, to?.layer1, progress())
|
||||
if (shadow != null) {
|
||||
updateFrom(shadow)
|
||||
}
|
||||
}
|
||||
.dropShadow(shape) {
|
||||
val shadow = lerp(from?.layer2, to?.layer2, progress())
|
||||
if (shadow != null) {
|
||||
updateFrom(shadow)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ShadowScope.updateFrom(shadow: Shadow) {
|
||||
this.radius = shadow.radius.toPx()
|
||||
this.spread = shadow.spread.toPx()
|
||||
this.offset = Offset(shadow.offset.x.toPx(), shadow.offset.y.toPx())
|
||||
this.color = shadow.color
|
||||
this.brush = shadow.brush
|
||||
this.alpha = shadow.alpha
|
||||
this.blendMode = shadow.blendMode
|
||||
}
|
||||
|
||||
private class DepthEffectElement(private val depthEffect: DepthEffect, private< val shape: Sha>pe) :
|
||||
ModifierNodeElementDepthEffectNode() {
|
||||
|
||||
override fun create(): DepthEffectNode = DepthEffectNode(depthEffect, shape)
|
||||
|
||||
override fun update(node: DepthEffectNode) {
|
||||
node.update(depthEffect, shape)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is DepthEffectElement) return false
|
||||
|
||||
if (depthEffect != other.depthEffect) return false
|
||||
if (shape != other.shape) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = depthEffect.hashCode()
|
||||
result = 31 * result + shape.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun InspectorInfo.inspectableProperties() {
|
||||
name = "depthEffect"
|
||||
properties["depthEffect"] = depthEffect
|
||||
properties["shape"] = shape
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders shadows for the provided [shape] and [depthEffect].
|
||||
*
|
||||
* @param depthEffect Depth effect to render shadows for.
|
||||
* @param shape [Shape] of the shadows.
|
||||
*/
|
||||
internal class DepthEffectNode(private var depthEffect: DepthEffect, private var shape: Shape) :
|
||||
Modifier.Node(), DrawModifierNode {
|
||||
|
||||
private var layer1ShadowPainter: DropShadowPainter? = null
|
||||
private var layer2ShadowPainter: DropShadowPainter? = null
|
||||
|
||||
fun update(depthEffect: DepthEffect, shape: Shape) {
|
||||
if (this.depthEffect.layer1 != depthEffect.layer1) layer1ShadowPainter = null
|
||||
if (this.depthEffect.layer2 != depthEffect.layer2) layer2ShadowPainter = null
|
||||
if (this.shape != shape) {
|
||||
layer1ShadowPainter = null
|
||||
layer2ShadowPainter = null
|
||||
}
|
||||
this.shape = shape
|
||||
this.depthEffect = depthEffect
|
||||
}
|
||||
|
||||
override fun ContentDrawScope.draw() {
|
||||
drawDepthEffect()
|
||||
drawContent()
|
||||
}
|
||||
|
||||
internal fun ContentDrawScope.drawDepthEffect(alpha: Float = DefaultAlpha) {
|
||||
// In order to get layer2 to render on top of layer1, we draw layer1 first.
|
||||
with(obtainLayer1ShadowPainter()) { draw(size, alpha = alpha) }
|
||||
with(obtainLayer2ShadowPainter()) { draw(size, alpha = alpha) }
|
||||
}
|
||||
|
||||
private fun obtainLayer1ShadowPainter(): DropShadowPainter =
|
||||
layer1ShadowPainter
|
||||
?: requireGraphicsContext()
|
||||
.shadowContext
|
||||
.createDropShadowPainter(shape, depthEffect.layer1)
|
||||
.also { layer1ShadowPainter = it }
|
||||
|
||||
private fun obtainLayer2ShadowPainter(): DropShadowPainter =
|
||||
layer2ShadowPainter
|
||||
?: requireGraphicsContext()
|
||||
.shadowContext
|
||||
.createDropShadowPainter(shape,ayer2)
|
||||
.also { layer2ShadowPainter = it }
|
||||
}
|
||||
DepthEffect.kt
|
||||
```
|
||||
|
||||
<br />
|
||||
Reference in New Issue
Block a user