1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.test.hwui; 18 19 import android.app.Activity; 20 import android.content.Context; 21 import android.graphics.Canvas; 22 import android.graphics.CanvasProperty; 23 import android.graphics.Color; 24 import android.graphics.Paint; 25 import android.graphics.RecordingCanvas; 26 import android.graphics.RuntimeShader; 27 import android.os.Bundle; 28 import android.os.Trace; 29 import android.view.RenderNodeAnimator; 30 import android.view.View; 31 import android.widget.LinearLayout; 32 import android.widget.LinearLayout.LayoutParams; 33 34 import java.util.ArrayList; 35 36 public class RippleActivity extends Activity { 37 @Override onCreate(Bundle savedInstanceState)38 protected void onCreate(Bundle savedInstanceState) { 39 super.onCreate(savedInstanceState); 40 41 final LinearLayout layout = new LinearLayout(this); 42 layout.setOrientation(LinearLayout.VERTICAL); 43 layout.addView(new RippleView(this), 44 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); 45 46 setContentView(layout); 47 } 48 49 static class RippleView extends View { 50 static final int DURATION = 1000; 51 static final int MAX_RADIUS = 250; 52 private final int mColor = Color.RED; 53 54 private boolean mToggle = false; 55 ArrayList<RenderNodeAnimator> mRunningAnimations = new ArrayList<RenderNodeAnimator>(); 56 57 CanvasProperty<Float> mX; 58 CanvasProperty<Float> mY; 59 CanvasProperty<Float> mRadius; 60 CanvasProperty<Float> mProgress; 61 CanvasProperty<Float> mNoisePhase; 62 CanvasProperty<Paint> mPaint; 63 RuntimeShader mRuntimeShader; 64 65 static final String sSkSL = "" 66 + "uniform float2 in_origin;" 67 + "uniform float in_progress;\n" 68 + "uniform float in_maxRadius;\n" 69 + "uniform shader in_paintColor;\n" 70 + "float dist2(float2 p0, float2 pf) { return sqrt((pf.x - p0.x) * (pf.x - p0.x) + " 71 + "(pf.y - p0.y) * (pf.y - p0.y)); }\n" 72 + "float mod2(float a, float b) { return a - (b * floor(a / b)); }\n" 73 + "float rand(float2 src) { return fract(sin(dot(src.xy, float2(12.9898, 78.233)))" 74 + " * 43758.5453123); }\n" 75 + "float4 main(float2 p)\n" 76 + "{\n" 77 + " float fraction = in_progress;\n" 78 + " float2 fragCoord = p;//sk_FragCoord.xy;\n" 79 + " float maxDist = in_maxRadius;\n" 80 + " float fragDist = dist2(in_origin, fragCoord.xy);\n" 81 + " float circleRadius = maxDist * fraction;\n" 82 + " float colorVal = (fragDist - circleRadius) / maxDist;\n" 83 + " float d = fragDist < circleRadius \n" 84 + " ? 1. - abs(colorVal * 2. * smoothstep(0., 1., fraction)) \n" 85 + " : 1. - abs(colorVal * 3.);\n" 86 + " d = smoothstep(0., 1., d);\n" 87 + " float divider = 2.;\n" 88 + " float x = floor(fragCoord.x / divider);\n" 89 + " float y = floor(fragCoord.y / divider);\n" 90 + " float density = .95;\n" 91 + " d = rand(float2(x, y)) > density ? d : d * .2;\n" 92 + " d = d * rand(float2(fraction, x * y));\n" 93 + " float alpha = 1. - pow(fraction, 3.);\n" 94 + " return float4(sample(in_paintColor, p).rgb, d * alpha);\n" 95 + "}"; 96 RippleView(Context c)97 RippleView(Context c) { 98 super(c); 99 setClickable(true); 100 101 mX = CanvasProperty.createFloat(200.0f); 102 mY = CanvasProperty.createFloat(200.0f); 103 mRadius = CanvasProperty.createFloat(150.0f); 104 mProgress = CanvasProperty.createFloat(0.0f); 105 mNoisePhase = CanvasProperty.createFloat(0.0f); 106 107 Paint p = new Paint(); 108 p.setAntiAlias(true); 109 p.setColor(mColor); 110 mPaint = CanvasProperty.createPaint(p); 111 112 mRuntimeShader = new RuntimeShader(sSkSL, false); 113 mRuntimeShader.setUniform("in_maxRadius", MAX_RADIUS); 114 } 115 116 @Override onDraw(Canvas canvas)117 protected void onDraw(Canvas canvas) { 118 super.onDraw(canvas); 119 120 if (canvas.isHardwareAccelerated()) { 121 RecordingCanvas recordingCanvas = (RecordingCanvas) canvas; 122 recordingCanvas.drawRipple(mX, mY, mRadius, mPaint, mProgress, mNoisePhase, 123 mColor, mRuntimeShader); 124 } 125 } 126 127 @Override performClick()128 public boolean performClick() { 129 for (int i = 0; i < mRunningAnimations.size(); i++) { 130 mRunningAnimations.get(i).cancel(); 131 } 132 mRunningAnimations.clear(); 133 134 mToggle = !mToggle; 135 136 mRunningAnimations.add(new RenderNodeAnimator( 137 mX, mToggle ? 400.0f : 200.0f)); 138 139 mRunningAnimations.add(new RenderNodeAnimator( 140 mY, mToggle ? 600.0f : 200.0f)); 141 142 mRunningAnimations.add(new RenderNodeAnimator( 143 mRadius, mToggle ? MAX_RADIUS : 150.0f)); 144 145 mRunningAnimations.add(new RenderNodeAnimator( 146 mProgress, mToggle ? 1.0f : 0.0f)); 147 148 mRunningAnimations.add(new RenderNodeAnimator( 149 mNoisePhase, DURATION)); 150 151 mRunningAnimations.add(new RenderNodeAnimator( 152 mPaint, RenderNodeAnimator.PAINT_ALPHA, 64.0f)); 153 154 // Will be "chained" to run after the above 155 mRunningAnimations.add(new RenderNodeAnimator( 156 mPaint, RenderNodeAnimator.PAINT_ALPHA, 255.0f)); 157 158 for (int i = 0; i < mRunningAnimations.size(); i++) { 159 RenderNodeAnimator anim = mRunningAnimations.get(i); 160 anim.setDuration(DURATION); 161 anim.setTarget(this); 162 if (i == (mRunningAnimations.size() - 1)) { 163 // "chain" test 164 anim.setStartValue(64.0f); 165 anim.setStartDelay(anim.getDuration()); 166 } 167 anim.start(); 168 } 169 170 if (mToggle) { 171 post(new Runnable() { 172 @Override 173 public void run() { 174 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "pretendBusy"); 175 try { 176 Thread.sleep(DURATION); 177 } catch (InterruptedException e) { 178 } 179 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 180 } 181 }); 182 } 183 return true; 184 } 185 } 186 } 187