Merge "Implement DoubleShadowIconDrawable for use in dream complications" into tm-qpr-dev

This commit is contained in:
Lucas Silva
2022-09-22 02:35:44 +00:00
committed by Android (Google) Code Review
8 changed files with 411 additions and 159 deletions

View File

@@ -14,8 +14,9 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<com.android.systemui.dreams.complication.DoubleShadowTextClock
<com.android.systemui.shared.shadow.DoubleShadowTextClock
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/time_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -25,4 +26,13 @@
android:format24Hour="@string/dream_time_complication_24_hr_time_format"
android:fontFeatureSettings="pnum, lnum"
android:letterSpacing="0.02"
android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"/>
android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"
app:keyShadowBlur="@dimen/dream_overlay_clock_key_text_shadow_radius"
app:keyShadowOffsetX="@dimen/dream_overlay_clock_key_text_shadow_dx"
app:keyShadowOffsetY="@dimen/dream_overlay_clock_key_text_shadow_dy"
app:keyShadowAlpha="0.3"
app:ambientShadowBlur="@dimen/dream_overlay_clock_ambient_text_shadow_radius"
app:ambientShadowOffsetX="@dimen/dream_overlay_clock_ambient_text_shadow_dx"
app:ambientShadowOffsetY="@dimen/dream_overlay_clock_ambient_text_shadow_dy"
app:ambientShadowAlpha="0.3"
/>

View File

@@ -25,4 +25,39 @@
<attr name="lockScreenWeight" format="integer" />
<attr name="chargeAnimationDelay" format="integer" />
</declare-styleable>
<declare-styleable name="DoubleShadowAttrDeclare">
<attr name="keyShadowBlur" format="dimension" />
<attr name="keyShadowOffsetX" format="dimension" />
<attr name="keyShadowOffsetY" format="dimension" />
<attr name="keyShadowAlpha" format="float" />
<attr name="ambientShadowBlur" format="dimension" />
<attr name="ambientShadowOffsetX" format="dimension" />
<attr name="ambientShadowOffsetY" format="dimension" />
<attr name="ambientShadowAlpha" format="float" />
</declare-styleable>
<declare-styleable name="DoubleShadowTextClock">
<attr name="keyShadowBlur" />
<attr name="keyShadowOffsetX" />
<attr name="keyShadowOffsetY" />
<attr name="keyShadowAlpha" />
<attr name="ambientShadowBlur" />
<attr name="ambientShadowOffsetX" />
<attr name="ambientShadowOffsetY" />
<attr name="ambientShadowAlpha" />
</declare-styleable>
<declare-styleable name="DoubleShadowTextView">
<attr name="keyShadowBlur" />
<attr name="keyShadowOffsetX" />
<attr name="keyShadowOffsetY" />
<attr name="keyShadowAlpha" />
<attr name="ambientShadowBlur" />
<attr name="ambientShadowOffsetX" />
<attr name="ambientShadowOffsetY" />
<attr name="ambientShadowAlpha" />
<attr name="drawableIconSize" format="dimension" />
<attr name="drawableIconInsetSize" format="dimension" />
</declare-styleable>
</resources>

View File

