Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c31f6432 authored by Jim Miller's avatar Jim Miller
Browse files

Fix 2797185: Integrate Carousel widget into framework.

Change-Id: Ia03e3f582541dd7f6079929e51d2484b5b91a67b
parent cadef538
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -39,6 +39,9 @@ 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?
+372 −0
Original line number Diff line number Diff line
/*
 * 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);
    }
}
+244 −0
Original line number Diff line number Diff line
/*
 * 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.widget.CarouselRS.CarouselCallback;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.renderscript.FileA3D;
import android.renderscript.Mesh;
import android.renderscript.RSSurfaceView;
import android.renderscript.RenderScriptGL;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

public class CarouselView extends RSSurfaceView {
    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) {
        super(context);
        mContext = context;
        boolean useDepthBuffer = true;
        mRS = createRenderScript(useDepthBuffer);
        mRenderScript = new CarouselRS();
        mRenderScript.init(mRS, getResources());
    }
    
    @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() {
        if(mRS != null) {
            mRS = null;
            destroyRenderScript();
        }
    }
    
    @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;
}
+753 −0

File added.

Preview size limit exceeded, changes collapsed.

+8 −0
Original line number Diff line number Diff line
@@ -33,8 +33,16 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)
# PRODUCT-agnostic resource data like IDs and type definitions.
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

include $(BUILD_PACKAGE)

# Make sure the system .rs files get compiled when building this package
$(LOCAL_BUILT_MODULE): $(framework_RenderScript_STAMP_FILE)

# define a global intermediate target that other module may depend on.
.PHONY: framework-res-package-target
framework-res-package-target: $(LOCAL_BUILT_MODULE)