/* * Copyright (C) 2013 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; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.Resources; import android.graphics.*; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; import android.view.View; import android.view.animation.AccelerateInterpolator; import android.view.animation.AnticipateOvershootInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; import android.widget.ImageView; import java.util.HashSet; import java.util.Set; public class DessertCaseView extends FrameLayout { private static final String TAG = DessertCaseView.class.getSimpleName(); private static final boolean DEBUG = false; static final int START_DELAY = 5000; static final int DELAY = 2000; static final int DURATION = 500; private static final int TAG_POS = 0x2000001; private static final int TAG_SPAN = 0x2000002; private static final int[] PASTRIES = { R.drawable.dessert_kitkat, // used with permission R.drawable.dessert_android, // thx irina }; private static final int[] RARE_PASTRIES = { R.drawable.dessert_cupcake, // 2009 R.drawable.dessert_donut, // 2009 R.drawable.dessert_eclair, // 2009 R.drawable.dessert_froyo, // 2010 R.drawable.dessert_gingerbread, // 2010 R.drawable.dessert_honeycomb, // 2011 R.drawable.dessert_ics, // 2011 R.drawable.dessert_jellybean, // 2012 }; private static final int[] XRARE_PASTRIES = { R.drawable.dessert_petitfour, // the original and still delicious R.drawable.dessert_donutburger, // remember kids, this was long before cronuts R.drawable.dessert_flan, // sholes final approach // landing gear punted to flan // runway foam glistens // -- mcleron R.drawable.dessert_keylimepie, // from an alternative timeline }; private static final int[] XXRARE_PASTRIES = { R.drawable.dessert_zombiegingerbread, // thx hackbod R.drawable.dessert_dandroid, // thx morrildl R.drawable.dessert_jandycane, // thx nes }; private static final int NUM_PASTRIES = PASTRIES.length + RARE_PASTRIES.length + XRARE_PASTRIES.length + XXRARE_PASTRIES.length; private SparseArray mDrawables = new SparseArray(NUM_PASTRIES); private static final float[] MASK = { 0f, 0f, 0f, 0f, 255f, 0f, 0f, 0f, 0f, 255f, 0f, 0f, 0f, 0f, 255f, 1f, 0f, 0f, 0f, 0f }; private static final float[] ALPHA_MASK = { 0f, 0f, 0f, 0f, 255f, 0f, 0f, 0f, 0f, 255f, 0f, 0f, 0f, 0f, 255f, 0f, 0f, 0f, 1f, 0f }; private static final float[] WHITE_MASK = { 0f, 0f, 0f, 0f, 255f, 0f, 0f, 0f, 0f, 255f, 0f, 0f, 0f, 0f, 255f, -1f, 0f, 0f, 0f, 255f }; public static final float SCALE = 0.25f; // natural display size will be SCALE*mCellSize private static final float PROB_2X = 0.33f; private static final float PROB_3X = 0.1f; private static final float PROB_4X = 0.01f; private boolean mStarted; private int mCellSize; private int mWidth, mHeight; private int mRows, mColumns; private View[] mCells; private final Set mFreeList = new HashSet(); private final Handler mHandler = new Handler(); private final Runnable mJuggle = new Runnable() { @Override public void run() { final int N = getChildCount(); final int K = 1; //irand(1,3); for (int i=0; i T pick(T[] a) { return a[(int)(Math.random()*a.length)]; } T pick(SparseArray sa) { return sa.valueAt((int)(Math.random()*sa.size())); } float[] hsv = new float[] { 0, 1f, .85f }; int random_color() { // return 0xFF000000 | (int) (Math.random() * (float) 0xFFFFFF); // totally random final int COLORS = 12; hsv[0] = irand(0,COLORS) * (360f/COLORS); return Color.HSVToColor(hsv); } @Override protected synchronized void onSizeChanged (int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (mWidth == w && mHeight == h) return; final boolean wasStarted = mStarted; if (wasStarted) { stop(); } mWidth = w; mHeight = h; mCells = null; removeAllViewsInLayout(); mFreeList.clear(); mRows = mHeight / mCellSize; mColumns = mWidth / mCellSize; mCells = new View[mRows * mColumns]; if (DEBUG) Log.v(TAG, String.format("New dimensions: %dx%d", mColumns, mRows)); setScaleX(SCALE); setScaleY(SCALE); setTranslationX(0.5f * (mWidth - mCellSize * mColumns) * SCALE); setTranslationY(0.5f * (mHeight - mCellSize * mRows) * SCALE); for (int j=0; j 0) { final float s = (Integer) v.getTag(TAG_SPAN); v.setScaleX(0.5f * s); v.setScaleY(0.5f * s); v.setAlpha(0f); v.animate().withLayer().scaleX(s).scaleY(s).alpha(1f).setDuration(animationLen); } } } public void place(View v, boolean animate) { place(v, new Point(irand(0, mColumns), irand(0, mRows)), animate); } // we don't have .withLayer() on general Animators private final Animator.AnimatorListener makeHardwareLayerListener(final View v) { return new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animator) { v.setLayerType(View.LAYER_TYPE_HARDWARE, null); v.buildLayer(); } @Override public void onAnimationEnd(Animator animator) { v.setLayerType(View.LAYER_TYPE_NONE, null); } }; } private final HashSet tmpSet = new HashSet(); public synchronized void place(View v, Point pt, boolean animate) { final int i = pt.x; final int j = pt.y; final float rnd = frand(); if (v.getTag(TAG_POS) != null) { for (final Point oc : getOccupied(v)) { mFreeList.add(oc); mCells[oc.y*mColumns + oc.x] = null; } } int scale = 1; if (rnd < PROB_4X) { if (!(i >= mColumns-3 || j >= mRows-3)) { scale = 4; } } else if (rnd < PROB_3X) { if (!(i >= mColumns-2 || j >= mRows-2)) { scale = 3; } } else if (rnd < PROB_2X) { if (!(i == mColumns-1 || j == mRows-1)) { scale = 2; } } v.setTag(TAG_POS, pt); v.setTag(TAG_SPAN, scale); tmpSet.clear(); final Point[] occupied = getOccupied(v); for (final Point oc : occupied) { final View squatter = mCells[oc.y*mColumns + oc.x]; if (squatter != null) { tmpSet.add(squatter); } } for (final View squatter : tmpSet) { for (final Point sq : getOccupied(squatter)) { mFreeList.add(sq); mCells[sq.y*mColumns + sq.x] = null; } if (squatter != v) { squatter.setTag(TAG_POS, null); if (animate) { squatter.animate().withLayer() .scaleX(0.5f).scaleY(0.5f).alpha(0) .setDuration(DURATION) .setInterpolator(new AccelerateInterpolator()) .setListener(new Animator.AnimatorListener() { public void onAnimationStart(Animator animator) { } public void onAnimationEnd(Animator animator) { removeView(squatter); } public void onAnimationCancel(Animator animator) { } public void onAnimationRepeat(Animator animator) { } }) .start(); } else { removeView(squatter); } } } for (final Point oc : occupied) { mCells[oc.y*mColumns + oc.x] = v; mFreeList.remove(oc); } final float rot = (float)irand(0, 4) * 90f; if (animate) { v.bringToFront(); AnimatorSet set1 = new AnimatorSet(); set1.playTogether( ObjectAnimator.ofFloat(v, View.SCALE_X, (float) scale), ObjectAnimator.ofFloat(v, View.SCALE_Y, (float) scale) ); set1.setInterpolator(new AnticipateOvershootInterpolator()); set1.setDuration(DURATION); AnimatorSet set2 = new AnimatorSet(); set2.playTogether( ObjectAnimator.ofFloat(v, View.ROTATION, rot), ObjectAnimator.ofFloat(v, View.X, i* mCellSize + (scale-1) * mCellSize /2), ObjectAnimator.ofFloat(v, View.Y, j* mCellSize + (scale-1) * mCellSize /2) ); set2.setInterpolator(new DecelerateInterpolator()); set2.setDuration(DURATION); set1.addListener(makeHardwareLayerListener(v)); set1.start(); set2.start(); } else { v.setX(i * mCellSize + (scale-1) * mCellSize /2); v.setY(j * mCellSize + (scale-1) * mCellSize /2); v.setScaleX((float) scale); v.setScaleY((float) scale); v.setRotation(rot); } } private Point[] getOccupied(View v) { final int scale = (Integer) v.getTag(TAG_SPAN); final Point pt = (Point)v.getTag(TAG_POS); if (pt == null || scale == 0) return new Point[0]; final Point[] result = new Point[scale * scale]; int p=0; for (int i=0; i