@@ -0,0 +1,124 @@
/*
* Copyright (C) 2022 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 com.android.systemui.shared.shadow
import android.graphics.BlendMode
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.ColorFilter
import android.graphics.PixelFormat
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.RenderEffect
import android.graphics.RenderNode
import android.graphics.Shader
import android.graphics.drawable.Drawable
import android.graphics.drawable.InsetDrawable
import com.android.systemui.shared.shadow.DoubleShadowTextHelper.ShadowInfo
/** A component to draw an icon with two layers of shadows. */
class DoubleShadowIconDrawable(
keyShadowInfo: ShadowInfo,
ambientShadowInfo: ShadowInfo,
iconDrawable: Drawable,
iconSize: Int,
val iconInsetSize: Int
) : Drawable() {
private val mAmbientShadowInfo: ShadowInfo
private val mCanvasSize: Int
private val mKeyShadowInfo: ShadowInfo
private val mIconDrawable: InsetDrawable
private val mDoubleShadowNode: RenderNode
init {
mCanvasSize = iconSize + iconInsetSize * 2
mKeyShadowInfo = keyShadowInfo
mAmbientShadowInfo = ambientShadowInfo
setBounds(0, 0, mCanvasSize, mCanvasSize)
mIconDrawable = InsetDrawable(iconDrawable, iconInsetSize)
mIconDrawable.setBounds(0, 0, mCanvasSize, mCanvasSize)
mDoubleShadowNode = createShadowRenderNode()
}
private fun createShadowRenderNode(): RenderNode {
val renderNode = RenderNode("DoubleShadowNode")
renderNode.setPosition(0, 0, mCanvasSize, mCanvasSize)
// Create render effects
val ambientShadow =
createShadowRenderEffect(
mAmbientShadowInfo.blur,
mAmbientShadowInfo.offsetX,
mAmbientShadowInfo.offsetY,
mAmbientShadowInfo.alpha
)
val keyShadow =
createShadowRenderEffect(
mKeyShadowInfo.blur,
mKeyShadowInfo.offsetX,
mKeyShadowInfo.offsetY,
mKeyShadowInfo.alpha
)
val blend = RenderEffect.createBlendModeEffect(ambientShadow, keyShadow, BlendMode.DARKEN)
renderNode.setRenderEffect(blend)
return renderNode
}
private fun createShadowRenderEffect(
radius: Float,
offsetX: Float,
offsetY: Float,
alpha: Float
): RenderEffect {
return RenderEffect.createColorFilterEffect(
PorterDuffColorFilter(Color.argb(alpha, 0f, 0f, 0f), PorterDuff.Mode.MULTIPLY),
RenderEffect.createOffsetEffect(
offsetX,
offsetY,
RenderEffect.createBlurEffect(radius, radius, Shader.TileMode.CLAMP)
)
)
}
override fun draw(canvas: Canvas) {
if (canvas.isHardwareAccelerated) {
if (!mDoubleShadowNode.hasDisplayList()) {
// Record render node if its display list is not recorded or discarded
// (which happens when it's no longer drawn by anything).
val recordingCanvas = mDoubleShadowNode.beginRecording()
mIconDrawable.draw(recordingCanvas)
mDoubleShadowNode.endRecording()
}
canvas.drawRenderNode(mDoubleShadowNode)
}
mIconDrawable.draw(canvas)
}
override fun getOpacity(): Int {
return PixelFormat.TRANSPARENT
}
override fun setAlpha(alpha: Int) {
mIconDrawable.alpha = alpha
}
override fun setColorFilter(colorFilter: ColorFilter?) {
mIconDrawable.colorFilter = colorFilter
}
override fun setTint(color: Int) {
mIconDrawable.setTint(color)
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2022 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 com.android.systemui.shared.shadow
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.widget.TextClock
import com.android.systemui.shared.R
import com.android.systemui.shared.shadow.DoubleShadowTextHelper.ShadowInfo
import com.android.systemui.shared.shadow.DoubleShadowTextHelper.applyShadows
/** Extension of [TextClock] which draws two shadows on the text (ambient and key shadows) */
class DoubleShadowTextClock
@JvmOverloads
constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : TextClock(context, attrs, defStyleAttr, defStyleRes) {
private val mAmbientShadowInfo: ShadowInfo
private val mKeyShadowInfo: ShadowInfo
init {
val attributes =
context.obtainStyledAttributes(
attrs,
R.styleable.DoubleShadowTextClock,
defStyleAttr,
defStyleRes
)
try {
val keyShadowBlur =
attributes.getDimensionPixelSize(R.styleable.DoubleShadowTextClock_keyShadowBlur, 0)
val keyShadowOffsetX =
attributes.getDimensionPixelSize(
R.styleable.DoubleShadowTextClock_keyShadowOffsetX,
0
)
val keyShadowOffsetY =
attributes.getDimensionPixelSize(
R.styleable.DoubleShadowTextClock_keyShadowOffsetY,
0
)
val keyShadowAlpha =
attributes.getFloat(R.styleable.DoubleShadowTextClock_keyShadowAlpha, 0f)
mKeyShadowInfo =
ShadowInfo(
keyShadowBlur.toFloat(),
keyShadowOffsetX.toFloat(),
keyShadowOffsetY.toFloat(),
keyShadowAlpha
)
val ambientShadowBlur =
attributes.getDimensionPixelSize(
R.styleable.DoubleShadowTextClock_ambientShadowBlur,
0
)
val ambientShadowOffsetX =
attributes.getDimensionPixelSize(
R.styleable.DoubleShadowTextClock_ambientShadowOffsetX,
0
)
val ambientShadowOffsetY =
attributes.getDimensionPixelSize(
R.styleable.DoubleShadowTextClock_ambientShadowOffsetY,
0
)
val ambientShadowAlpha =
attributes.getFloat(R.styleable.DoubleShadowTextClock_ambientShadowAlpha, 0f)
mAmbientShadowInfo =
ShadowInfo(
ambientShadowBlur.toFloat(),
ambientShadowOffsetX.toFloat(),
ambientShadowOffsetY.toFloat(),
ambientShadowAlpha
)
} finally {
attributes.recycle()
}
}
public override fun onDraw(canvas: Canvas) {
applyShadows(mKeyShadowInfo, mAmbientShadowInfo, this, canvas) { super.onDraw(canvas) }
}
}

View File

@@ -14,31 +14,33 @@
* limitations under the License.
*/
package com.android.systemui.dreams.complication
package com.android.systemui.shared.shadow
import android.graphics.Canvas
import android.graphics.Color
import android.widget.TextView
import androidx.annotation.ColorInt
class DoubleShadowTextHelper
constructor(
private val keyShadowInfo: ShadowInfo,
private val ambientShadowInfo: ShadowInfo,
) {
object DoubleShadowTextHelper {
data class ShadowInfo(
val blur: Float,
val offsetX: Float = 0f,
val offsetY: Float = 0f,
@ColorInt val color: Int
val alpha: Float
)
fun applyShadows(view: TextView, canvas: Canvas, onDrawCallback: () -> Unit) {
fun applyShadows(
keyShadowInfo: ShadowInfo,
ambientShadowInfo: ShadowInfo,
view: TextView,
canvas: Canvas,
onDrawCallback: () -> Unit
) {
// We enhance the shadow by drawing the shadow twice
view.paint.setShadowLayer(
ambientShadowInfo.blur,
ambientShadowInfo.offsetX,
ambientShadowInfo.offsetY,
ambientShadowInfo.color
Color.argb(ambientShadowInfo.alpha, 0f, 0f, 0f)
)
onDrawCallback()
canvas.save()
@@ -53,7 +55,7 @@ constructor(
keyShadowInfo.blur,
keyShadowInfo.offsetX,
keyShadowInfo.offsetY,
keyShadowInfo.color
Color.argb(keyShadowInfo.alpha, 0f, 0f, 0f)
)
onDrawCallback()
canvas.restore()

View File

@@ -0,0 +1,127 @@
/*
* Copyright (C) 2022 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 com.android.systemui.shared.shadow
import android.content.Context
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.widget.TextView
import com.android.systemui.shared.R
import com.android.systemui.shared.shadow.DoubleShadowTextHelper.ShadowInfo
import com.android.systemui.shared.shadow.DoubleShadowTextHelper.applyShadows
/** Extension of [TextView] which draws two shadows on the text (ambient and key shadows} */
class DoubleShadowTextView
@JvmOverloads
constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : TextView(context, attrs, defStyleAttr, defStyleRes) {
private val mKeyShadowInfo: ShadowInfo
private val mAmbientShadowInfo: ShadowInfo
init {
val attributes =
context.obtainStyledAttributes(
attrs,
R.styleable.DoubleShadowTextView,
defStyleAttr,
defStyleRes
)
val drawableSize: Int
val drawableInsetSize: Int
try {
val keyShadowBlur =
attributes.getDimensionPixelSize(R.styleable.DoubleShadowTextView_keyShadowBlur, 0)
val keyShadowOffsetX =
attributes.getDimensionPixelSize(
R.styleable.DoubleShadowTextView_keyShadowOffsetX,
0
)
val keyShadowOffsetY =
attributes.getDimensionPixelSize(
R.styleable.DoubleShadowTextView_keyShadowOffsetY,
0
)
val keyShadowAlpha =
attributes.getFloat(R.styleable.DoubleShadowTextView_keyShadowAlpha, 0f)
mKeyShadowInfo =
ShadowInfo(
keyShadowBlur.toFloat(),
keyShadowOffsetX.toFloat(),
keyShadowOffsetY.toFloat(),
keyShadowAlpha
)
val ambientShadowBlur =
attributes.getDimensionPixelSize(
R.styleable.DoubleShadowTextView_ambientShadowBlur,
0
)
val ambientShadowOffsetX =
attributes.getDimensionPixelSize(
R.styleable.DoubleShadowTextView_ambientShadowOffsetX,
0
)
val ambientShadowOffsetY =
attributes.getDimensionPixelSize(
R.styleable.DoubleShadowTextView_ambientShadowOffsetY,
0
)
val ambientShadowAlpha =
attributes.getFloat(R.styleable.DoubleShadowTextView_ambientShadowAlpha, 0f)
mAmbientShadowInfo =
ShadowInfo(
ambientShadowBlur.toFloat(),
ambientShadowOffsetX.toFloat(),
ambientShadowOffsetY.toFloat(),
ambientShadowAlpha
)
drawableSize =
attributes.getDimensionPixelSize(
R.styleable.DoubleShadowTextView_drawableIconSize,
0
)
drawableInsetSize =
attributes.getDimensionPixelSize(
R.styleable.DoubleShadowTextView_drawableIconInsetSize,
0
)
} finally {
attributes.recycle()
}
val drawables = arrayOf<Drawable?>(null, null, null, null)
for ((index, drawable) in compoundDrawablesRelative.withIndex()) {
if (drawable == null) continue
drawables[index] =
DoubleShadowIconDrawable(
mKeyShadowInfo,
mAmbientShadowInfo,
drawable,
drawableSize,
drawableInsetSize
)
}
setCompoundDrawablesRelative(drawables[0], drawables[1], drawables[2], drawables[3])
}
public override fun onDraw(canvas: Canvas) {
applyShadows(mKeyShadowInfo, mAmbientShadowInfo, this, canvas) { super.onDraw(canvas) }
}
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright (C) 2022 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 com.android.systemui.dreams.complication;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.TextClock;
import com.android.systemui.R;
import com.android.systemui.dreams.complication.DoubleShadowTextHelper.ShadowInfo;
import kotlin.Unit;
/**
* Extension of {@link TextClock} which draws two shadows on the text (ambient and key shadows)
*/
public class DoubleShadowTextClock extends TextClock {
private final DoubleShadowTextHelper mShadowHelper;
public DoubleShadowTextClock(Context context) {
this(context, null);
}
public DoubleShadowTextClock(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DoubleShadowTextClock(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
final Resources resources = context.getResources();
final ShadowInfo keyShadowInfo = new ShadowInfo(
resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_radius),
resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_dx),
resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_dy),
resources.getColor(R.color.dream_overlay_clock_key_text_shadow_color));
final ShadowInfo ambientShadowInfo = new ShadowInfo(
resources.getDimensionPixelSize(
R.dimen.dream_overlay_clock_ambient_text_shadow_radius),
resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_ambient_text_shadow_dx),
resources.getDimensionPixelSize(R.dimen.dream_overlay_clock_ambient_text_shadow_dy),
resources.getColor(R.color.dream_overlay_clock_ambient_text_shadow_color));
mShadowHelper = new DoubleShadowTextHelper(keyShadowInfo, ambientShadowInfo);
}
@Override
public void onDraw(Canvas canvas) {
mShadowHelper.applyShadows(this, canvas, () -> {
super.onDraw(canvas);
return Unit.INSTANCE;
});
}
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright (C) 2022 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 com.android.systemui.dreams.complication;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.TextView;
import com.android.systemui.R;
import kotlin.Unit;
/**
* Extension of {@link TextView} which draws two shadows on the text (ambient and key shadows}
*/
public class DoubleShadowTextView extends TextView {
private final DoubleShadowTextHelper mShadowHelper;
public DoubleShadowTextView(Context context) {
this(context, null);
}
public DoubleShadowTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public DoubleShadowTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
final Resources resources = context.getResources();
final DoubleShadowTextHelper.ShadowInfo
keyShadowInfo = new DoubleShadowTextHelper.ShadowInfo(
resources.getDimensionPixelSize(
R.dimen.dream_overlay_status_bar_key_text_shadow_radius),
resources.getDimensionPixelSize(
R.dimen.dream_overlay_status_bar_key_text_shadow_dx),
resources.getDimensionPixelSize(
R.dimen.dream_overlay_status_bar_key_text_shadow_dy),
resources.getColor(R.color.dream_overlay_status_bar_key_text_shadow_color));
final DoubleShadowTextHelper.ShadowInfo
ambientShadowInfo = new DoubleShadowTextHelper.ShadowInfo(
resources.getDimensionPixelSize(
R.dimen.dream_overlay_status_bar_ambient_text_shadow_radius),
resources.getDimensionPixelSize(
R.dimen.dream_overlay_status_bar_ambient_text_shadow_dx),
resources.getDimensionPixelSize(
R.dimen.dream_overlay_status_bar_ambient_text_shadow_dy),
resources.getColor(R.color.dream_overlay_status_bar_ambient_text_shadow_color));
mShadowHelper = new DoubleShadowTextHelper(keyShadowInfo, ambientShadowInfo);
}
@Override
public void onDraw(Canvas canvas) {
mShadowHelper.applyShadows(this, canvas, () -> {
super.onDraw(canvas);
return Unit.INSTANCE;
});
}
}