1 /*); 2 * Copyright (C) 2012 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.systemui; 18 19 import android.animation.AnimatorSet; 20 import android.animation.PropertyValuesHolder; 21 import android.animation.ObjectAnimator; 22 import android.animation.TimeAnimator; 23 import android.app.Activity; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.graphics.drawable.AnimationDrawable; 28 import android.graphics.drawable.BitmapDrawable; 29 import android.graphics.Bitmap; 30 import android.graphics.Canvas; 31 import android.graphics.Color; 32 import android.graphics.ColorMatrix; 33 import android.graphics.ColorMatrixColorFilter; 34 import android.graphics.Matrix; 35 import android.graphics.Paint; 36 import android.graphics.Point; 37 import android.graphics.PorterDuffColorFilter; 38 import android.graphics.PorterDuffXfermode; 39 import android.graphics.Rect; 40 import android.graphics.RectF; 41 import android.os.Handler; 42 import android.os.SystemClock; 43 import android.util.AttributeSet; 44 import android.util.DisplayMetrics; 45 import android.util.Pair; 46 import android.view.Gravity; 47 import android.view.MotionEvent; 48 import android.view.View; 49 import android.view.ViewGroup; 50 import android.view.WindowManager; 51 import android.view.animation.AnimationUtils; 52 import android.widget.FrameLayout; 53 import android.widget.ImageView; 54 import java.util.HashMap; 55 import java.util.Random; 56 57 public class BeanBag extends Activity { 58 final static boolean DEBUG = false; 59 60 public static class Board extends FrameLayout 61 { 62 static Random sRNG = new Random(); 63 64 static float lerp(float a, float b, float f) { 65 return (b-a)*f + a; 66 } 67 68 static float randfrange(float a, float b) { 69 return lerp(a, b, sRNG.nextFloat()); 70 } 71 72 static int randsign() { 73 return sRNG.nextBoolean() ? 1 : -1; 74 } 75 76 static boolean flip() { 77 return sRNG.nextBoolean(); 78 } 79 80 static float mag(float x, float y) { 81 return (float) Math.sqrt(x*x+y*y); 82 } 83 84 static float clamp(float x, float a, float b) { 85 return ((x<a)?a:((x>b)?b:x)); 86 } 87 88 static float dot(float x1, float y1, float x2, float y2) { 89 return x1*x2+y1+y2; 90 } 91 92 static <E> E pick(E[] array) { 93 if (array.length == 0) return null; 94 return array[sRNG.nextInt(array.length)]; 95 } 96 97 static int pickInt(int[] array) { 98 if (array.length == 0) return 0; 99 return array[sRNG.nextInt(array.length)]; 100 } 101 102 static int NUM_BEANS = 40; 103 static float MIN_SCALE = 0.2f; 104 static float MAX_SCALE = 1f; 105 106 static float LUCKY = 0.001f; 107 108 static int MAX_RADIUS = (int)(576 * MAX_SCALE); 109 110 static int BEANS[] = { 111 R.drawable.redbean0, 112 R.drawable.redbean0, 113 R.drawable.redbean0, 114 R.drawable.redbean0, 115 R.drawable.redbean1, 116 R.drawable.redbean1, 117 R.drawable.redbean2, 118 R.drawable.redbean2, 119 R.drawable.redbeandroid, 120 }; 121 122 static int COLORS[] = { 123 0xFF00CC00, 124 0xFFCC0000, 125 0xFF0000CC, 126 0xFFFFFF00, 127 0xFFFF8000, 128 0xFF00CCFF, 129 0xFFFF0080, 130 0xFF8000FF, 131 0xFFFF8080, 132 0xFF8080FF, 133 0xFFB0C0D0, 134 0xFFDDDDDD, 135 0xFF333333, 136 }; 137 138 public class Bean extends ImageView { 139 public static final float VMAX = 1000.0f; 140 public static final float VMIN = 100.0f; 141 142 public float x, y, a; 143 144 public float va; 145 public float vx, vy; 146 147 public float r; 148 149 public float z; 150 151 public int h,w; 152 153 public boolean grabbed; 154 public float grabx, graby; 155 public long grabtime; 156 private float grabx_offset, graby_offset; 157 158 public Bean(Context context, AttributeSet as) { 159 super(context, as); 160 } 161 162 public String toString() { 163 return String.format("<bean (%.1f, %.1f) (%d x %d)>", 164 getX(), getY(), getWidth(), getHeight()); 165 } 166 167 private void pickBean() { 168 int beanId = pickInt(BEANS); 169 if (randfrange(0,1) <= LUCKY) { 170 beanId = R.drawable.jandycane; 171 } 172 BitmapDrawable bean = (BitmapDrawable) getContext().getResources().getDrawable(beanId); 173 Bitmap beanBits = bean.getBitmap(); 174 h=beanBits.getHeight(); 175 w=beanBits.getWidth(); 176 177 if (DEBUG) { 178 bean.setAlpha(0x80); 179 } 180 this.setImageDrawable(bean); 181 182 Paint pt = new Paint(); 183 final int color = pickInt(COLORS); 184 ColorMatrix CM = new ColorMatrix(); 185 float[] M = CM.getArray(); 186 // we assume the color information is in the red channel 187 /* R */ M[0] = (float)((color & 0x00FF0000) >> 16) / 0xFF; 188 /* G */ M[5] = (float)((color & 0x0000FF00) >> 8) / 0xFF; 189 /* B */ M[10] = (float)((color & 0x000000FF)) / 0xFF; 190 pt.setColorFilter(new ColorMatrixColorFilter(M)); 191 setLayerType(View.LAYER_TYPE_HARDWARE, (beanId == R.drawable.jandycane) ? null : pt); 192 } 193 194 public void reset() { 195 pickBean(); 196 197 final float scale = lerp(MIN_SCALE,MAX_SCALE,z); 198 setScaleX(scale); setScaleY(scale); 199 200 r = 0.3f*Math.max(h,w)*scale; 201 202 a=(randfrange(0,360)); 203 va = randfrange(-30,30); 204 205 vx = randfrange(-40,40) * z; 206 vy = randfrange(-40,40) * z; 207 final float boardh = boardHeight; 208 final float boardw = boardWidth; 209 //android.util.Log.d("BeanBag", "reset: w="+w+" h="+h); 210 if (flip()) { 211 x=(vx < 0 ? boardw+2*r : -r*4f); 212 y=(randfrange(0, boardh-3*r)*0.5f + ((vy < 0)?boardh*0.5f:0)); 213 } else { 214 y=(vy < 0 ? boardh+2*r : -r*4f); 215 x=(randfrange(0, boardw-3*r)*0.5f + ((vx < 0)?boardw*0.5f:0)); 216 } 217 } 218 219 public void update(float dt) { 220 if (grabbed) { 221 // final float interval = (SystemClock.uptimeMillis() - grabtime) / 1000f; 222 vx = (vx * 0.75f) + ((grabx - x) / dt) * 0.25f; 223 x = grabx; 224 vy = (vy * 0.75f) + ((graby - y) / dt) * 0.25f;; 225 y = graby; 226 } else { 227 x = (x + vx * dt); 228 y = (y + vy * dt); 229 a = (a + va * dt); 230 } 231 } 232 233 public float overlap(Bean other) { 234 final float dx = (x - other.x); 235 final float dy = (y - other.y); 236 return mag(dx, dy) - r - other.r; 237 } 238 239 @Override 240 public boolean onTouchEvent(MotionEvent e) { 241 switch (e.getAction()) { 242 case MotionEvent.ACTION_DOWN: 243 grabbed = true; 244 grabx_offset = e.getRawX() - x; 245 graby_offset = e.getRawY() - y; 246 va = 0; 247 // fall 248 case MotionEvent.ACTION_MOVE: 249 grabx = e.getRawX() - grabx_offset; 250 graby = e.getRawY() - graby_offset; 251 grabtime = e.getEventTime(); 252 break; 253 case MotionEvent.ACTION_CANCEL: 254 case MotionEvent.ACTION_UP: 255 grabbed = false; 256 float a = randsign() * clamp(mag(vx, vy) * 0.33f, 0, 1080f); 257 va = randfrange(a*0.5f, a); 258 break; 259 } 260 return true; 261 } 262 } 263 264 TimeAnimator mAnim; 265 private int boardWidth; 266 private int boardHeight; 267 268 public Board(Context context, AttributeSet as) { 269 super(context, as); 270 271 setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); 272 273 setWillNotDraw(!DEBUG); 274 } 275 276 private void reset() { 277 // android.util.Log.d("Nyandroid", "board reset"); 278 removeAllViews(); 279 280 final ViewGroup.LayoutParams wrap = new ViewGroup.LayoutParams( 281 ViewGroup.LayoutParams.WRAP_CONTENT, 282 ViewGroup.LayoutParams.WRAP_CONTENT); 283 284 for(int i=0; i<NUM_BEANS; i++) { 285 Bean nv = new Bean(getContext(), null); 286 addView(nv, wrap); 287 nv.z = ((float)i/NUM_BEANS); 288 nv.z *= nv.z; 289 nv.reset(); 290 nv.x = (randfrange(0, boardWidth)); 291 nv.y = (randfrange(0, boardHeight)); 292 } 293 294 if (mAnim != null) { 295 mAnim.cancel(); 296 } 297 mAnim = new TimeAnimator(); 298 mAnim.setTimeListener(new TimeAnimator.TimeListener() { 299 private long lastPrint = 0; 300 public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { 301 if (DEBUG && totalTime - lastPrint > 5000) { 302 lastPrint = totalTime; 303 for (int i=0; i<getChildCount(); i++) { 304 android.util.Log.d("BeanBag", "bean " + i + ": " + getChildAt(i)); 305 } 306 } 307 308 for (int i=0; i<getChildCount(); i++) { 309 View v = getChildAt(i); 310 if (!(v instanceof Bean)) continue; 311 Bean nv = (Bean) v; 312 nv.update(deltaTime / 1000f); 313 314 for (int j=i+1; j<getChildCount(); j++) { 315 View v2 = getChildAt(j); 316 if (!(v2 instanceof Bean)) continue; 317 Bean nv2 = (Bean) v2; 318 final float overlap = nv.overlap(nv2); 319 } 320 321 nv.setRotation(nv.a); 322 nv.setX(nv.x-nv.getPivotX()); 323 nv.setY(nv.y-nv.getPivotY()); 324 325 if ( nv.x < - MAX_RADIUS 326 || nv.x > boardWidth + MAX_RADIUS 327 || nv.y < -MAX_RADIUS 328 || nv.y > boardHeight + MAX_RADIUS) 329 { 330 nv.reset(); 331 } 332 } 333 334 if (DEBUG) invalidate(); 335 } 336 }); 337 } 338 339 @Override 340 protected void onSizeChanged (int w, int h, int oldw, int oldh) { 341 super.onSizeChanged(w,h,oldw,oldh); 342 boardWidth = w; 343 boardHeight = h; 344 // android.util.Log.d("Nyandroid", "resized: " + w + "x" + h); 345 } 346 347 public void startAnimation() { 348 stopAnimation(); 349 if (mAnim == null) { 350 post(new Runnable() { public void run() { 351 reset(); 352 startAnimation(); 353 } }); 354 } else { 355 mAnim.start(); 356 } 357 } 358 359 public void stopAnimation() { 360 if (mAnim != null) mAnim.cancel(); 361 } 362 363 @Override 364 protected void onDetachedFromWindow() { 365 super.onDetachedFromWindow(); 366 stopAnimation(); 367 } 368 369 @Override 370 public boolean isOpaque() { 371 return false; 372 } 373 374 @Override 375 public void onDraw(Canvas c) { 376 if (DEBUG) { 377 //android.util.Log.d("BeanBag", "onDraw"); 378 Paint pt = new Paint(); 379 pt.setAntiAlias(true); 380 pt.setStyle(Paint.Style.STROKE); 381 pt.setColor(0xFFFF0000); 382 pt.setStrokeWidth(4.0f); 383 c.drawRect(0, 0, getWidth(), getHeight(), pt); 384 pt.setColor(0xFFFFCC00); 385 pt.setStrokeWidth(1.0f); 386 for (int i=0; i<getChildCount(); i++) { 387 Bean b = (Bean) getChildAt(i); 388 final float a = (360-b.a)/180f*3.14159f; 389 final float tx = b.getTranslationX(); 390 final float ty = b.getTranslationY(); 391 c.drawCircle(b.x, b.y, b.r, pt); 392 c.drawCircle(tx, ty, 4, pt); 393 c.drawLine(b.x, b.y, (float)(b.x+b.r*Math.sin(a)), (float)(b.y+b.r*Math.cos(a)), pt); 394 } 395 } 396 } 397 } 398 399 private Board mBoard; 400 401 @Override 402 public void onStart() { 403 super.onStart(); 404 405 getWindow().addFlags( 406 WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON 407 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED 408 ); 409 mBoard = new Board(this, null); 410 setContentView(mBoard); 411 } 412 413 @Override 414 public void onPause() { 415 super.onPause(); 416 mBoard.stopAnimation(); 417 } 418 419 @Override 420 public void onResume() { 421 super.onResume(); 422 mBoard.startAnimation(); 423 } 424 } 425