blob: 2bc334bc7008873ba91932ad3f7374fe486c1bc8 [file] [log] [blame]
/*
* Copyright (C) 2009 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.
*/
package com.cooliris.media;
import java.util.Random;
import android.content.Context;
import com.cooliris.app.App;
import com.cooliris.media.FloatUtils;
/**
* A simple structure for a MediaItem that can be rendered.
*/
public final class DisplayItem {
private static final float STACK_SPACING = 0.2f;
private DirectLinkedList.Entry<DisplayItem> mAnimatablesEntry = new DirectLinkedList.Entry<DisplayItem>(this);
private static final Random random = new Random();
private Vector3f mStacktopPosition = new Vector3f(-1.0f, -1.0f, -1.0f);
private Vector3f mJitteredPosition = new Vector3f();
private boolean mHasFocus;
private Vector3f mTargetPosition = new Vector3f();
private float mTargetTheta;
private float mImageTheta;
private int mStackId;
private MediaItemTexture mThumbnailImage = null;
private Texture mScreennailImage = null;
private UriTexture mHiResImage = null;
private float mConvergenceSpeed = 1.0f;
public final MediaItem mItemRef;
public float mAnimatedTheta;
public float mAnimatedImageTheta;
public float mAnimatedPlaceholderFade = 0f;
public boolean mAlive;
public Vector3f mAnimatedPosition = new Vector3f();
public int mCurrentSlotIndex;
private boolean mPerformingScale;
private float mSpan;
private float mSpanDirection;
private float mStartOffset;
private float mSpanSpeed;
private static final String TAG = "DisplayItem";
public DisplayItem(MediaItem item) {
mItemRef = item;
mAnimatedImageTheta = item.mRotation;
mImageTheta = item.mRotation;
if (item == null)
throw new UnsupportedOperationException("Cannot create a displayitem from a null MediaItem.");
mCurrentSlotIndex = Shared.INVALID;
}
public DirectLinkedList.Entry<DisplayItem> getAnimatablesEntry() {
return mAnimatablesEntry;
}
public final void rotateImageBy(float theta) {
mImageTheta += theta;
}
public final void set(Vector3f position, int stackIndex, boolean performTransition) {
mConvergenceSpeed = 1.0f;
Vector3f animatedPosition = mAnimatedPosition;
Vector3f targetPosition = mTargetPosition;
int seed = stackIndex;
int randomSeed = stackIndex;
if (seed > 3) {
seed = 3;
randomSeed = 0;
}
if (!mAlive) {
animatedPosition.set(position);
animatedPosition.z = -3.0f + stackIndex * STACK_SPACING;
}
targetPosition.set(position);
if (mStackId != stackIndex && stackIndex >= 0) {
mStackId = stackIndex;
}
if (randomSeed == 0) {
if (stackIndex == 0) {
mTargetTheta = 0.0f;
} else if (mTargetTheta == 0.0f){
mTargetTheta = 30.0f * (0.5f - (float) Math.random());
}
mTargetPosition.z = seed * STACK_SPACING;
mJitteredPosition.set(0, 0, seed * STACK_SPACING);
} else {
int sign = (seed % 2 == 0) ? 1 : -1;
if (seed != 0 && !mStacktopPosition.equals(position) && mTargetTheta == 0) {
mTargetTheta = 30.0f * (0.5f - (float) Math.random());
mJitteredPosition.x = sign * 12.0f * seed + (0.5f - random.nextFloat()) * 4 * seed;
mJitteredPosition.y = sign * 4 + ((sign == 1) ? -8.0f : sign * (random.nextFloat()) * 16.0f);
mJitteredPosition.x *= App.PIXEL_DENSITY;
mJitteredPosition.y *= App.PIXEL_DENSITY;
mJitteredPosition.z = seed * STACK_SPACING;
}
}
mTargetPosition.add(mJitteredPosition);
mStacktopPosition.set(position);
mStartOffset = 0.0f;
}
public int getStackIndex() {
return mStackId;
}
public Texture getThumbnailImage(Context context, MediaItemTexture.Config config) {
MediaItemTexture texture = mThumbnailImage;
if (texture == null && config != null) {
if (mItemRef.mId != Shared.INVALID) {
texture = new MediaItemTexture(context, config, mItemRef);
}
mThumbnailImage = texture;
}
return texture;
}
public Texture getScreennailImage(Context context) {
Texture texture = mScreennailImage;
if (texture == null || texture.mState == Texture.STATE_ERROR) {
MediaSet parentMediaSet = mItemRef.mParentMediaSet;
if (parentMediaSet != null && parentMediaSet.mDataSource.getThumbnailCache() == LocalDataSource.sThumbnailCache) {
if (mItemRef.mId != Shared.INVALID && mItemRef.mId != 0) {
texture = new MediaItemTexture(context, null, mItemRef);
} else if (mItemRef.mContentUri != null) {
texture = new UriTexture(mItemRef.mContentUri);
}
} else {
texture = new UriTexture(mItemRef.mScreennailUri);
((UriTexture) texture).setCacheId(Utils.Crc64Long(mItemRef.mFilePath));
}
mScreennailImage = texture;
}
return texture;
}
public void clearScreennailImage() {
if (mScreennailImage != null) {
mScreennailImage = null;
mHiResImage = null;
}
}
public void clearHiResImage() {
mHiResImage = null;
}
public void clearThumbnail() {
mThumbnailImage = null;
}
/**
* Use this function to query the animation state of the display item
*
* @return true if the display item is animating
*/
public boolean isAnimating() {
return mAlive
&& (mPerformingScale ||
!mAnimatedPosition.equals(mTargetPosition) || mAnimatedTheta != mTargetTheta
|| mAnimatedImageTheta != mImageTheta || mAnimatedPlaceholderFade != 1f);
}
/**
* This function should be called every time the frame needs to be updated.
*/
public final void update(float timeElapsedInSec) {
if (mAlive) {
timeElapsedInSec *= 1.25f;
Vector3f animatedPosition = mAnimatedPosition;
Vector3f targetPosition = mTargetPosition;
timeElapsedInSec *= mConvergenceSpeed;
animatedPosition.x = FloatUtils.animate(animatedPosition.x, targetPosition.x, timeElapsedInSec);
animatedPosition.y = FloatUtils.animate(animatedPosition.y, targetPosition.y, timeElapsedInSec);
mAnimatedTheta = FloatUtils.animate(mAnimatedTheta, mTargetTheta, timeElapsedInSec);
mAnimatedImageTheta = FloatUtils.animate(mAnimatedImageTheta, mImageTheta, timeElapsedInSec);
mAnimatedPlaceholderFade = FloatUtils.animate(mAnimatedPlaceholderFade, 1f, timeElapsedInSec);
animatedPosition.z = FloatUtils.animate(animatedPosition.z, targetPosition.z, timeElapsedInSec);
}
}
/**
* Commits all animations for the Display Item
*/
public final void commit() {
mAnimatedPosition.set(mTargetPosition);
mAnimatedTheta = mTargetTheta;
mAnimatedImageTheta = mImageTheta;
}
public final void setHasFocus(boolean hasFocus, boolean pushDown) {
mConvergenceSpeed = 2.0f;
mHasFocus = hasFocus;
int seed = mStackId;
if (seed > 3) {
seed = 3;
}
if (hasFocus) {
mTargetPosition.set(mStacktopPosition);
mTargetPosition.add(mJitteredPosition);
mTargetPosition.add(mJitteredPosition);
mTargetPosition.z = seed * STACK_SPACING + (pushDown ? 1.0f : -0.5f);
} else {
mTargetPosition.set(mStacktopPosition);
mTargetPosition.add(mJitteredPosition);
mTargetPosition.z = seed * STACK_SPACING;
}
}
public final void setSingleOffset(boolean useOffset, boolean pushAway, float x, float y, float z, float spreadValue) {
int seed = mStackId;
if (useOffset) {
mTargetPosition.set(mStacktopPosition);
if (spreadValue > 4.0f)
spreadValue = 4.0f + 0.1f * spreadValue;
if (spreadValue < 1.0f) {
spreadValue = 1.0f / spreadValue;
pushAway = true;
}
if (!pushAway) {
if (seed == 0) {
mTargetPosition.add(0, -spreadValue * 14, 0);
}
if (seed == 1) {
mTargetPosition.add(-spreadValue * 32, 0, 0);
}
if (seed == 2) {
mTargetPosition.add(0, spreadValue * 14, 0);
}
if (seed == 3) {
mTargetPosition.add(spreadValue * 32, 0, 0);
}
mTargetPosition.z = -1.0f * spreadValue + seed * STACK_SPACING * spreadValue;
mTargetTheta = 0.0f;
} else {
mTargetPosition.z = seed * STACK_SPACING + spreadValue * 0.5f;
}
} else {
if (seed > 3) {
seed = 3;
}
mTargetPosition.set(mStacktopPosition);
mTargetPosition.add(mJitteredPosition);
mTargetPosition.z = seed * STACK_SPACING;
if (seed != 0 && mTargetTheta == 0.0f) {
mTargetTheta = 30.0f * (0.5f - (float) Math.random());
}
mStartOffset = 0.0f;
}
}
public final void setOffset(boolean useOffset, boolean pushDown, float span, float dx1, float dy1, float dx2, float dy2) {
int seed = mStackId;
if (useOffset) {
mPerformingScale = true;
float spanDelta = span - mSpan;
float maxSlots = mItemRef.mParentMediaSet.getNumExpectedItems();
maxSlots = FloatUtils.clamp(maxSlots, 0, GridLayer.MAX_ITEMS_PER_SLOT);
if (Math.abs(spanDelta) < 10 * App.PIXEL_DENSITY) {
// almost the same span
mStartOffset += (mSpanDirection * mSpanSpeed);
mStartOffset = FloatUtils.clamp(mStartOffset, 0, maxSlots);
} else {
mSpanSpeed = Math.abs(span / (600 * App.PIXEL_DENSITY));
if (mSpanSpeed > 2.0f) {
mSpanSpeed = 2.0f;
}
mSpanSpeed *= 0.1f;
mSpanDirection = Math.signum(spanDelta);
}
mSpan = span;
mTargetPosition.set(mStacktopPosition);
if (!pushDown) {
if (maxSlots < 2)
return;
// If it is the stacktop, we track the top finger, ie, x1, y1
// else
// we track bottom finger x2, y2
// Instead of using linear interpolation, we will also try to
// look at the spread value to decide how many move at a given
// point of time.
int maxSeedVal = (int)(span / (125 * App.PIXEL_DENSITY));
maxSeedVal = (int)FloatUtils.clamp(maxSeedVal, 2, maxSlots - 1);
float startOffset = FloatUtils.clamp(mStartOffset, 0, maxSlots - maxSeedVal - 1);
float offsetSeed = seed - startOffset;
float seedFactor = offsetSeed / maxSeedVal;
seedFactor = FloatUtils.clamp(seedFactor, 0.0f, 1.0f);
float dx = dx2 * seedFactor + (1.0f - seedFactor) * dx1;
float dy = dy2 * seedFactor + (1.0f - seedFactor) * dy1;
mTargetPosition.add(dx, dy, seed * 0.1f);
mTargetTheta = 0.0f;
} else {
mStartOffset = 0.0f;
mTargetPosition.z = seed * STACK_SPACING + 3.0f;
}
} else {
mPerformingScale = false;
mStartOffset = 0.0f;
if (seed > 3) {
seed = 3;
}
mTargetPosition.set(mStacktopPosition);
mTargetPosition.add(mJitteredPosition);
mTargetPosition.z = seed * STACK_SPACING;
if (seed != 0 && mTargetTheta == 0.0f) {
mTargetTheta = 30.0f * (0.5f - (float) Math.random());
}
}
}
public final boolean getHasFocus() {
return mHasFocus;
}
public final Texture getHiResImage(Context context) {
UriTexture texture = mHiResImage;
if (texture == null) {
texture = new UriTexture(mItemRef.mContentUri);
texture.setCacheId(Utils.Crc64Long(mItemRef.mFilePath));
mHiResImage = texture;
}
return texture;
}
public boolean isAlive() {
return mAlive;
}
public float getImageTheta() {
return mImageTheta;
}
}