Update RecentApplications to use shared Carousel widget.

Change-Id: I5ca7389aeca9ee6f03f48317f9d1034f9fb8c1ca
This commit is contained in:
Jim Miller
2010-09-02 18:08:05 -07:00
parent 2cb3e29e82
commit 783cb60ddb
9 changed files with 49 additions and 1437 deletions

View File

@@ -39,9 +39,6 @@ LOCAL_SRC_FILES += \
core/java/android/webkit/EventLogTags.logtags \
telephony/java/com/android/internal/telephony/EventLogTags.logtags \
# RenderScript files for internal widgets
LOCAL_SRC_FILES += $(call all-renderscript-files-under, core/java/com/android/internal/widget)
# The following filters out code we are temporarily not including at all.
# TODO: Move AWT and beans (and associated harmony code) back into libcore.
# TODO: Maybe remove javax.microedition entirely?

View File

@@ -1,372 +0,0 @@
/*
* Copyright (C) 2010 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.android.internal.widget;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.renderscript.*;
import android.renderscript.RenderScript.RSMessage;
import android.renderscript.Sampler.Value;
import android.renderscript.ProgramRaster.CullMode;
import android.util.Log;
import com.android.internal.R;
import static android.renderscript.Element.*;
import static android.renderscript.Sampler.Value.LINEAR;
import static android.renderscript.Sampler.Value.WRAP;
import static android.renderscript.Sampler.Value.CLAMP;
public class CarouselRS {
private static final int DEFAULT_VISIBLE_SLOTS = 1;
private static final int DEFAULT_CARD_COUNT = 1;
// Client messages *** THIS LIST MUST MATCH THOSE IN carousel.rs ***
public static final int CMD_CARD_SELECTED = 100;
public static final int CMD_REQUEST_TEXTURE = 200;
public static final int CMD_INVALIDATE_TEXTURE = 210;
public static final int CMD_REQUEST_GEOMETRY = 300;
public static final int CMD_INVALIDATE_GEOMETRY = 310;
public static final int CMD_ANIMATION_STARTED = 400;
public static final int CMD_ANIMATION_FINISHED = 500;
public static final int CMD_PING = 600; // for debugging
private static final String TAG = "CarouselRS";
private static final int DEFAULT_SLOT_COUNT = 10;
private static final boolean MIPMAP = false;
private RenderScriptGL mRS;
private Resources mRes;
private ScriptC_Carousel mScript;
private ScriptField_Card mCards;
private Sampler mSampler;
private ProgramRaster mProgramRaster;
private ProgramStore mProgramStore;
private ProgramFragment mFragmentProgram;
private ProgramVertex mVertexProgram;
private ProgramRaster mRasterProgram;
private CarouselCallback mCallback;
private float[] mEyePoint = new float[3];
private float[] mAtPoint = new float[3];
private float[] mUp = new float[3];
public static interface CarouselCallback {
/**
* Called when a card is selected
* @param n the id of the card
*/
void onCardSelected(int n);
/**
* Called when texture is needed for card n. This happens when the given card becomes
* visible.
* @param n the id of the card
*/
void onRequestTexture(int n);
/**
* Called when a texture is no longer needed for card n. This happens when the card
* goes out of view.
* @param n the id of the card
*/
void onInvalidateTexture(int n);
/**
* Called when geometry is needed for card n.
* @param n the id of the card.
*/
void onRequestGeometry(int n);
/**
* Called when geometry is no longer needed for card n. This happens when the card goes
* out of view.
* @param n the id of the card
*/
void onInvalidateGeometry(int n);
/**
* Called when card animation (e.g. a fling) has started.
*/
void onAnimationStarted();
/**
* Called when card animation has stopped.
*/
void onAnimationFinished();
};
private RSMessage mRsMessage = new RSMessage() {
public void run() {
if (mCallback == null) return;
switch (mID) {
case CMD_CARD_SELECTED:
mCallback.onCardSelected(mData[0]);
break;
case CMD_REQUEST_TEXTURE:
mCallback.onRequestTexture(mData[0]);
break;
case CMD_INVALIDATE_TEXTURE:
mCallback.onInvalidateTexture(mData[0]);
break;
case CMD_REQUEST_GEOMETRY:
mCallback.onRequestGeometry(mData[0]);
break;
case CMD_INVALIDATE_GEOMETRY:
mCallback.onInvalidateGeometry(mData[0]);
break;
case CMD_ANIMATION_STARTED:
mCallback.onAnimationStarted();
break;
case CMD_ANIMATION_FINISHED:
mCallback.onAnimationFinished();
break;
case CMD_PING:
Log.v(TAG, "PING...");
break;
default:
Log.e(TAG, "Unknown RSMessage: " + mID);
}
}
};
public void init(RenderScriptGL rs, Resources res) {
mRS = rs;
mRes = res;
// create the script object
mScript = new ScriptC_Carousel(mRS, mRes, R.raw.carousel, true);
mRS.mMessageCallback = mRsMessage;
initProgramStore();
initFragmentProgram();
initRasterProgram();
initVertexProgram();
setSlotCount(DEFAULT_SLOT_COUNT);
setVisibleSlots(DEFAULT_VISIBLE_SLOTS);
createCards(DEFAULT_CARD_COUNT);
setStartAngle(0.0f);
setRadius(1.0f);
// update the camera
boolean pcam = true;
if (pcam) {
float eye[] = { 20.6829f, 2.77081f, 16.7314f };
float at[] = { 14.7255f, -3.40001f, -1.30184f };
float up[] = { 0.0f, 1.0f, 0.0f };
setLookAt(eye, at, up);
setRadius(20.0f);
// Fov: 25
} else {
mScript.invoke_lookAt(2.5f, 2.0f, 2.5f, 0.0f, -0.75f, 0.0f, 0.0f, 1.0f, 0.0f);
mScript.set_cardRotation(0.0f);
setRadius(1.5f);
}
resumeRendering();
}
public void setLookAt(float[] eye, float[] at, float[] up) {
for (int i = 0; i < 3; i++) {
mEyePoint[i] = eye[i];
mAtPoint[i] = at[i];
mUp[i] = up[i];
}
mScript.invoke_lookAt(eye[0], eye[1], eye[2], at[0], at[1], at[2], up[0], up[1], up[2]);
}
public void setRadius(float radius) {
mScript.set_radius(radius);
}
private void initVertexProgram() {
ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null);
mVertexProgram = pvb.create();
ProgramVertex.MatrixAllocation pva = new ProgramVertex.MatrixAllocation(mRS);
mVertexProgram.bindAllocation(pva);
pva.setupProjectionNormalized(1, 1);
mScript.set_vertexProgram(mVertexProgram);
}
private void initRasterProgram() {
ProgramRaster.Builder programRasterBuilder = new ProgramRaster.Builder(mRS);
mRasterProgram = programRasterBuilder.create();
//mRasterProgram.setCullMode(CullMode.NONE);
mScript.set_rasterProgram(mRasterProgram);
}
private void initFragmentProgram() {
Sampler.Builder sampleBuilder = new Sampler.Builder(mRS);
sampleBuilder.setMin(Value.LINEAR_MIP_LINEAR);
sampleBuilder.setMag(LINEAR);
sampleBuilder.setWrapS(CLAMP);
sampleBuilder.setWrapT(CLAMP);
mSampler = sampleBuilder.create();
ProgramFragment.Builder fragmentBuilder = new ProgramFragment.Builder(mRS);
fragmentBuilder.setTexture(ProgramFragment.Builder.EnvMode.DECAL,
ProgramFragment.Builder.Format.RGBA, 0);
mFragmentProgram = fragmentBuilder.create();
mFragmentProgram.bindSampler(mSampler, 0);
mScript.set_fragmentProgram(mFragmentProgram);
}
private void initProgramStore() {
ProgramStore.Builder programStoreBuilder = new ProgramStore.Builder(mRS, null, null);
programStoreBuilder.setDepthFunc(ProgramStore.DepthFunc.LESS);
programStoreBuilder.setBlendFunc(ProgramStore.BlendSrcFunc.SRC_ALPHA,
ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA);
programStoreBuilder.setDitherEnable(false);
programStoreBuilder.setDepthMask(true);
mProgramStore = programStoreBuilder.create();
mScript.set_programStore(mProgramStore);
}
public void createCards(int count)
{
mCards = count > 0 ? new ScriptField_Card(mRS, count) : null;
mScript.bind_cards(mCards);
mScript.invoke_createCards(count);
}
public void setVisibleSlots(int count)
{
mScript.set_visibleSlotCount(count);
}
public void setDefaultBitmap(Bitmap bitmap)
{
mScript.set_defaultTexture(allocationFromBitmap(bitmap, MIPMAP));
}
public void setLoadingBitmap(Bitmap bitmap)
{
mScript.set_loadingTexture(allocationFromBitmap(bitmap, MIPMAP));
}
public void setDefaultGeometry(Mesh mesh)
{
mScript.set_defaultGeometry(mesh);
}
public void setLoadingGeometry(Mesh mesh)
{
mScript.set_loadingGeometry(mesh);
}
public void setStartAngle(float theta)
{
mScript.set_startAngle(theta);
}
public void setCallback(CarouselCallback callback)
{
mCallback = callback;
}
private Allocation allocationFromBitmap(Bitmap bitmap, boolean mipmap)
{
if (bitmap == null) return null;
Allocation allocation = Allocation.createFromBitmap(mRS, bitmap, RGB_565(mRS), mipmap);
allocation.uploadToTexture(0);
return allocation;
}
public void setTexture(int n, Bitmap bitmap)
{
ScriptField_Card.Item item = mCards.get(n);
if (item == null) {
Log.v(TAG, "setTexture(): no item at index " + n);
item = new ScriptField_Card.Item();
}
if (bitmap != null) {
Log.v(TAG, "creating new bitmap");
item.texture = Allocation.createFromBitmap(mRS, bitmap, RGB_565(mRS), MIPMAP);
Log.v(TAG, "uploadToTexture(" + n + ")");
item.texture.uploadToTexture(0);
Log.v(TAG, "done...");
} else {
if (item.texture != null) {
Log.v(TAG, "unloading texture " + n);
// Don't wait for GC to free native memory.
// Only works if textures are not shared.
item.texture.destroy();
item.texture = null;
}
}
mCards.set(item, n, false); // This is primarily used for reference counting.
mScript.invoke_setTexture(n, item.texture);
}
public void setGeometry(int n, Mesh geometry)
{
final boolean mipmap = false;
ScriptField_Card.Item item = mCards.get(n);
if (item == null) {
Log.v(TAG, "setGeometry(): no item at index " + n);
item = new ScriptField_Card.Item();
}
if (geometry != null) {
item.geometry = geometry;
} else {
Log.v(TAG, "unloading geometry " + n);
if (item.geometry != null) {
// item.geometry.destroy();
item.geometry = null;
}
}
mCards.set(item, n, false);
mScript.invoke_setGeometry(n, item.geometry);
}
public void pauseRendering() {
// Used to update multiple states at once w/o redrawing for each.
mRS.contextBindRootScript(null);
}
public void resumeRendering() {
mRS.contextBindRootScript(mScript);
}
public void doMotion(float x, float y) {
mScript.invoke_doMotion(x,y);
}
public void doSelection(float x, float y) {
mScript.invoke_doSelection(x, y);
}
public void doStart(float x, float y) {
mScript.invoke_doStart(x, y);
}
public void doStop(float x, float y) {
mScript.invoke_doStop(x, y);
}
public void setSlotCount(int n) {
mScript.set_slotCount(n);
}
}

