diff options
Diffstat (limited to 'src/com/android/launcher2/RocketLauncher.java')
-rw-r--r-- | src/com/android/launcher2/RocketLauncher.java | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/src/com/android/launcher2/RocketLauncher.java b/src/com/android/launcher2/RocketLauncher.java new file mode 100644 index 000000000..1c6510fa5 --- /dev/null +++ b/src/com/android/launcher2/RocketLauncher.java @@ -0,0 +1,426 @@ +/*); + * Copyright (C) 2011 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. + */ + +// TODO: +// background stellar matter: +// - add some slow horizontal parallax motion, or perhaps veeeeery gradual outward drift + +package com.android.launcher2; + +import android.animation.AnimatorSet; +import android.animation.PropertyValuesHolder; +import android.animation.ObjectAnimator; +import android.animation.TimeAnimator; +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.support.v13.dreams.BasicDream; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.RectF; +import android.os.Handler; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.Pair; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.ImageView; +import java.util.HashMap; +import java.util.Random; + +import com.android.launcher.R; + +public class RocketLauncher extends BasicDream { + public static final boolean ROCKET_LAUNCHER = true; + + public static class Board extends FrameLayout + { + public static final boolean FIXED_STARS = true; + public static final boolean FLYING_STARS = true; + public static final int NUM_ICONS = 20; + + public static final float MANEUVERING_THRUST_SCALE = 0.1f; // tenth speed + private boolean mManeuveringThrusters = false; + private float mSpeedScale = 1.0f; + + public static final int LAUNCH_ZOOM_TIME = 400; // ms + + HashMap<ComponentName, Bitmap> mIcons; + ComponentName[] mComponentNames; + + static Random sRNG = new Random(); + + static float lerp(float a, float b, float f) { + return (b-a)*f + a; + } + + static float randfrange(float a, float b) { + return lerp(a, b, sRNG.nextFloat()); + } + + static int randsign() { + return sRNG.nextBoolean() ? 1 : -1; + } + + static <E> E pick(E[] array) { + if (array.length == 0) return null; + return array[sRNG.nextInt(array.length)]; + } + + public class FlyingIcon extends ImageView { + public static final float VMAX = 1000.0f; + public static final float VMIN = 100.0f; + public static final float ANGULAR_VMAX = 45f; + public static final float ANGULAR_VMIN = 0f; + public static final float SCALE_MIN = 0.5f; + public static final float SCALE_MAX = 4f; + + public float v, vr; + + public final float[] hsv = new float[3]; + + public float angle, anglex, angley; + public float fuse; + public float dist; + public float endscale; + public float boardCenterX, boardCenterY; + + public ComponentName component; + + public FlyingIcon(Context context, AttributeSet as) { + super(context, as); + setLayerType(View.LAYER_TYPE_HARDWARE, null); + + setBackgroundResource(R.drawable.flying_icon_bg); + //android.util.Log.d("RocketLauncher", "ctor: " + this); + hsv[1] = 1f; + hsv[2] = 1f; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!mManeuveringThrusters || component == null) { + return false; + } + if (getAlpha() < 0.5f) { + setPressed(false); + return false; + } + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + setPressed(true); + Board.this.resetWarpTimer(); + break; + case MotionEvent.ACTION_MOVE: + final Rect hit = new Rect(); + final Point offset = new Point(); + getGlobalVisibleRect(hit, offset); + final int globx = (int) event.getX() + offset.x; + final int globy = (int) event.getY() + offset.y; + setPressed(hit.contains(globx, globy)); + Board.this.resetWarpTimer(); + break; + case MotionEvent.ACTION_UP: + if (isPressed()) { + setPressed(false); + postDelayed(new Runnable() { + public void run() { + try { + getContext().startActivity(new Intent(Intent.ACTION_MAIN) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .setComponent(component)); + } catch (android.content.ActivityNotFoundException e) { + } catch (SecurityException e) { + } + } + }, LAUNCH_ZOOM_TIME); + endscale = 0; + AnimatorSet s = new AnimatorSet(); + s.playTogether( + ObjectAnimator.ofFloat(this, "scaleX", 15f), + ObjectAnimator.ofFloat(this, "scaleY", 15f), + ObjectAnimator.ofFloat(this, "alpha", 0f) + ); + + // make sure things are still moving until the very last instant the + // activity is visible + s.setDuration((int)(LAUNCH_ZOOM_TIME * 1.25)); + s.setInterpolator(new android.view.animation.AccelerateInterpolator(3)); + s.start(); + } + break; + } + return true; + } + + public String toString() { + return String.format("<'%s' @ (%.1f, %.1f) v=%.1f a=%.1f dist/fuse=%.1f/%.1f>", + "icon", getX(), getY(), v, angle, dist, fuse); + } + + public void randomizeIcon() { + component = pick(mComponentNames); + setImageBitmap(mIcons.get(component)); + } + + public void randomize() { + v = randfrange(VMIN, VMAX); + angle = randfrange(0, 360f); + anglex = (float) Math.sin(angle / 180. * Math.PI); + angley = (float) Math.cos(angle / 180. * Math.PI); + vr = randfrange(ANGULAR_VMIN, ANGULAR_VMAX) * randsign(); + endscale = randfrange(SCALE_MIN, SCALE_MAX); + + randomizeIcon(); + } + public void reset() { + randomize(); + boardCenterX = (Board.this.getWidth() - getWidth()) / 2; + boardCenterY = (Board.this.getHeight() - getHeight()) / 2; + setX(boardCenterX); + setY(boardCenterY); + fuse = (float) Math.max(boardCenterX, boardCenterY); + setRotation(180-angle); + setScaleX(0f); + setScaleY(0f); + dist = 0; + setAlpha(0f); + } + public void update(float dt) { + dist += v * dt; + setX(getX() + anglex * v * dt); + setY(getY() + angley * v * dt); + //setRotation(getRotation() + vr * dt); + if (endscale > 0) { + float scale = lerp(0, endscale, (float) Math.sqrt(dist / fuse)); + setScaleX(scale * lerp(1f, 0.75f, (float) Math.pow((v-VMIN)/(VMAX-VMIN),3))); + setScaleY(scale * lerp(1f, 1.5f, (float) Math.pow((v-VMIN)/(VMAX-VMIN),3))); + final float q1 = fuse*0.15f; + final float q4 = fuse*0.75f; + if (dist < q1) { + setAlpha((float) Math.sqrt(dist/q1)); + } else if (dist > q4) { + setAlpha((dist >= fuse) ? 0f : (1f-(float)Math.pow((dist-q4)/(fuse-q4),2))); + } else { + setAlpha(1f); + } + } + } + } + + public class FlyingStar extends FlyingIcon { + public FlyingStar(Context context, AttributeSet as) { + super(context, as); + } + public void randomizeIcon() { + setImageResource(R.drawable.widget_resize_handle_bottom); + } + public void randomize() { + super.randomize(); + v = randfrange(VMAX*0.75f, VMAX*2f); // fasticate + endscale = randfrange(1f, 2f); // ensmallen + } + } + + TimeAnimator mAnim; + + public Board(Context context, AttributeSet as) { + super(context, as); + + setBackgroundColor(0xFF000000); + + LauncherApplication app = (LauncherApplication)context.getApplicationContext(); + mIcons = app.getIconCache().getAllIcons(); + mComponentNames = new ComponentName[mIcons.size()]; + mComponentNames = mIcons.keySet().toArray(mComponentNames); + } + + private void reset() { + removeAllViews(); + + final ViewGroup.LayoutParams wrap = new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + + if (FIXED_STARS) { + for(int i=0; i<20; i++) { + ImageView fixedStar = new ImageView(getContext(), null); + fixedStar.setImageResource(R.drawable.widget_resize_handle_bottom); + final float s = randfrange(0.25f, 0.75f); + fixedStar.setScaleX(s); + fixedStar.setScaleY(s); + fixedStar.setAlpha(0.75f); + addView(fixedStar, wrap); + fixedStar.setX(randfrange(0, getWidth())); + fixedStar.setY(randfrange(0, getHeight())); + } + } + + for(int i=0; i<NUM_ICONS*2; i++) { + FlyingIcon nv = (FLYING_STARS && (i < NUM_ICONS)) + ? new FlyingStar(getContext(), null) + : new FlyingIcon(getContext(), null); + addView(nv, wrap); + nv.reset(); + } + + mAnim = new TimeAnimator(); + mAnim.setTimeListener(new TimeAnimator.TimeListener() { + public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { + // setRotation(totalTime * 0.01f); // not as cool as you would think + + final int START_ZOOM_TIME = 3000; + if (totalTime < START_ZOOM_TIME) { + final float x = totalTime/(float)START_ZOOM_TIME; + final float s = 1f-(float)Math.pow(x-1, 4); + setScaleX(s); setScaleY(s); + } else { + setScaleX(1.0f); setScaleY(1.0f); + } + + if (mManeuveringThrusters) { + if (mSpeedScale > MANEUVERING_THRUST_SCALE) { + mSpeedScale -= (2*deltaTime/1000f); + } + if (mSpeedScale < MANEUVERING_THRUST_SCALE) { + mSpeedScale = MANEUVERING_THRUST_SCALE; + } + } else { + if (mSpeedScale < 1.0f) { + mSpeedScale += (deltaTime/1000f); + } + if (mSpeedScale > 1.0f) { + mSpeedScale = 1.0f; + } + } + + for (int i=0; i<getChildCount(); i++) { + View v = getChildAt(i); + if (!(v instanceof FlyingIcon)) continue; + FlyingIcon nv = (FlyingIcon) v; + nv.update(deltaTime / 1000f * mSpeedScale); + final float scaledWidth = nv.getWidth() * nv.getScaleX(); + final float scaledHeight = nv.getHeight() * nv.getScaleY(); + if ( nv.getX() + scaledWidth < 0 + || nv.getX() - scaledWidth > getWidth() + || nv.getY() + scaledHeight < 0 + || nv.getY() - scaledHeight > getHeight()) + { + nv.reset(); + } + } + } + }); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + setLayerType(View.LAYER_TYPE_HARDWARE, null); + setSystemUiVisibility(View.STATUS_BAR_HIDDEN); + + reset(); + mAnim.start(); + } + + protected void onSizeChanged (int w, int h, int oldw, int oldh) { + super.onSizeChanged(w,h,oldw,oldh); + mAnim.cancel(); + reset(); + mAnim.start(); + } + + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mAnim.cancel(); + } + + @Override + public boolean isOpaque() { + return true; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent e) { + // we want to eat touch events ourselves if we're in warp speed + return (!(ROCKET_LAUNCHER && mManeuveringThrusters)); + } + + final Runnable mEngageWarp = new Runnable() { + @Override + public void run() { + mManeuveringThrusters = false; + } + }; + public void resetWarpTimer() { + final Handler h = getHandler(); + h.removeCallbacks(mEngageWarp); + h.postDelayed(mEngageWarp, 5000); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!ROCKET_LAUNCHER) { + return true; + } + + if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (!mManeuveringThrusters) { + mManeuveringThrusters = true; + resetWarpTimer(); + return true; + } + } + + return false; + } + } + + @Override + public void onStart() { + super.onStart(); + + DisplayMetrics metrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(metrics); + final int longside = metrics.widthPixels > metrics.heightPixels + ? metrics.widthPixels : metrics.heightPixels; + + Board b = new Board(this, null); + setContentView(b, new ViewGroup.LayoutParams(longside, longside)); + b.setX((metrics.widthPixels - longside) / 2); + b.setY((metrics.heightPixels - longside) / 2); + } + + @Override + public void onUserInteraction() { + if (!ROCKET_LAUNCHER) { + finish(); + } + } +} |