Home | History | Annotate | Download | only in launcher2
      1 /*);
      2  * Copyright (C) 2011 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 // TODO:
     18 // background stellar matter:
     19 //  - add some slow horizontal parallax motion, or perhaps veeeeery gradual outward drift
     20 
     21 package com.android.launcher2;
     22 
     23 import android.animation.AnimatorSet;
     24 import android.animation.ObjectAnimator;
     25 import android.animation.TimeAnimator;
     26 import android.content.ComponentName;
     27 import android.content.Context;
     28 import android.content.Intent;
     29 import android.graphics.Bitmap;
     30 import android.graphics.Point;
     31 import android.graphics.Rect;
     32 import android.os.Handler;
     33 import android.support.v13.dreams.BasicDream;
     34 import android.util.AttributeSet;
     35 import android.util.DisplayMetrics;
     36 import android.view.MotionEvent;
     37 import android.view.View;
     38 import android.view.ViewGroup;
     39 import android.widget.FrameLayout;
     40 import android.widget.ImageView;
     41 
     42 import com.android.launcher.R;
     43 
     44 import java.util.HashMap;
     45 import java.util.Random;
     46 
     47 public class RocketLauncher extends BasicDream {
     48     public static final boolean ROCKET_LAUNCHER = true;
     49 
     50     public static class Board extends FrameLayout
     51     {
     52         public static final boolean FIXED_STARS = true;
     53         public static final boolean FLYING_STARS = true;
     54         public static final int NUM_ICONS = 20;
     55 
     56         public static final float MANEUVERING_THRUST_SCALE = 0.1f; // tenth speed
     57         private boolean mManeuveringThrusters = false;
     58         private float mSpeedScale = 1.0f;
     59 
     60         public static final int LAUNCH_ZOOM_TIME = 400; // ms
     61 
     62         HashMap<ComponentName, Bitmap> mIcons;
     63         ComponentName[] mComponentNames;
     64 
     65         static Random sRNG = new Random();
     66 
     67         static float lerp(float a, float b, float f) {
     68             return (b-a)*f + a;
     69         }
     70 
     71         static float randfrange(float a, float b) {
     72             return lerp(a, b, sRNG.nextFloat());
     73         }
     74 
     75         static int randsign() {
     76             return sRNG.nextBoolean() ? 1 : -1;
     77         }
     78 
     79         static <E> E pick(E[] array) {
     80             if (array.length == 0) return null;
     81             return array[sRNG.nextInt(array.length)];
     82         }
     83 
     84         public class FlyingIcon extends ImageView {
     85             public static final float VMAX = 1000.0f;
     86             public static final float VMIN = 100.0f;
     87             public static final float ANGULAR_VMAX = 45f;
     88             public static final float ANGULAR_VMIN = 0f;
     89             public static final float SCALE_MIN = 0.5f;
     90             public static final float SCALE_MAX = 4f;
     91 
     92             public float v, vr;
     93 
     94             public final float[] hsv = new float[3];
     95 
     96             public float angle, anglex, angley;
     97             public float fuse;
     98             public float dist;
     99             public float endscale;
    100             public float boardCenterX, boardCenterY;
    101 
    102             public ComponentName component;
    103 
    104             public FlyingIcon(Context context, AttributeSet as) {
    105                 super(context, as);
    106                 setLayerType(View.LAYER_TYPE_HARDWARE, null);
    107 
    108                 setBackgroundResource(R.drawable.flying_icon_bg);
    109                 //android.util.Log.d("RocketLauncher", "ctor: " + this);
    110                 hsv[1] = 1f;
    111                 hsv[2] = 1f;
    112             }
    113 
    114             @Override
    115             public boolean onTouchEvent(MotionEvent event) {
    116                 if (!mManeuveringThrusters || component == null) {
    117                     return false;
    118                 }
    119                 if (getAlpha() < 0.5f) {
    120                     setPressed(false);
    121                     return false;
    122                 }
    123 
    124                 switch (event.getAction()) {
    125                     case MotionEvent.ACTION_DOWN:
    126                         setPressed(true);
    127                         Board.this.resetWarpTimer();
    128                         break;
    129                     case MotionEvent.ACTION_MOVE:
    130                         final Rect hit = new Rect();
    131                         final Point offset = new Point();
    132                         getGlobalVisibleRect(hit, offset);
    133                         final int globx = (int) event.getX() + offset.x;
    134                         final int globy = (int) event.getY() + offset.y;
    135                         setPressed(hit.contains(globx, globy));
    136                         Board.this.resetWarpTimer();
    137                         break;
    138                     case MotionEvent.ACTION_UP:
    139                         if (isPressed()) {
    140                             setPressed(false);
    141                             postDelayed(new Runnable() {
    142                                 public void run() {
    143                                     try {
    144                                         getContext().startActivity(new Intent(Intent.ACTION_MAIN)
    145                                             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    146                                             .setComponent(component));
    147                                     } catch (android.content.ActivityNotFoundException e) {
    148                                     } catch (SecurityException e) {
    149                                     }
    150                                 }
    151                             }, LAUNCH_ZOOM_TIME);
    152                             endscale = 0;
    153                             AnimatorSet s = new AnimatorSet();
    154                             s.playTogether(
    155                                 ObjectAnimator.ofFloat(this, "scaleX", 15f),
    156                                 ObjectAnimator.ofFloat(this, "scaleY", 15f),
    157                                 ObjectAnimator.ofFloat(this, "alpha", 0f)
    158                             );
    159 
    160                             // make sure things are still moving until the very last instant the
    161                             // activity is visible
    162                             s.setDuration((int)(LAUNCH_ZOOM_TIME * 1.25));
    163                             s.setInterpolator(new android.view.animation.AccelerateInterpolator(3));
    164                             s.start();
    165                         }
    166                         break;
    167                 }
    168                 return true;
    169             }
    170 
    171             public String toString() {
    172                 return String.format("<'%s' @ (%.1f, %.1f) v=%.1f a=%.1f dist/fuse=%.1f/%.1f>",
    173                         "icon", getX(), getY(), v, angle, dist, fuse);
    174             }
    175 
    176             public void randomizeIcon() {
    177                 component = pick(mComponentNames);
    178                 setImageBitmap(mIcons.get(component));
    179             }
    180 
    181             public void randomize() {
    182                 v = randfrange(VMIN, VMAX);
    183                 angle = randfrange(0, 360f);
    184                 anglex = (float) Math.sin(angle / 180. * Math.PI);
    185                 angley = (float) Math.cos(angle / 180. * Math.PI);
    186                 vr = randfrange(ANGULAR_VMIN, ANGULAR_VMAX) * randsign();
    187                 endscale = randfrange(SCALE_MIN, SCALE_MAX);
    188 
    189                 randomizeIcon();
    190             }
    191             public void reset() {
    192                 randomize();
    193                 boardCenterX = (Board.this.getWidth() - getWidth()) / 2;
    194                 boardCenterY = (Board.this.getHeight() - getHeight()) / 2;
    195                 setX(boardCenterX);
    196                 setY(boardCenterY);
    197                 fuse = (float) Math.max(boardCenterX, boardCenterY);
    198                 setRotation(180-angle);
    199                 setScaleX(0f);
    200                 setScaleY(0f);
    201                 dist = 0;
    202                 setAlpha(0f);
    203             }
    204             public void update(float dt) {
    205                 dist += v * dt;
    206                 setX(getX() + anglex * v * dt);
    207                 setY(getY() + angley * v * dt);
    208                 //setRotation(getRotation() + vr * dt);
    209                 if (endscale > 0) {
    210                     float scale = lerp(0, endscale, (float) Math.sqrt(dist / fuse));
    211                         setScaleX(scale * lerp(1f, 0.75f, (float) Math.pow((v-VMIN)/(VMAX-VMIN),3)));
    212                         setScaleY(scale * lerp(1f, 1.5f, (float) Math.pow((v-VMIN)/(VMAX-VMIN),3)));
    213                     final float q1 = fuse*0.15f;
    214                     final float q4 = fuse*0.75f;
    215                     if (dist < q1) {
    216                         setAlpha((float) Math.sqrt(dist/q1));
    217                     } else if (dist > q4) {
    218                         setAlpha((dist >= fuse) ? 0f : (1f-(float)Math.pow((dist-q4)/(fuse-q4),2)));
    219                     } else {
    220                         setAlpha(1f);
    221                     }
    222                 }
    223             }
    224         }
    225 
    226         public class FlyingStar extends FlyingIcon {
    227             public FlyingStar(Context context, AttributeSet as) {
    228                 super(context, as);
    229             }
    230             public void randomizeIcon() {
    231                 setImageResource(R.drawable.widget_resize_handle_bottom);
    232             }
    233             public void randomize() {
    234                 super.randomize();
    235                 v = randfrange(VMAX*0.75f, VMAX*2f); // fasticate
    236                 endscale = randfrange(1f, 2f); // ensmallen
    237             }
    238         }
    239 
    240         TimeAnimator mAnim;
    241 
    242         public Board(Context context, AttributeSet as) {
    243             super(context, as);
    244 
    245             setBackgroundColor(0xFF000000);
    246 
    247             LauncherApplication app = (LauncherApplication)context.getApplicationContext();
    248             mIcons = app.getIconCache().getAllIcons();
    249             mComponentNames = new ComponentName[mIcons.size()];
    250             mComponentNames = mIcons.keySet().toArray(mComponentNames);
    251         }
    252 
    253         private void reset() {
    254             removeAllViews();
    255 
    256             final ViewGroup.LayoutParams wrap = new ViewGroup.LayoutParams(
    257                         ViewGroup.LayoutParams.WRAP_CONTENT,
    258                         ViewGroup.LayoutParams.WRAP_CONTENT);
    259 
    260             if (FIXED_STARS) {
    261                 for(int i=0; i<20; i++) {
    262                     ImageView fixedStar = new ImageView(getContext(), null);
    263                     fixedStar.setImageResource(R.drawable.widget_resize_handle_bottom);
    264                     final float s = randfrange(0.25f, 0.75f);
    265                     fixedStar.setScaleX(s);
    266                     fixedStar.setScaleY(s);
    267                     fixedStar.setAlpha(0.75f);
    268                     addView(fixedStar, wrap);
    269                     fixedStar.setX(randfrange(0, getWidth()));
    270                     fixedStar.setY(randfrange(0, getHeight()));
    271                 }
    272             }
    273 
    274             for(int i=0; i<NUM_ICONS*2; i++) {
    275                 FlyingIcon nv = (FLYING_STARS && (i < NUM_ICONS))
    276                     ? new FlyingStar(getContext(), null)
    277                     : new FlyingIcon(getContext(), null);
    278                 addView(nv, wrap);
    279                 nv.reset();
    280             }
    281 
    282             mAnim = new TimeAnimator();
    283             mAnim.setTimeListener(new TimeAnimator.TimeListener() {
    284                 public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
    285                     // setRotation(totalTime * 0.01f); // not as cool as you would think
    286 
    287                     final int START_ZOOM_TIME = 3000;
    288                     if (totalTime < START_ZOOM_TIME) {
    289                         final float x = totalTime/(float)START_ZOOM_TIME;
    290                         final float s = 1f-(float)Math.pow(x-1, 4);
    291                         setScaleX(s); setScaleY(s);
    292                     } else {
    293                         setScaleX(1.0f); setScaleY(1.0f);
    294                     }
    295 
    296                     if (mManeuveringThrusters) {
    297                         if (mSpeedScale > MANEUVERING_THRUST_SCALE) {
    298                             mSpeedScale -= (2*deltaTime/1000f);
    299                         }
    300                         if (mSpeedScale < MANEUVERING_THRUST_SCALE) {
    301                             mSpeedScale = MANEUVERING_THRUST_SCALE;
    302                         }
    303                     } else {
    304                         if (mSpeedScale < 1.0f) {
    305                             mSpeedScale += (deltaTime/1000f);
    306                         }
    307                         if (mSpeedScale > 1.0f) {
    308                             mSpeedScale = 1.0f;
    309                         }
    310                     }
    311 
    312                     for (int i=0; i<getChildCount(); i++) {
    313                         View v = getChildAt(i);
    314                         if (!(v instanceof FlyingIcon)) continue;
    315                         FlyingIcon nv = (FlyingIcon) v;
    316                         nv.update(deltaTime / 1000f * mSpeedScale);
    317                         final float scaledWidth = nv.getWidth() * nv.getScaleX();
    318                         final float scaledHeight = nv.getHeight() * nv.getScaleY();
    319                         if (   nv.getX() + scaledWidth < 0
    320                             || nv.getX() - scaledWidth > getWidth()
    321                             || nv.getY() + scaledHeight < 0
    322                             || nv.getY() - scaledHeight > getHeight())
    323                         {
    324                             nv.reset();
    325                         }
    326                     }
    327                 }
    328             });
    329         }
    330 
    331         @Override
    332         protected void onAttachedToWindow() {
    333             super.onAttachedToWindow();
    334             setLayerType(View.LAYER_TYPE_HARDWARE, null);
    335             setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
    336 
    337             reset();
    338             mAnim.start();
    339         }
    340 
    341         protected void onSizeChanged (int w, int h, int oldw, int oldh) {
    342             super.onSizeChanged(w,h,oldw,oldh);
    343             mAnim.cancel();
    344             reset();
    345             mAnim.start();
    346         }
    347 
    348 
    349         @Override
    350         protected void onDetachedFromWindow() {
    351             super.onDetachedFromWindow();
    352             mAnim.cancel();
    353         }
    354 
    355         @Override
    356         public boolean isOpaque() {
    357             return true;
    358         }
    359 
    360         @Override
    361         public boolean onInterceptTouchEvent(MotionEvent e) {
    362             // we want to eat touch events ourselves if we're in warp speed
    363             return (!(ROCKET_LAUNCHER && mManeuveringThrusters));
    364         }
    365 
    366         final Runnable mEngageWarp = new Runnable() {
    367             @Override
    368             public void run() {
    369                 mManeuveringThrusters = false;
    370             }
    371         };
    372         public void resetWarpTimer() {
    373             final Handler h = getHandler();
    374             h.removeCallbacks(mEngageWarp);
    375             h.postDelayed(mEngageWarp, 5000);
    376         }
    377 
    378         @Override
    379         public boolean onTouchEvent(MotionEvent event) {
    380             if (!ROCKET_LAUNCHER) {
    381                 return true;
    382             }
    383 
    384             if (event.getAction() == MotionEvent.ACTION_DOWN) {
    385                 if (!mManeuveringThrusters) {
    386                     mManeuveringThrusters = true;
    387                     resetWarpTimer();
    388                     return true;
    389                 }
    390             }
    391 
    392             return false;
    393         }
    394     }
    395 
    396     @Override
    397     public void onStart() {
    398         super.onStart();
    399 
    400         DisplayMetrics metrics = new DisplayMetrics();
    401         getWindowManager().getDefaultDisplay().getMetrics(metrics);
    402         final int longside = metrics.widthPixels > metrics.heightPixels
    403             ? metrics.widthPixels : metrics.heightPixels;
    404 
    405         Board b = new Board(this, null);
    406         setContentView(b, new ViewGroup.LayoutParams(longside, longside));
    407         b.setX((metrics.widthPixels - longside) / 2);
    408         b.setY((metrics.heightPixels - longside) / 2);
    409     }
    410 
    411     @Override
    412     public void onUserInteraction() {
    413         if (!ROCKET_LAUNCHER) {
    414             finish();
    415         }
    416     }
    417 }
    418