• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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.cooliris.media;
18 
19 import java.util.Random;
20 
21 import android.content.Context;
22 
23 import com.cooliris.app.App;
24 import com.cooliris.media.FloatUtils;
25 
26 /**
27  * A simple structure for a MediaItem that can be rendered.
28  */
29 public final class DisplayItem {
30     private static final float STACK_SPACING = 0.2f;
31     private DirectLinkedList.Entry<DisplayItem> mAnimatablesEntry = new DirectLinkedList.Entry<DisplayItem>(this);
32     private static final Random random = new Random();
33     private Vector3f mStacktopPosition = new Vector3f(-1.0f, -1.0f, -1.0f);
34     private Vector3f mJitteredPosition = new Vector3f();
35     private boolean mHasFocus;
36     private Vector3f mTargetPosition = new Vector3f();
37     private float mTargetTheta;
38     private float mImageTheta;
39     private int mStackId;
40     private MediaItemTexture mThumbnailImage = null;
41     private Texture mScreennailImage = null;
42     private UriTexture mHiResImage = null;
43     private float mConvergenceSpeed = 1.0f;
44 
45     public final MediaItem mItemRef;
46     public float mAnimatedTheta;
47     public float mAnimatedImageTheta;
48     public float mAnimatedPlaceholderFade = 0f;
49     public boolean mAlive;
50     public Vector3f mAnimatedPosition = new Vector3f();
51     public int mCurrentSlotIndex;
52     private boolean mPerformingScale;
53     private float mSpan;
54     private float mSpanDirection;
55     private float mStartOffset;
56     private float mSpanSpeed;
57     private static final String TAG = "DisplayItem";
58 
DisplayItem(MediaItem item)59     public DisplayItem(MediaItem item) {
60         mItemRef = item;
61         mAnimatedImageTheta = item.mRotation;
62         mImageTheta = item.mRotation;
63         if (item == null)
64             throw new UnsupportedOperationException("Cannot create a displayitem from a null MediaItem.");
65         mCurrentSlotIndex = Shared.INVALID;
66     }
67 
getAnimatablesEntry()68     public DirectLinkedList.Entry<DisplayItem> getAnimatablesEntry() {
69         return mAnimatablesEntry;
70     }
71 
rotateImageBy(float theta)72     public final void rotateImageBy(float theta) {
73         mImageTheta += theta;
74     }
75 
set(Vector3f position, int stackIndex, boolean performTransition)76     public final void set(Vector3f position, int stackIndex, boolean performTransition) {
77         mConvergenceSpeed = 1.0f;
78         Vector3f animatedPosition = mAnimatedPosition;
79         Vector3f targetPosition = mTargetPosition;
80         int seed = stackIndex;
81         int randomSeed = stackIndex;
82 
83         if (seed > 3) {
84             seed = 3;
85             randomSeed = 0;
86         }
87 
88         if (!mAlive) {
89             animatedPosition.set(position);
90             animatedPosition.z = -3.0f + stackIndex * STACK_SPACING;
91         }
92 
93         targetPosition.set(position);
94         if (mStackId != stackIndex && stackIndex >= 0) {
95             mStackId = stackIndex;
96         }
97 
98         if (randomSeed == 0) {
99             if (stackIndex == 0) {
100                 mTargetTheta = 0.0f;
101             } else if (mTargetTheta == 0.0f){
102                 mTargetTheta = 30.0f * (0.5f - (float) Math.random());
103             }
104             mTargetPosition.z = seed * STACK_SPACING;
105             mJitteredPosition.set(0, 0, seed * STACK_SPACING);
106         } else {
107             int sign = (seed % 2 == 0) ? 1 : -1;
108             if (seed != 0 && !mStacktopPosition.equals(position) && mTargetTheta == 0) {
109                 mTargetTheta = 30.0f * (0.5f - (float) Math.random());
110                 mJitteredPosition.x = sign * 12.0f * seed + (0.5f - random.nextFloat()) * 4 * seed;
111                 mJitteredPosition.y = sign * 4 + ((sign == 1) ? -8.0f : sign * (random.nextFloat()) * 16.0f);
112                 mJitteredPosition.x *= App.PIXEL_DENSITY;
113                 mJitteredPosition.y *= App.PIXEL_DENSITY;
114                 mJitteredPosition.z = seed * STACK_SPACING;
115             }
116         }
117         mTargetPosition.add(mJitteredPosition);
118         mStacktopPosition.set(position);
119         mStartOffset = 0.0f;
120     }
121 
getStackIndex()122     public int getStackIndex() {
123         return mStackId;
124     }
125 
getThumbnailImage(Context context, MediaItemTexture.Config config)126     public Texture getThumbnailImage(Context context, MediaItemTexture.Config config) {
127         MediaItemTexture texture = mThumbnailImage;
128         if (texture == null && config != null) {
129             if (mItemRef.mId != Shared.INVALID) {
130                 texture = new MediaItemTexture(context, config, mItemRef);
131             }
132             mThumbnailImage = texture;
133         }
134         return texture;
135     }
136 
getScreennailImage(Context context)137     public Texture getScreennailImage(Context context) {
138         Texture texture = mScreennailImage;
139         if (texture == null || texture.mState == Texture.STATE_ERROR) {
140             MediaSet parentMediaSet = mItemRef.mParentMediaSet;
141             if (parentMediaSet != null && parentMediaSet.mDataSource.getThumbnailCache() == LocalDataSource.sThumbnailCache) {
142                 if (mItemRef.mId != Shared.INVALID && mItemRef.mId != 0) {
143                     texture = new MediaItemTexture(context, null, mItemRef);
144                 } else if (mItemRef.mContentUri != null) {
145                     texture = new UriTexture(mItemRef.mContentUri);
146                 }
147             } else {
148                 texture = new UriTexture(mItemRef.mScreennailUri);
149                 ((UriTexture) texture).setCacheId(Utils.Crc64Long(mItemRef.mFilePath));
150             }
151             mScreennailImage = texture;
152         }
153         return texture;
154     }
155 
clearScreennailImage()156     public void clearScreennailImage() {
157         if (mScreennailImage != null) {
158             mScreennailImage = null;
159             mHiResImage = null;
160         }
161     }
162 
clearHiResImage()163     public void clearHiResImage() {
164         mHiResImage = null;
165     }
166 
clearThumbnail()167     public void clearThumbnail() {
168         mThumbnailImage = null;
169     }
170 
171     /**
172      * Use this function to query the animation state of the display item
173      *
174      * @return true if the display item is animating
175      */
isAnimating()176     public boolean isAnimating() {
177         return mAlive
178                 && (mPerformingScale ||
179                         !mAnimatedPosition.equals(mTargetPosition) || mAnimatedTheta != mTargetTheta
180                         || mAnimatedImageTheta != mImageTheta || mAnimatedPlaceholderFade != 1f);
181     }
182 
183     /**
184      * This function should be called every time the frame needs to be updated.
185      */
update(float timeElapsedInSec)186     public final void update(float timeElapsedInSec) {
187         if (mAlive) {
188             timeElapsedInSec *= 1.25f;
189             Vector3f animatedPosition = mAnimatedPosition;
190             Vector3f targetPosition = mTargetPosition;
191             timeElapsedInSec *= mConvergenceSpeed;
192             animatedPosition.x = FloatUtils.animate(animatedPosition.x, targetPosition.x, timeElapsedInSec);
193             animatedPosition.y = FloatUtils.animate(animatedPosition.y, targetPosition.y, timeElapsedInSec);
194             mAnimatedTheta = FloatUtils.animate(mAnimatedTheta, mTargetTheta, timeElapsedInSec);
195             mAnimatedImageTheta = FloatUtils.animate(mAnimatedImageTheta, mImageTheta, timeElapsedInSec);
196             mAnimatedPlaceholderFade = FloatUtils.animate(mAnimatedPlaceholderFade, 1f, timeElapsedInSec);
197             animatedPosition.z = FloatUtils.animate(animatedPosition.z, targetPosition.z, timeElapsedInSec);
198         }
199     }
200 
201     /**
202      * Commits all animations for the Display Item
203      */
commit()204     public final void commit() {
205         mAnimatedPosition.set(mTargetPosition);
206         mAnimatedTheta = mTargetTheta;
207         mAnimatedImageTheta = mImageTheta;
208     }
209 
setHasFocus(boolean hasFocus, boolean pushDown)210     public final void setHasFocus(boolean hasFocus, boolean pushDown) {
211         mConvergenceSpeed = 2.0f;
212         mHasFocus = hasFocus;
213         int seed = mStackId;
214         if (seed > 3) {
215             seed = 3;
216         }
217         if (hasFocus) {
218             mTargetPosition.set(mStacktopPosition);
219             mTargetPosition.add(mJitteredPosition);
220             mTargetPosition.add(mJitteredPosition);
221             mTargetPosition.z = seed * STACK_SPACING + (pushDown ? 1.0f : -0.5f);
222         } else {
223             mTargetPosition.set(mStacktopPosition);
224             mTargetPosition.add(mJitteredPosition);
225             mTargetPosition.z = seed * STACK_SPACING;
226         }
227     }
228 
setSingleOffset(boolean useOffset, boolean pushAway, float x, float y, float z, float spreadValue)229     public final void setSingleOffset(boolean useOffset, boolean pushAway, float x, float y, float z, float spreadValue) {
230         int seed = mStackId;
231         if (useOffset) {
232             mTargetPosition.set(mStacktopPosition);
233             if (spreadValue > 4.0f)
234                 spreadValue = 4.0f + 0.1f * spreadValue;
235             if (spreadValue < 1.0f) {
236                 spreadValue = 1.0f / spreadValue;
237                 pushAway = true;
238             }
239             if (!pushAway) {
240                 if (seed == 0) {
241                     mTargetPosition.add(0, -spreadValue * 14, 0);
242                 }
243                 if (seed == 1) {
244                     mTargetPosition.add(-spreadValue * 32, 0, 0);
245                 }
246                 if (seed == 2) {
247                     mTargetPosition.add(0, spreadValue * 14, 0);
248                 }
249                 if (seed == 3) {
250                     mTargetPosition.add(spreadValue * 32, 0, 0);
251                 }
252                 mTargetPosition.z = -1.0f * spreadValue + seed * STACK_SPACING * spreadValue;
253                 mTargetTheta = 0.0f;
254             } else {
255                 mTargetPosition.z = seed * STACK_SPACING + spreadValue * 0.5f;
256             }
257         } else {
258             if (seed > 3) {
259                 seed = 3;
260             }
261             mTargetPosition.set(mStacktopPosition);
262             mTargetPosition.add(mJitteredPosition);
263             mTargetPosition.z = seed * STACK_SPACING;
264             if (seed != 0 && mTargetTheta == 0.0f) {
265                 mTargetTheta = 30.0f * (0.5f - (float) Math.random());
266             }
267             mStartOffset = 0.0f;
268         }
269     }
270 
setOffset(boolean useOffset, boolean pushDown, float span, float dx1, float dy1, float dx2, float dy2)271     public final void setOffset(boolean useOffset, boolean pushDown, float span, float dx1, float dy1, float dx2, float dy2) {
272         int seed = mStackId;
273         if (useOffset) {
274             mPerformingScale = true;
275             float spanDelta = span - mSpan;
276             float maxSlots = mItemRef.mParentMediaSet.getNumExpectedItems();
277             maxSlots = FloatUtils.clamp(maxSlots, 0, GridLayer.MAX_ITEMS_PER_SLOT);
278             if (Math.abs(spanDelta) < 10 * App.PIXEL_DENSITY) {
279                 // almost the same span
280                 mStartOffset += (mSpanDirection * mSpanSpeed);
281                 mStartOffset = FloatUtils.clamp(mStartOffset, 0, maxSlots);
282             } else {
283                 mSpanSpeed = Math.abs(span / (600 * App.PIXEL_DENSITY));
284                 if (mSpanSpeed > 2.0f) {
285                     mSpanSpeed = 2.0f;
286                 }
287                 mSpanSpeed *= 0.1f;
288                 mSpanDirection = Math.signum(spanDelta);
289             }
290             mSpan = span;
291             mTargetPosition.set(mStacktopPosition);
292             if (!pushDown) {
293                 if (maxSlots < 2)
294                     return;
295                 // If it is the stacktop, we track the top finger, ie, x1, y1
296                 // else
297                 // we track bottom finger x2, y2
298                 // Instead of using linear interpolation, we will also try to
299                 // look at the spread value to decide how many move at a given
300                 // point of time.
301                 int maxSeedVal = (int)(span / (125 * App.PIXEL_DENSITY));
302                 maxSeedVal = (int)FloatUtils.clamp(maxSeedVal, 2, maxSlots - 1);
303                 float startOffset = FloatUtils.clamp(mStartOffset, 0, maxSlots - maxSeedVal - 1);
304                 float offsetSeed = seed - startOffset;
305                 float seedFactor = offsetSeed / maxSeedVal;
306                 seedFactor = FloatUtils.clamp(seedFactor, 0.0f, 1.0f);
307                 float dx = dx2 * seedFactor + (1.0f - seedFactor) * dx1;
308                 float dy = dy2 * seedFactor + (1.0f - seedFactor) * dy1;
309                 mTargetPosition.add(dx, dy, seed * 0.1f);
310                 mTargetTheta = 0.0f;
311             } else {
312                 mStartOffset = 0.0f;
313                 mTargetPosition.z = seed * STACK_SPACING + 3.0f;
314             }
315         } else {
316             mPerformingScale = false;
317             mStartOffset = 0.0f;
318             if (seed > 3) {
319                 seed = 3;
320             }
321             mTargetPosition.set(mStacktopPosition);
322             mTargetPosition.add(mJitteredPosition);
323             mTargetPosition.z = seed * STACK_SPACING;
324             if (seed != 0 && mTargetTheta == 0.0f) {
325                 mTargetTheta = 30.0f * (0.5f - (float) Math.random());
326             }
327         }
328     }
329 
getHasFocus()330     public final boolean getHasFocus() {
331         return mHasFocus;
332     }
333 
getHiResImage(Context context)334     public final Texture getHiResImage(Context context) {
335         UriTexture texture = mHiResImage;
336         if (texture == null) {
337             texture = new UriTexture(mItemRef.mContentUri);
338             texture.setCacheId(Utils.Crc64Long(mItemRef.mFilePath));
339             mHiResImage = texture;
340         }
341         return texture;
342     }
343 
isAlive()344     public boolean isAlive() {
345         return mAlive;
346     }
347 
getImageTheta()348     public float getImageTheta() {
349         return mImageTheta;
350     }
351 }
352