diff --git a/packages/EasterEgg/Android.mk b/packages/EasterEgg/Android.mk index df081f4091d97..d4c1e7094b99d 100644 --- a/packages/EasterEgg/Android.mk +++ b/packages/EasterEgg/Android.mk @@ -5,6 +5,7 @@ LOCAL_MODULE_TAGS := optional LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-v4 \ android-support-v13 \ + android-support-dynamic-animation \ android-support-v7-recyclerview \ android-support-v7-preference \ android-support-v7-appcompat \ diff --git a/packages/EasterEgg/AndroidManifest.xml b/packages/EasterEgg/AndroidManifest.xml index fbc2386576471..14861c261f0da 100644 --- a/packages/EasterEgg/AndroidManifest.xml +++ b/packages/EasterEgg/AndroidManifest.xml @@ -19,12 +19,25 @@ Copyright (C) 2016 The Android Open Source Project android:versionCode="1" android:versionName="1.0"> - + + + + + + + + + + + + - diff --git a/packages/EasterEgg/res/drawable/icon.xml b/packages/EasterEgg/res/drawable/icon.xml index defa83a9b5c6d..5ce9e51338471 100644 --- a/packages/EasterEgg/res/drawable/icon.xml +++ b/packages/EasterEgg/res/drawable/icon.xml @@ -1,5 +1,5 @@ + android:pathData="M25.0,25.0m-20.5,0.0a20.5,20.5,0,1,1,41.0,0.0a20.5,20.5,0,1,1,-41.0,0.0" + android:fillAlpha="0.066" + android:fillColor="#000000"/> + android:pathData="M24.0,24.0m-20.0,0.0a20.0,20.0,0,1,1,40.0,0.0a20.0,20.0,0,1,1,-40.0,0.0" + android:fillColor="#283593"/> + android:pathData="M44,24.2010101 L33.9004889,14.101499 L14.101499,33.9004889 L24.2010101,44 C29.2525804,43.9497929 34.2887564,41.9975027 38.1431296,38.1431296 C41.9975027,34.2887564 43.9497929,29.2525804 44,24.2010101 Z" + android:fillColor="#1a237e"/> + android:pathData="M24.0,24.0m-14.0,0.0a14.0,14.0,0,1,1,28.0,0.0a14.0,14.0,0,1,1,-28.0,0.0" + android:fillColor="#5c6bc0"/> + android:pathData="M37.7829445,26.469236 L29.6578482,18.3441397 L18.3441397,29.6578482 L26.469236,37.7829445 C29.1911841,37.2979273 31.7972024,36.0037754 33.9004889,33.9004889 C36.0037754,31.7972024 37.2979273,29.1911841 37.7829445,26.469236 Z" + android:fillColor="#3f51b5"/> + - diff --git a/packages/EasterEgg/res/drawable/octo_bg.xml b/packages/EasterEgg/res/drawable/octo_bg.xml new file mode 100644 index 0000000000000..1e46cf434a8be --- /dev/null +++ b/packages/EasterEgg/res/drawable/octo_bg.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java b/packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java new file mode 100644 index 0000000000000..bbbdf80612d74 --- /dev/null +++ b/packages/EasterEgg/src/com/android/egg/octo/Ocquarium.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2017 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.egg.octo; + +import android.app.Activity; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import com.android.egg.R; + +public class Ocquarium extends Activity { + ImageView mImageView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final float dp = getResources().getDisplayMetrics().density; + + getWindow().setBackgroundDrawableResource(R.drawable.octo_bg); + + FrameLayout bg = new FrameLayout(this); + setContentView(bg); + bg.setAlpha(0f); + bg.animate().setStartDelay(500).setDuration(5000).alpha(1f).start(); + + mImageView = new ImageView(this); + bg.addView(mImageView, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + + final OctopusDrawable octo = new OctopusDrawable(getApplicationContext()); + octo.setSizePx((int) (OctopusDrawable.randfrange(40f,180f) * dp)); + mImageView.setImageDrawable(octo); + octo.startDrift(); + + mImageView.setOnTouchListener(new View.OnTouchListener() { + boolean touching; + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + switch (motionEvent.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + if (octo.hitTest(motionEvent.getX(), motionEvent.getY())) { + touching = true; + octo.stopDrift(); + } + break; + case MotionEvent.ACTION_MOVE: + if (touching) { + octo.moveTo(motionEvent.getX(), motionEvent.getY()); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + touching = false; + octo.startDrift(); + break; + } + return true; + } + }); + } +} diff --git a/packages/EasterEgg/src/com/android/egg/octo/OctopusDrawable.java b/packages/EasterEgg/src/com/android/egg/octo/OctopusDrawable.java new file mode 100644 index 0000000000000..5dde6e1152688 --- /dev/null +++ b/packages/EasterEgg/src/com/android/egg/octo/OctopusDrawable.java @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2017 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.egg.octo; + +import android.animation.TimeAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.DashPathEffect; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.support.animation.DynamicAnimation; +import android.support.animation.SpringForce; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.animation.SpringAnimation; +import android.support.animation.FloatValueHolder; + +public class OctopusDrawable extends Drawable { + private static float BASE_SCALE = 100f; + public static boolean PATH_DEBUG = false; + + private static int BODY_COLOR = 0xFF101010; + private static int ARM_COLOR = 0xFF101010; + private static int ARM_COLOR_BACK = 0xFF000000; + private static int EYE_COLOR = 0xFF808080; + + private static int[] BACK_ARMS = {1, 3, 4, 6}; + private static int[] FRONT_ARMS = {0, 2, 5, 7}; + + private Paint mPaint = new Paint(); + private Arm[] mArms = new Arm[8]; + final PointF point = new PointF(); + private int mSizePx = 100; + final Matrix M = new Matrix(); + final Matrix M_inv = new Matrix(); + private TimeAnimator mDriftAnimation; + private boolean mBlinking; + private float[] ptmp = new float[2]; + private float[] scaledBounds = new float[2]; + + public static float randfrange(float a, float b) { + return (float) (Math.random()*(b-a) + a); + } + public static float clamp(float v, float a, float b) { + return vb?b:v; + } + + public OctopusDrawable(Context context) { + float dp = context.getResources().getDisplayMetrics().density; + setSizePx((int) (100*dp)); + mPaint.setAntiAlias(true); + for (int i=0; i nextjump) { + vy = JUMP_VY; + nextjump = t + (long) randfrange(5000, 10000); + } + if (unblink > 0 && t > unblink) { + setBlinking(false); + unblink = 0; + } else if (Math.random() < 0.001f) { + setBlinking(true); + unblink = t + 200; + } + + ax = (float) (MAX_VX * Math.sin(t_sec*.25f)); + + vx = clamp(vx + dt_sec * ax, -MAX_VX, MAX_VX); + vy = clamp(vy + dt_sec * ay, -100*MAX_VY, MAX_VY); + + // oob check + if (point.y - BASE_SCALE/2 > scaledBounds[1]) { + vy = JUMP_VY; + } else if (point.y + BASE_SCALE < 0) { + vy = MAX_VY; + } + + point.x = clamp(point.x + dt_sec * vx, 0, scaledBounds[0]); + point.y = point.y + dt_sec * vy; + + repositionArms(); + } + }); + } + mDriftAnimation.start(); + } + + public void stopDrift() { + mDriftAnimation.cancel(); + } + + @Override + public void onBoundsChange(Rect bounds) { + final float w = bounds.width(); + final float h = bounds.height(); + + lockArms(true); + moveTo(w/2, h/2); + lockArms(false); + + scaledBounds[0] = w; + scaledBounds[1] = h; + M_inv.mapPoints(scaledBounds); + } + + // real pixel coordinates + public void moveTo(float x, float y) { + point.x = x; + point.y = y; + mapPointF(M_inv, point); + repositionArms(); + } + + public boolean hitTest(float x, float y) { + ptmp[0] = x; + ptmp[1] = y; + M_inv.mapPoints(ptmp); + return Math.hypot(ptmp[0] - point.x, ptmp[1] - point.y) < BASE_SCALE/2; + } + + private void lockArms(boolean l) { + for (Arm arm : mArms) { + arm.setLocked(l); + } + } + private void repositionArms() { + for (int i=0; i=len) { + t=len; + last=true; + } + pm.getPosTan(t, pos, tan); + float r = len > 0 ? lerp(t/len, r1, r2) : r1; + c.drawCircle(pos[0], pos[1], r, pt); + t += Math.max(r*0.25f, sMinStepPx); // walk forward 1/4 radius, not too small though + if (last) break; + } + } +}