View File

@@ -1,266 +0,0 @@
/*
* Copyright (C) 2010 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.android.internal.widget;
import com.android.internal.R;
import com.android.internal.widget.CarouselRS.CarouselCallback;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.Bitmap.Config;
import android.renderscript.FileA3D;
import android.renderscript.Mesh;
import android.renderscript.RSSurfaceView;
import android.renderscript.RenderScriptGL;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
public class CarouselView extends RSSurfaceView {
private static final boolean USE_DEPTH_BUFFER = true;
private final int DEFAULT_SLOT_COUNT = 10;
private final Bitmap DEFAULT_BITMAP = Bitmap.createBitmap(1, 1, Config.RGB_565);
private static final String TAG = "CarouselView";
private CarouselRS mRenderScript;
private RenderScriptGL mRS;
private Context mContext;
private boolean mTracking;
private Bitmap mDefaultBitmap;
private Bitmap mLoadingBitmap;
private Mesh mDefaultGeometry;
private Mesh mLoadingGeometry;
private int mCardCount = 0;
private int mVisibleSlots = 0;
private float mStartAngle;
private int mSlotCount = DEFAULT_SLOT_COUNT;
public CarouselView(Context context) {
this(context, null);
}
/**
* Constructor used when this widget is created from a layout file.
*/
public CarouselView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
boolean useDepthBuffer = true;
mRS = createRenderScript(USE_DEPTH_BUFFER);
mRenderScript = new CarouselRS();
mRenderScript.init(mRS, getResources());
// TODO: add parameters to layout
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
super.surfaceChanged(holder, format, w, h);
//mRS.contextSetSurface(w, h, holder.getSurface());
mRenderScript.init(mRS, getResources());
setSlotCount(mSlotCount);
createCards(mCardCount);
setVisibleSlots(mVisibleSlots);
setCallback(mCarouselCallback);
setDefaultBitmap(mDefaultBitmap);
setLoadingBitmap(mLoadingBitmap);
setDefaultGeometry(mDefaultGeometry);
setLoadingGeometry(mLoadingGeometry);
setStartAngle(mStartAngle);
}
/**
* Loads geometry from a resource id.
*
* @param resId
* @return the loaded mesh or null if it cannot be loaded
*/
public Mesh loadGeometry(int resId) {
Resources res = mContext.getResources();
FileA3D model = FileA3D.createFromResource(mRS, res, resId);
FileA3D.IndexEntry entry = model.getIndexEntry(0);
if(entry == null || entry.getClassID() != FileA3D.ClassID.MESH) {
return null;
}
return (Mesh) entry.getObject();
}
/**
* Load A3D file from resource. If resId == 0, will clear geometry for this item.
* @param n
* @param resId
*/
public void setGeometryForItem(int n, Mesh mesh) {
mRenderScript.setGeometry(n, mesh);
}
public void setSlotCount(int n) {
mSlotCount = n;
if (mRenderScript != null) {
mRenderScript.setSlotCount(n);
}
}
public void setVisibleSlots(int n) {
mVisibleSlots = n;
if (mRenderScript != null) {
mRenderScript.setVisibleSlots(n);
}
}
public void createCards(int n) {
mCardCount = n;
if (mRenderScript != null) {
mRenderScript.createCards(n);
}
}
public void setTextureForItem(int n, Bitmap bitmap) {
if (mRenderScript != null) {
Log.v(TAG, "setTextureForItem(" + n + ")");
mRenderScript.setTexture(n, bitmap);
Log.v(TAG, "done");
}
}
public void setDefaultBitmap(Bitmap bitmap) {
mDefaultBitmap = bitmap;
if (mRenderScript != null) {
mRenderScript.setDefaultBitmap(bitmap);
}
}
public void setLoadingBitmap(Bitmap bitmap) {
mLoadingBitmap = bitmap;
if (mRenderScript != null) {
mRenderScript.setLoadingBitmap(bitmap);
}
}
public void setDefaultGeometry(Mesh mesh) {
mDefaultGeometry = mesh;
if (mRenderScript != null) {
mRenderScript.setDefaultGeometry(mesh);
}
}
public void setLoadingGeometry(Mesh mesh) {
mLoadingGeometry = mesh;
if (mRenderScript != null) {
mRenderScript.setLoadingGeometry(mesh);
}
}
public void setCallback(CarouselCallback callback)
{
mCarouselCallback = callback;
if (mRenderScript != null) {
mRenderScript.setCallback(callback);
}
}
public void setStartAngle(float angle)
{
mStartAngle = angle;
if (mRenderScript != null) {
mRenderScript.setStartAngle(angle);
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if(mRS != null) {
mRS = null;
destroyRenderScript();
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mRS == null) {
mRS = createRenderScript(USE_DEPTH_BUFFER);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getAction();
final float x = event.getX();
final float y = event.getY();
if (mRenderScript == null) {
return true;
}
switch (action) {
case MotionEvent.ACTION_DOWN:
mTracking = true;
mRenderScript.doStart(x, y);
break;
case MotionEvent.ACTION_MOVE:
if (mTracking) {
mRenderScript.doMotion(x, y);
}
break;
case MotionEvent.ACTION_UP:
mRenderScript.doStop(x, y);
mTracking = false;
break;
}
return true;
}
private final CarouselCallback DEBUG_CALLBACK = new CarouselCallback() {
public void onAnimationStarted() {
Log.v(TAG, "onAnimationStarted()");
}
public void onAnimationFinished() {
Log.v(TAG, "onAnimationFinished()");
}
public void onCardSelected(int n) {
Log.v(TAG, "onCardSelected(" + n + ")");
}
public void onRequestGeometry(int n) {
Log.v(TAG, "onRequestGeometry(" + n + ")");
}
public void onInvalidateGeometry(int n) {
Log.v(TAG, "onInvalidateGeometry(" + n + ")");
}
public void onRequestTexture(final int n) {
Log.v(TAG, "onRequestTexture(" + n + ")");
}
public void onInvalidateTexture(int n) {
Log.v(TAG, "onInvalidateTexture(" + n + ")");
}
};
private CarouselCallback mCarouselCallback = DEBUG_CALLBACK;
}

View File

@@ -1,788 +0,0 @@
/*
* Copyright (C) 2010 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.
*/
#pragma version(1)
#pragma rs java_package_name(com.android.internal.widget)
#pragma rs set_reflect_license()
#include "rs_graphics.rsh"
typedef struct __attribute__((aligned(4))) Card {
rs_allocation texture;
rs_mesh geometry;
//rs_matrix4x4 matrix; // custom transform for this card/geometry
int textureState; // whether or not the texture is loaded.
int geometryState; // whether or not geometry is loaded
int visible; // not bool because of packing bug?
} Card_t;
typedef struct Ray_s {
float3 position;
float3 direction;
} Ray;
typedef struct PerspectiveCamera_s {
float3 from;
float3 at;
float3 up;
float fov;
float aspect;
float near;
float far;
} PerspectiveCamera;
// Request states. Used for loading 3D object properties from the Java client.
// Typical properties: texture, geometry and matrices.
enum {
STATE_INVALID = 0, // item hasn't been loaded
STATE_LOADING, // we've requested an item but are waiting for it to load
STATE_LOADED // item was delivered
};
// Client messages *** THIS LIST MUST MATCH THOSE IN CarouselRS.java. ***
static const int CMD_CARD_SELECTED = 100;
static const int CMD_REQUEST_TEXTURE = 200;
static const int CMD_INVALIDATE_TEXTURE = 210;
static const int CMD_REQUEST_GEOMETRY = 300;
static const int CMD_INVALIDATE_GEOMETRY = 310;
static const int CMD_ANIMATION_STARTED = 400;
static const int CMD_ANIMATION_FINISHED = 500;
static const int CMD_PING = 600;
// Constants
static const int ANIMATION_SCALE_TIME = 200; // Time it takes to animate selected card, in ms
static const float3 SELECTED_SCALE_FACTOR = { 0.2f, 0.2f, 0.2f }; // increase by this %
// Debug flags
bool debugCamera = false; // dumps ray/camera coordinate stuff
bool debugPicking = false; // renders picking area on top of geometry
// Exported variables. These will be reflected to Java set_* variables.
Card_t *cards; // array of cards to draw
float startAngle; // position of initial card, in radians
int slotCount; // number of positions where a card can be
int cardCount; // number of cards in stack
int visibleSlotCount; // number of visible slots (for culling)
float radius; // carousel radius. Cards will be centered on a circle with this radius
float cardRotation; // rotation of card in XY plane relative to Z=1
rs_program_store programStore;
rs_program_fragment fragmentProgram;
rs_program_vertex vertexProgram;
rs_program_raster rasterProgram;
rs_allocation defaultTexture; // shown when no other texture is assigned
rs_allocation loadingTexture; // progress texture (shown when app is fetching the texture)
rs_mesh defaultGeometry; // shown when no geometry is loaded
rs_mesh loadingGeometry; // shown when geometry is loading
rs_matrix4x4 projectionMatrix;
rs_matrix4x4 modelviewMatrix;
#pragma rs export_var(radius, cards, slotCount, visibleSlotCount, cardRotation)
#pragma rs export_var(programStore, fragmentProgram, vertexProgram, rasterProgram)
#pragma rs export_var(startAngle, defaultTexture, loadingTexture, defaultGeometry, loadingGeometry)
#pragma rs export_func(createCards, lookAt, doStart, doStop, doMotion, doSelection, setTexture)
#pragma rs export_func(setGeometry, debugCamera, debugPicking)
// Local variables
static float bias; // rotation bias, in radians. Used for animation and dragging.
static bool updateCamera; // force a recompute of projection and lookat matrices
static bool initialized;
static float3 backgroundColor = { 0.0f, 0.0f, 0.0f };
static const float FLT_MAX = 1.0e37;
static int currentSelection = -1;
static int64_t touchTime = -1; // time of first touch (see doStart())
static float touchBias = 0.0f; // bias on first touch
// Default geometry when card.geometry is not set.
static const float3 cardVertices[4] = {
{ -1.0, -1.0, 0.0 },
{ 1.0, -1.0, 0.0 },
{ 1.0, 1.0, 0.0 },
{-1.0, 1.0, 0.0 }
};
// Default camera
static PerspectiveCamera camera = {
{2,2,2}, // from
{0,0,0}, // at
{0,1,0}, // up
25.0f, // field of view
1.0f, // aspect
0.1f, // near
100.0f // far
};
// Forward references
static int intersectGeometry(Ray* ray, float *bestTime);
static bool makeRayForPixelAt(Ray* ray, float x, float y);
static float deltaTimeInSeconds(int64_t current);
void init() {
// initializers currently have a problem when the variables are exported, so initialize
// globals here.
rsDebug("Renderscript: init()", 0);
startAngle = 0.0f;
slotCount = 10;
visibleSlotCount = 1;
bias = 0.0f;
radius = 1.0f;
cardRotation = 0.0f;
updateCamera = true;
initialized = false;
}
static void updateAllocationVars()
{
// Cards
rs_allocation cardAlloc = rsGetAllocation(cards);
// TODO: use new rsIsObject()
cardCount = cardAlloc.p != 0 ? rsAllocationGetDimX(cardAlloc) : 0;
}
void createCards(int n)
{
rsDebug("CreateCards: ", n);
initialized = false;
updateAllocationVars();
}
// Return angle for position p. Typically p will be an integer position, but can be fractional.
static float cardPosition(float p)
{
return startAngle + bias + 2.0f * M_PI * p / slotCount;
}
// Return slot for a card in position p. Typically p will be an integer slot, but can be fractional.
static float slotPosition(float p)
{
return startAngle + 2.0f * M_PI * p / slotCount;
}
// Return the lowest slot number for a given angular position.
static int cardIndex(float angle)
{
return floor(angle - startAngle - bias) * slotCount / (2.0f * M_PI);
}
// Set basic camera properties:
// from - position of the camera in x,y,z
// at - target we're looking at - used to compute view direction
// up - a normalized vector indicating up (typically { 0, 1, 0})
//
// NOTE: the view direction and up vector cannot be parallel/antiparallel with each other
void lookAt(float fromX, float fromY, float fromZ,
float atX, float atY, float atZ,
float upX, float upY, float upZ)
{
camera.from.x = fromX;
camera.from.y = fromY;
camera.from.z = fromZ;
camera.at.x = atX;
camera.at.y = atY;
camera.at.z = atZ;
camera.up.x = upX;
camera.up.y = upY;
camera.up.z = upZ;
updateCamera = true;
}
// Load a projection matrix for the given parameters. This is equivalent to gluPerspective()
static void loadPerspectiveMatrix(rs_matrix4x4* matrix, float fovy, float aspect, float near, float far)
{
rsMatrixLoadIdentity(matrix);
float top = near * tan((float) (fovy * M_PI / 360.0f));
float bottom = -top;
float left = bottom * aspect;
float right = top * aspect;
rsMatrixLoadFrustum(matrix, left, right, bottom, top, near, far);
}
// Construct a matrix based on eye point, center and up direction. Based on the
// man page for gluLookat(). Up must be normalized.
static void loadLookatMatrix(rs_matrix4x4* matrix, float3 eye, float3 center, float3 up)
{
float3 f = normalize(center - eye);
float3 s = normalize(cross(f, up));
float3 u = cross(s, f);
float m[16];
m[0] = s.x;
m[4] = s.y;
m[8] = s.z;
m[12] = 0.0f;
m[1] = u.x;
m[5] = u.y;
m[9] = u.z;
m[13] = 0.0f;
m[2] = -f.x;
m[6] = -f.y;
m[10] = -f.z;
m[14] = 0.0f;
m[3] = m[7] = m[11] = 0.0f;
m[15] = 1.0f;
rsMatrixLoad(matrix, m);
rsMatrixTranslate(matrix, -eye.x, -eye.y, -eye.z);
}
void setTexture(int n, rs_allocation texture)
{
cards[n].texture = texture;
if (cards[n].texture.p != 0)
cards[n].textureState = STATE_LOADED;
else
cards[n].textureState = STATE_INVALID;
}
void setGeometry(int n, rs_mesh geometry)
{
cards[n].geometry = geometry;
if (cards[n].geometry.p != 0)
cards[n].geometryState = STATE_LOADED;
else
cards[n].geometryState = STATE_INVALID;
}
static float3 getAnimatedScaleForSelected()
{
int64_t dt = (rsUptimeMillis() - touchTime);
float fraction = (dt < ANIMATION_SCALE_TIME) ? (float) dt / ANIMATION_SCALE_TIME : 1.0f;
const float3 one = { 1.0f, 1.0f, 1.0f };
return one + fraction * SELECTED_SCALE_FACTOR;
}
static void getMatrixForCard(rs_matrix4x4* matrix, int i)
{
float theta = cardPosition(i);
rsMatrixRotate(matrix, degrees(theta), 0, 1, 0);
rsMatrixTranslate(matrix, radius, 0, 0);
rsMatrixRotate(matrix, degrees(-theta + cardRotation), 0, 1, 0);
if (i == currentSelection) {
float3 scale = getAnimatedScaleForSelected();
rsMatrixScale(matrix, scale.x, scale.y, scale.z);
}
// TODO: apply custom matrix for cards[i].geometry
}
static void drawCards()
{
float depth = 1.0f;
for (int i = 0; i < cardCount; i++) {
if (cards[i].visible) {
// Bind texture
if (cards[i].textureState == STATE_LOADED) {
rsgBindTexture(fragmentProgram, 0, cards[i].texture);
} else if (cards[i].textureState == STATE_LOADING) {
rsgBindTexture(fragmentProgram, 0, loadingTexture);
} else {
rsgBindTexture(fragmentProgram, 0, defaultTexture);
}
// Draw geometry
rs_matrix4x4 matrix = modelviewMatrix;
getMatrixForCard(&matrix, i);
rsgProgramVertexLoadModelMatrix(&matrix);
if (cards[i].geometryState == STATE_LOADED && cards[i].geometry.p != 0) {
rsgDrawMesh(cards[i].geometry);
} else if (cards[i].geometryState == STATE_LOADING && loadingGeometry.p != 0) {
rsgDrawMesh(loadingGeometry);
} else if (defaultGeometry.p != 0) {
rsgDrawMesh(defaultGeometry);
} else {
// Draw place-holder geometry
rsgDrawQuad(
cardVertices[0].x, cardVertices[0].y, cardVertices[0].z,
cardVertices[1].x, cardVertices[1].y, cardVertices[1].z,
cardVertices[2].x, cardVertices[2].y, cardVertices[2].z,
cardVertices[3].x, cardVertices[3].y, cardVertices[3].z);
}
}
}
}
static void updateCameraMatrix(float width, float height)
{
float aspect = width / height;
if (aspect != camera.aspect || updateCamera) {
camera.aspect = aspect;
loadPerspectiveMatrix(&projectionMatrix, camera.fov, camera.aspect, camera.near, camera.far);
rsgProgramVertexLoadProjectionMatrix(&projectionMatrix);
loadLookatMatrix(&modelviewMatrix, camera.from, camera.at, camera.up);
rsgProgramVertexLoadModelMatrix(&modelviewMatrix);
updateCamera = false;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Behavior/Physics
////////////////////////////////////////////////////////////////////////////////////////////////////
static float velocity = 0.0f; // angular velocity in radians/s
static bool isDragging;
static int64_t lastTime = 0L; // keep track of how much time has passed between frames
static float2 lastPosition;
static bool animating = false;
static float velocityThreshold = 0.1f * M_PI / 180.0f;
static float velocityTracker;
static int velocityTrackerCount;
static float mass = 5.0f; // kg
static const float G = 9.80f; // gravity constant, in m/s
static const float springConstant = 0.0f;
static const float frictionCoeff = 10.0f;
static const float dragFactor = 0.25f;
static float dragFunction(float x, float y)
{
return dragFactor * ((x - lastPosition.x) / rsgGetWidth()) * M_PI;
}
static float deltaTimeInSeconds(int64_t current)
{
return (lastTime > 0L) ? (float) (current - lastTime) / 1000.0f : 0.0f;
}
int doSelection(float x, float y)
{
Ray ray;
if (makeRayForPixelAt(&ray, x, y)) {
float bestTime = FLT_MAX;
return intersectGeometry(&ray, &bestTime);
}
return -1;
}
void doStart(float x, float y)
{
lastPosition.x = x;
lastPosition.y = y;
velocity = 0.0f;
if (animating) {
rsSendToClient(CMD_ANIMATION_FINISHED);
animating = false;
}
velocityTracker = 0.0f;
velocityTrackerCount = 0;
touchTime = rsUptimeMillis();
touchBias = bias;
currentSelection = doSelection(x, y);
}
void doStop(float x, float y)
{
int64_t currentTime = rsUptimeMillis();
updateAllocationVars();
if (currentSelection != -1 && (currentTime - touchTime) < ANIMATION_SCALE_TIME) {
rsDebug("HIT!", currentSelection);
int data[1];
data[0] = currentSelection;
rsSendToClientBlocking(CMD_CARD_SELECTED, data, sizeof(data));
} else {
velocity = velocityTrackerCount > 0 ?
(velocityTracker / velocityTrackerCount) : 0.0f; // avg velocity
if (fabs(velocity) > velocityThreshold) {
animating = true;
rsSendToClient(CMD_ANIMATION_STARTED);
}
}
currentSelection = -1;
lastTime = rsUptimeMillis();
}
void doMotion(float x, float y)
{
int64_t currentTime = rsUptimeMillis();
float deltaOmega = dragFunction(x, y);
bias += deltaOmega;
lastPosition.x = x;
lastPosition.y = y;
float dt = deltaTimeInSeconds(currentTime);
if (dt > 0.0f) {
float v = deltaOmega / dt;
//if ((velocityTracker > 0.0f) == (v > 0.0f)) {
velocityTracker += v;
velocityTrackerCount++;
//} else {
// velocityTracker = v;
// velocityTrackerCount = 1;
//}
}
// Drop current selection if user drags position +- a partial slot
if (currentSelection != -1) {
const float slotMargin = 0.5f * (2.0f * M_PI / slotCount);
if (fabs(touchBias - bias) > slotMargin) {
currentSelection = -1;
}
}
lastTime = currentTime;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Hit detection using ray casting.
////////////////////////////////////////////////////////////////////////////////////////////////////
static bool
rayTriangleIntersect(Ray* ray, float3 p0, float3 p1, float3 p2, float *tout)
{
static const float tmin = 0.0f;
float3 e1 = p1 - p0;
float3 e2 = p2 - p0;
float3 s1 = cross(ray->direction, e2);
float div = dot(s1, e1);
if (div == 0.0f) return false; // ray is parallel to plane.
float3 d = ray->position - p0;
float invDiv = 1.0f / div;
float u = dot(d, s1) * invDiv;
if (u < 0.0f || u > 1.0f) return false;
float3 s2 = cross(d, e1);
float v = dot(ray->direction, s2) * invDiv;
if ( v < 0.0f || (u+v) > 1.0f) return false;
float t = dot(e2, s2) * invDiv;
if (t < tmin || t > *tout)
return false;
*tout = t;
return true;
}
// Creates a ray for an Android pixel coordinate.
// Note that the Y coordinate is opposite of GL rendering coordinates.
static bool makeRayForPixelAt(Ray* ray, float x, float y)
{
if (debugCamera) {
rsDebug("------ makeRay() -------", 0);
rsDebug("Camera.from:", camera.from);
rsDebug("Camera.at:", camera.at);
rsDebug("Camera.dir:", normalize(camera.at - camera.from));
}
// Vector math. This has the potential to be much faster.
// TODO: pre-compute lowerLeftRay, du, dv to eliminate most of this math.
if (true) {
const float u = x / rsgGetWidth();
const float v = 1.0f - (y / rsgGetHeight());
const float aspect = (float) rsgGetWidth() / rsgGetHeight();
const float tanfov2 = 2.0f * tan(radians(camera.fov / 2.0f));
float3 dir = normalize(camera.at - camera.from);
float3 du = tanfov2 * normalize(cross(dir, camera.up));
float3 dv = tanfov2 * normalize(cross(du, dir));
du *= aspect;
float3 lowerLeftRay = dir - (0.5f * du) - (0.5f * dv);
const float3 rayPoint = camera.from;
const float3 rayDir = normalize(lowerLeftRay + u*du + v*dv);
if (debugCamera) {
rsDebug("Ray direction (vector math) = ", rayDir);
}
ray->position = rayPoint;
ray->direction = rayDir;
}
// Matrix math. This is more generic if we allow setting model view and projection matrices
// directly
else {
rs_matrix4x4 pm = modelviewMatrix;
rsMatrixLoadMultiply(&pm, &projectionMatrix, &modelviewMatrix);
if (!rsMatrixInverse(&pm)) {
rsDebug("ERROR: SINGULAR PM MATRIX", 0);
return false;
}
const float width = rsgGetWidth();
const float height = rsgGetHeight();
const float winx = 2.0f * x / width - 1.0f;
const float winy = 2.0f * y / height - 1.0f;
float4 eye = { 0.0f, 0.0f, 0.0f, 1.0f };
float4 at = { winx, winy, 1.0f, 1.0f };
eye = rsMatrixMultiply(&pm, eye);
eye *= 1.0f / eye.w;
at = rsMatrixMultiply(&pm, at);
at *= 1.0f / at.w;
const float3 rayPoint = { eye.x, eye.y, eye.z };
const float3 atPoint = { at.x, at.y, at.z };
const float3 rayDir = normalize(atPoint - rayPoint);
if (debugCamera) {
rsDebug("winx: ", winx);
rsDebug("winy: ", winy);
rsDebug("Ray position (transformed) = ", eye);
rsDebug("Ray direction (transformed) = ", rayDir);
}
ray->position = rayPoint;
ray->direction = rayDir;
}
return true;
}
static int intersectGeometry(Ray* ray, float *bestTime)
{
int hit = -1;
for (int id = 0; id < cardCount; id++) {
if (cards[id].visible) {
rs_matrix4x4 matrix;
float3 p[4];
// Transform card vertices to world space
rsMatrixLoadIdentity(&matrix);
getMatrixForCard(&matrix, id);
for (int vertex = 0; vertex < 4; vertex++) {
float4 tmp = rsMatrixMultiply(&matrix, cardVertices[vertex]);
if (tmp.w != 0.0f) {
p[vertex].x = tmp.x;
p[vertex].y = tmp.y;
p[vertex].z = tmp.z;
p[vertex] *= 1.0f / tmp.w;
} else {
rsDebug("Bad w coord: ", tmp);
}
}
// Intersect card geometry
if (rayTriangleIntersect(ray, p[0], p[1], p[2], bestTime)
|| rayTriangleIntersect(ray, p[2], p[3], p[0], bestTime)) {
hit = id;
}
}
}
return hit;
}
// This method computes the position of all the cards by updating bias based on a
// simple physics model.
// If the cards are still in motion, returns true.
static bool updateNextPosition(int64_t currentTime)
{
if (animating) {
float dt = deltaTimeInSeconds(currentTime);
if (dt <= 0.0f)
return animating;
const float minStepTime = 1.0f / 300.0f; // ~5 steps per frame
const int N = (dt > minStepTime) ? (1 + round(dt / minStepTime)) : 1;
dt /= N;
for (int i = 0; i < N; i++) {
// Force friction - always opposes motion
const float Ff = -frictionCoeff * velocity;
// Restoring force to match cards with slots
const float theta = startAngle + bias;
const float dtheta = 2.0f * M_PI / slotCount;
const float position = theta / dtheta;
const float fraction = position - floor(position); // fractional position between slots
float x;
if (fraction > 0.5f) {
x = - (1.0f - fraction);
} else {
x = fraction;
}
const float Fr = - springConstant * x;
// compute velocity
const float momentum = mass * velocity + (Ff + Fr)*dt;
velocity = momentum / mass;
bias += velocity * dt;
}
// TODO: Add animation to smoothly move back to slots. Currently snaps to location.
if (cardCount <= visibleSlotCount) {
// TODO: this aligns the cards to the first slot (theta = startAngle) when there aren't
// enough visible cards. It should be generalized to allow alignment to front,
// middle or back of the stack.
if (cardPosition(0) != slotPosition(0)) {
bias = 0.0f;
}
} else {
if (cardPosition(cardCount) < 0.0f) {
bias = -slotPosition(cardCount);
} else if (cardPosition(0) > slotPosition(0)) {
bias = 0.0f;
}
}
animating = fabs(velocity) > velocityThreshold;
if (!animating) {
const float dtheta = 2.0f * M_PI / slotCount;
bias = round((startAngle + bias) / dtheta) * dtheta - startAngle;
rsSendToClient(CMD_ANIMATION_FINISHED);
}
}
lastTime = currentTime;
return animating;
}
// Cull cards based on visibility and visibleSlotCount.
// If visibleSlotCount is > 0, then only show those slots and cull the rest.
// Otherwise, it should cull based on bounds of geometry.
static int cullCards()
{
const float thetaFirst = slotPosition(-1); // -1 keeps the card in front around a bit longer
const float thetaLast = slotPosition(visibleSlotCount);
int count = 0;
for (int i = 0; i < cardCount; i++) {
if (visibleSlotCount > 0) {
// If visibleSlotCount is specified, then only show up to visibleSlotCount cards.
float p = cardPosition(i);
if (p >= thetaFirst && p < thetaLast) {
cards[i].visible = true;
count++;
} else {
cards[i].visible = false;
}
} else {
// Cull the rest of the cards using bounding box of geometry.
// TODO
cards[i].visible = true;
count++;
}
}
return count;
}
// Request texture/geometry for items that have come into view
// or doesn't have a texture yet.
static void updateCardResources()
{
for (int i = 0; i < cardCount; i++) {
int data[1];
if (cards[i].visible) {
// request texture from client if not loaded
if (cards[i].textureState == STATE_INVALID) {
data[0] = i;
bool enqueued = rsSendToClient(CMD_REQUEST_TEXTURE, data, sizeof(data));
if (enqueued) {
cards[i].textureState = STATE_LOADING;
} else {
rsDebug("Couldn't send CMD_REQUEST_TEXTURE", 0);
}
}
// request geometry from client if not loaded
if (cards[i].geometryState == STATE_INVALID) {
data[0] = i;
bool enqueued = rsSendToClient(CMD_REQUEST_GEOMETRY, data, sizeof(data));
if (enqueued) {
cards[i].geometryState = STATE_LOADING;
} else {
rsDebug("Couldn't send CMD_REQUEST_GEOMETRY", 0);
}
}
} else {
// ask the host to remove the texture
if (cards[i].textureState == STATE_LOADED) {
data[0] = i;
bool enqueued = rsSendToClient(CMD_INVALIDATE_TEXTURE, data, sizeof(data));
if (enqueued) {
cards[i].textureState = STATE_INVALID;
} else {
rsDebug("Couldn't send CMD_INVALIDATE_TEXTURE", 0);
}
}
// ask the host to remove the geometry
if (cards[i].geometryState == STATE_LOADED) {
data[0] = i;
bool enqueued = rsSendToClient(CMD_INVALIDATE_GEOMETRY, data, sizeof(data));
if (enqueued) {
cards[i].geometryState = STATE_INVALID;
} else {
rsDebug("Couldn't send CMD_INVALIDATE_GEOMETRY", 0);
}
}
}
}
}
// Places dots on geometry to visually inspect that objects can be seen by rays.
// NOTE: the color of the dot is somewhat random, as it depends on texture of previously-rendered
// card.
static void renderWithRays()
{
const float w = rsgGetWidth();
const float h = rsgGetHeight();
const int skip = 8;
color(1.0f, 0.0f, 0.0f, 1.0f);
for (int j = 0; j < (int) h; j+=skip) {
float posY = (float) j;
for (int i = 0; i < (int) w; i+=skip) {
float posX = (float) i;
Ray ray;
if (makeRayForPixelAt(&ray, posX, posY)) {
float bestTime = FLT_MAX;
if (intersectGeometry(&ray, &bestTime) != -1) {
rsgDrawSpriteScreenspace(posX, h - posY - 1, 0.0f, 2.0f, 2.0f);
}
}
}
}
}
int root() {
int64_t currentTime = rsUptimeMillis();
rsgClearDepth(1.0f);
rsgBindProgramVertex(vertexProgram);
rsgBindProgramFragment(fragmentProgram);
rsgBindProgramStore(programStore);
rsgBindProgramRaster(rasterProgram);
updateAllocationVars();
if (!initialized) {
for (int i = 0; i < cardCount; i++)
cards[i].textureState = STATE_INVALID;
initialized = true;
}
if (false) { // for debugging - flash the screen so we know we're still rendering
static bool toggle;
if (toggle)
rsgClearColor(backgroundColor.x, backgroundColor.y, backgroundColor.z, 1.0);
else
rsgClearColor(1.0f, 0.0f, 0.0f, 1.f);
toggle = !toggle;
} else {
rsgClearColor(backgroundColor.x, backgroundColor.y, backgroundColor.z, 1.0);
}
updateCameraMatrix(rsgGetWidth(), rsgGetHeight());
const bool timeExpired = (currentTime - touchTime) > ANIMATION_SCALE_TIME;
if (timeExpired) {
//currentSelection = -1;
}
bool stillAnimating = updateNextPosition(currentTime) || !timeExpired;
cullCards();
updateCardResources();
drawCards();
if (debugPicking) {
renderWithRays();
}
//rsSendToClient(CMD_PING);
return stillAnimating ? 1 : 0;
}

View File

@@ -36,12 +36,12 @@ LOCAL_EXPORT_PACKAGE_RESOURCES := true
# Include resources generated by system RenderScript files.
framework_GENERATED_SOURCE_DIR := $(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)/src
framework_RenderScript_STAMP_FILE := $(framework_GENERATED_SOURCE_DIR)/RenderScript.stamp
LOCAL_RESOURCE_DIR := $(framework_GENERATED_SOURCE_DIR)/renderscript/res $(LOCAL_PATH)/res
#LOCAL_RESOURCE_DIR := $(framework_GENERATED_SOURCE_DIR)/renderscript/res $(LOCAL_PATH)/res
include $(BUILD_PACKAGE)
# Make sure the system .rs files get compiled before building the package-export.apk.
$(resource_export_package): $(framework_RenderScript_STAMP_FILE)
# $(resource_export_package): $(framework_RenderScript_STAMP_FILE)
# define a global intermediate target that other module may depend on.
.PHONY: framework-res-package-target

View File

@@ -45,11 +45,11 @@
android:text="@android:string/no_recent_tasks"
android:visibility="gone"/>
<com.android.internal.widget.CarouselView
<com.android.systemui.statusbar.RecentApplicationsCarouselView
android:id="@+id/carousel"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1">
</com.android.internal.widget.CarouselView>
</com.android.systemui.statusbar.RecentApplicationsCarouselView>
</LinearLayout>

View File

@@ -3,10 +3,13 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_SRC_FILES := $(call all-subdir-java-files) \
../../../ex/carousel/java/com/android/ex/carousel/carousel.rs
LOCAL_JAVA_LIBRARIES := services
LOCAL_STATIC_JAVA_LIBRARIES := android-common-carousel
LOCAL_PACKAGE_NAME := SystemUI
LOCAL_CERTIFICATE := platform

View File

@@ -17,14 +17,13 @@
package com.android.systemui.statusbar;
import com.android.ex.carousel.CarouselView;
import com.android.ex.carousel.CarouselRS.CarouselCallback;
import com.android.internal.R;
import java.util.ArrayList;
import java.util.List;
import com.android.internal.widget.CarouselView;
import com.android.internal.widget.CarouselRS.CarouselCallback;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.IThumbnailReceiver;

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2010 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.android.systemui.statusbar;
import android.content.Context;
import android.util.AttributeSet;
import com.android.ex.carousel.CarouselView;
import com.android.systemui.R;
public class RecentApplicationsCarouselView extends CarouselView {
public RecentApplicationsCarouselView(Context context) {
this(context, null);
}
public RecentApplicationsCarouselView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public Info getRenderScriptInfo() {
return new Info(R.raw.carousel);
}
}