• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
lerp(float a, float b, float f)67         static float lerp(float a, float b, float f) {
68             return (b-a)*f + a;
69         }
70 
randfrange(float a, float b)71         static float randfrange(float a, float b) {
72             return lerp(a, b, sRNG.nextFloat());
73         }
74 
randsign()75         static int randsign() {
76             return sRNG.nextBoolean() ? 1 : -1;
77         }
78 
pick(E[] array)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 
FlyingIcon(Context context, AttributeSet as)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
onTouchEvent(MotionEvent event)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 
toString()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 
randomizeIcon()176             public void randomizeIcon() {
177                 component = pick(mComponentNames);
178                 setImageBitmap(mIcons.get(component));
179             }
180 
randomize()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             }
reset()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             }
update(float dt)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 {
FlyingStar(Context context, AttributeSet as)227             public FlyingStar(Context context, AttributeSet as) {
228                 super(context, as);
229             }
randomizeIcon()230             public void randomizeIcon() {
231                 setImageResource(R.drawable.widget_resize_handle_bottom);
232             }
randomize()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 
Board(Context context, AttributeSet as)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 
reset()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
onAttachedToWindow()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 
onSizeChanged(int w, int h, int oldw, int oldh)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
onDetachedFromWindow()350         protected void onDetachedFromWindow() {
351             super.onDetachedFromWindow();
352             mAnim.cancel();
353         }
354 
355         @Override
isOpaque()356         public boolean isOpaque() {
357             return true;
358         }
359 
360         @Override
onInterceptTouchEvent(MotionEvent e)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         };
resetWarpTimer()372         public void resetWarpTimer() {
373             final Handler h = getHandler();
374             h.removeCallbacks(mEngageWarp);
375             h.postDelayed(mEngageWarp, 5000);
376         }
377 
378         @Override
onTouchEvent(MotionEvent event)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
onStart()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
onUserInteraction()412     public void onUserInteraction() {
413         if (!ROCKET_LAUNCHER) {
414             finish();
415         }
416     }
417 }
418