Home | History | Annotate | Download | only in app
      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