1 /* 2 * Copyright (C) 2018 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.internal.app; 18 19 import android.animation.TimeAnimator; 20 import android.app.Activity; 21 import android.graphics.Canvas; 22 import android.graphics.Color; 23 import android.graphics.ColorFilter; 24 import android.graphics.Paint; 25 import android.graphics.Path; 26 import android.graphics.drawable.Drawable; 27 import android.os.Bundle; 28 import android.util.Log; 29 import android.view.MotionEvent; 30 import android.view.MotionEvent.PointerCoords; 31 import android.view.View; 32 import android.widget.FrameLayout; 33 34 public class PlatLogoActivity extends Activity { 35 FrameLayout layout; 36 TimeAnimator anim; 37 PBackground bg; 38 39 private class PBackground extends Drawable { 40 private float maxRadius, radius, x, y, dp; 41 private int[] palette; 42 private int darkest; 43 private float offset; 44 45 public PBackground() { 46 randomizePalette(); 47 } 48 49 /** 50 * set inner radius of "p" logo 51 */ 52 public void setRadius(float r) { 53 this.radius = Math.max(48*dp, r); 54 } 55 56 /** 57 * move the "p" 58 */ 59 public void setPosition(float x, float y) { 60 this.x = x; 61 this.y = y; 62 } 63 64 /** 65 * for animating the "p" 66 */ 67 public void setOffset(float o) { 68 this.offset = o; 69 } 70 71 /** 72 * rough luminance calculation 73 * https://www.w3.org/TR/AERT/#color-contrast 74 */ 75 public float lum(int rgb) { 76 return ((Color.red(rgb) * 299f) + (Color.green(rgb) * 587f) + (Color.blue(rgb) * 114f)) / 1000f; 77 } 78 79 /** 80 * create a random evenly-spaced color palette 81 * guaranteed to contrast! 82 */ 83 public void randomizePalette() { 84 final int slots = 2 + (int)(Math.random() * 2); 85 float[] color = new float[] { (float) Math.random() * 360f, 1f, 1f }; 86 palette = new int[slots]; 87 darkest = 0; 88 for (int i=0; i<slots; i++) { 89 palette[i] = Color.HSVToColor(color); 90 color[0] += 360f/slots; 91 if (lum(palette[i]) < lum(palette[darkest])) darkest = i; 92 } 93 94 final StringBuilder str = new StringBuilder(); 95 for (int c : palette) { 96 str.append(String.format("#%08x ", c)); 97 } 98 Log.v("PlatLogoActivity", "color palette: " + str); 99 } 100 101 @Override 102 public void draw(Canvas canvas) { 103 if (dp == 0) dp = getResources().getDisplayMetrics().density; 104 final float width = canvas.getWidth(); 105 final float height = canvas.getHeight(); 106 if (radius == 0) { 107 setPosition(width / 2, height / 2); 108 setRadius(width / 6); 109 } 110 final float inner_w = radius * 0.667f; 111 112 final Paint paint = new Paint(); 113 paint.setStrokeCap(Paint.Cap.BUTT); 114 canvas.translate(x, y); 115 116 Path p = new Path(); 117 p.moveTo(-radius, height); 118 p.lineTo(-radius, 0); 119 p.arcTo(-radius, -radius, radius, radius, -180, 270, false); 120 p.lineTo(-radius, radius); 121 122 float w = Math.max(canvas.getWidth(), canvas.getHeight()) * 1.414f; 123 paint.setStyle(Paint.Style.FILL); 124 125 int i=0; 126 while (w > radius*2 + inner_w*2) { 127 paint.setColor(0xFF000000 | palette[i % palette.length]); 128 // for a slower but more complete version: 129 // paint.setStrokeWidth(w); 130 // canvas.drawPath(p, paint); 131 canvas.drawOval(-w/2, -w/2, w/2, w/2, paint); 132 w -= inner_w * (1.1f + Math.sin((i/20f + offset) * 3.14159f)); 133 i++; 134 } 135 136 // the innermost circle needs to be a constant color to avoid rapid flashing 137 paint.setColor(0xFF000000 | palette[(darkest+1) % palette.length]); 138 canvas.drawOval(-radius, -radius, radius, radius, paint); 139 140 p.reset(); 141 p.moveTo(-radius, height); 142 p.lineTo(-radius, 0); 143 p.arcTo(-radius, -radius, radius, radius, -180, 270, false); 144 p.lineTo(-radius + inner_w, radius); 145 146 paint.setStyle(Paint.Style.STROKE); 147 paint.setStrokeWidth(inner_w*2); 148 paint.setColor(palette[darkest]); 149 canvas.drawPath(p, paint); 150 paint.setStrokeWidth(inner_w); 151 paint.setColor(0xFFFFFFFF); 152 canvas.drawPath(p, paint); 153 } 154 155 @Override 156 public void setAlpha(int alpha) { 157 158 } 159 160 @Override 161 public void setColorFilter(ColorFilter colorFilter) { 162 163 } 164 165 @Override 166 public int getOpacity() { 167 return 0; 168 } 169 } 170 171 @Override 172 protected void onCreate(Bundle savedInstanceState) { 173 super.onCreate(savedInstanceState); 174 175 layout = new FrameLayout(this); 176 setContentView(layout); 177 178 bg = new PBackground(); 179 layout.setBackground(bg); 180 181 layout.setOnTouchListener(new View.OnTouchListener() { 182 final PointerCoords pc0 = new PointerCoords(); 183 final PointerCoords pc1 = new PointerCoords(); 184 185 @Override 186 public boolean onTouch(View v, MotionEvent event) { 187 switch (event.getActionMasked()) { 188 case MotionEvent.ACTION_DOWN: 189 case MotionEvent.ACTION_MOVE: 190 if (event.getPointerCount() > 1) { 191 event.getPointerCoords(0, pc0); 192 event.getPointerCoords(1, pc1); 193 bg.setRadius((float) Math.hypot(pc0.x - pc1.x, pc0.y - pc1.y) / 2f); 194 } 195 break; 196 } 197 return true; 198 } 199 }); 200 } 201 202 @Override 203 public void onStart() { 204 super.onStart(); 205 206 bg.randomizePalette(); 207 208 anim = new TimeAnimator(); 209 anim.setTimeListener( 210 new TimeAnimator.TimeListener() { 211 @Override 212 public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { 213 bg.setOffset((float) totalTime / 60000f); 214 bg.invalidateSelf(); 215 } 216 }); 217 218 anim.start(); 219 } 220 221 @Override 222 public void onStop() { 223 if (anim != null) { 224 anim.cancel(); 225 anim = null; 226 } 227 super.onStop(); 228 } 229 } 230