/* * Copyright (C) 2008 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.launcher2; import java.io.Writer; import java.util.ArrayList; import java.util.concurrent.Semaphore; import java.lang.Float; import android.renderscript.RSSurfaceView; import android.renderscript.RenderScript; import android.renderscript.RenderScript; import android.renderscript.ProgramVertex; import android.renderscript.Element; import android.renderscript.Allocation; import android.renderscript.Script; import android.renderscript.ScriptC; import android.renderscript.ProgramFragment; import android.renderscript.ProgramStore; import android.renderscript.Sampler; import android.content.Context; import android.content.res.Resources; import android.database.DataSetObserver; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BlurMaskFilter; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Message; import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.VelocityTracker; import android.view.ViewConfiguration; import android.graphics.PixelFormat; public class AllAppsView extends RSSurfaceView implements View.OnClickListener, View.OnLongClickListener, DragSource { private static final String TAG = "Launcher.AllAppsView"; /** Bit for mLocks for when there are icons being loaded. */ private static final int LOCK_ICONS_PENDING = 1; /** Bit for mLocks for when the enter/exit is going. */ private static final int LOCK_ZOOMING = 2; private Launcher mLauncher; private DragController mDragController; /** When this is 0, modifications are allowed, when it's not, they're not. * TODO: What about scrolling? */ private int mLocks = LOCK_ICONS_PENDING; private RenderScript mRS; private RolloRS mRollo; private ArrayList mAllAppsList; private ViewConfiguration mConfig; private int mPageCount; private boolean mStartedScrolling; private VelocityTracker mVelocity; private int mLastScrollX; private int mLastMotionX; private int mMotionDownRawX; private int mMotionDownRawY; private int mScrollHandleTop; static class Defines { private static float farSize(float sizeAt0) { return sizeAt0 * (Defines.RADIUS - Defines.CAMERA_Z) / -Defines.CAMERA_Z; } public static final int ALLOC_PARAMS = 0; public static final int ALLOC_STATE = 1; public static final int ALLOC_ICON_IDS = 2; public static final int ALLOC_LABEL_IDS = 3; public static final int ALLOC_X_BORDERS = 4; public static final int ALLOC_Y_BORDERS = 5; public static final int COLUMNS_PER_PAGE = 4; public static final int ROWS_PER_PAGE = 4; public static final float RADIUS = 4.0f; public static final int SCREEN_WIDTH_PX = 480; public static final int SCREEN_HEIGHT_PX = 854; public static final int ICON_WIDTH_PX = 64; public static final int ICON_TEXTURE_WIDTH_PX = 128; public static final int ICON_HEIGHT_PX = 64; public static final int ICON_TEXTURE_HEIGHT_PX = 128; public static final float ICON_TOP_OFFSET = 0.2f; public static final float CAMERA_Z = -2; public static final float FAR_ICON_SIZE = farSize(2 * ICON_WIDTH_PX / (float)SCREEN_WIDTH_PX); } public AllAppsView(Context context, AttributeSet attrs) { super(context, attrs); setFocusable(true); getHolder().setFormat(PixelFormat.TRANSLUCENT); mConfig = ViewConfiguration.get(context); setOnClickListener(this); setOnLongClickListener(this); } public AllAppsView(Context context, AttributeSet attrs, int defStyle) { this(context, attrs); } public void setLauncher(Launcher launcher) { mLauncher = launcher; } @Override public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { super.surfaceChanged(holder, format, w, h); long startTime = SystemClock.uptimeMillis(); mRS = createRenderScript(true); mRollo = new RolloRS(); mRollo.init(getResources(), w, h); if (mAllAppsList != null) { mRollo.setApps(mAllAppsList); Log.d(TAG, "surfaceChanged... calling mRollo.setApps"); } Resources res = getContext().getResources(); int barHeight = (int)res.getDimension(R.dimen.button_bar_height); mScrollHandleTop = h - barHeight; long endTime = SystemClock.uptimeMillis(); Log.d(TAG, "surfaceChanged took " + (endTime-startTime) + "ms"); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { // this method doesn't work when 'extends View' include 'extends ScrollView'. return super.onKeyDown(keyCode, event); } @Override public boolean onTouchEvent(MotionEvent ev) { if (mRollo.mState.visible == 0) { return true; } if (mLocks != 0) { return true; } super.onTouchEvent(ev); int x = (int)ev.getX(); int deltaX; switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mMotionDownRawX = (int)ev.getRawX(); mMotionDownRawY = (int)ev.getRawY(); mLastMotionX = x; mRollo.mState.read(); mRollo.mState.startScrollX = mRollo.mState.scrollX = mLastScrollX = mRollo.mState.currentScrollX; if (mRollo.mState.flingVelocityX != 0) { mRollo.clearSelectedIcon(); } else { mRollo.selectIcon(x, (int)ev.getY(), mRollo.mState.startScrollX, (-mRollo.mState.startScrollX / Defines.SCREEN_WIDTH_PX)); } mRollo.mState.flingVelocityX = 0; mRollo.mState.adjustedDeceleration = 0; mRollo.mState.save(); mVelocity = VelocityTracker.obtain(); mVelocity.addMovement(ev); mStartedScrolling = false; break; case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_OUTSIDE: int slop = Math.abs(x - mLastMotionX); if (!mStartedScrolling && slop < mConfig.getScaledTouchSlop()) { // don't update mLastMotionX so slop is right and when we do start scrolling // below, we get the right delta. } else { mStartedScrolling = true; mRollo.clearSelectedIcon(); deltaX = x - mLastMotionX; mVelocity.addMovement(ev); mRollo.mState.currentScrollX = mLastScrollX; mLastScrollX += deltaX; mRollo.mState.scrollX = mLastScrollX; mRollo.mState.save(); mLastMotionX = x; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mVelocity.computeCurrentVelocity(1000 /* px/sec */, mConfig.getScaledMaximumFlingVelocity()); mRollo.mState.flingTimeMs = (int)SystemClock.uptimeMillis(); // TODO: use long mRollo.mState.flingVelocityX = (int)mVelocity.getXVelocity(); mRollo.clearSelectedIcon(); mRollo.mState.save(); mLastMotionX = -10000; mVelocity.recycle(); mVelocity = null; break; } return true; } public void onClick(View v) { if (mLocks != 0) { return; } int index = mRollo.mState.selectedIconIndex; if (mRollo.mState.flingVelocityX == 0 && index >= 0 && index < mAllAppsList.size()) { ApplicationInfo app = mAllAppsList.get(index); mLauncher.startActivitySafely(app.intent); } } public boolean onLongClick(View v) { if (mLocks != 0) { return true; } int index = mRollo.mState.selectedIconIndex; Log.d(TAG, "long click! velocity=" + mRollo.mState.flingVelocityX + " index=" + index); if (mRollo.mState.flingVelocityX == 0 && index >= 0 && index < mAllAppsList.size()) { ApplicationInfo app = mAllAppsList.get(index); // We don't really have an accurate location to use. This will do. int screenX = mMotionDownRawX - (Defines.ICON_WIDTH_PX / 2); int screenY = mMotionDownRawY - Defines.ICON_HEIGHT_PX; int left = (Defines.ICON_TEXTURE_WIDTH_PX - Defines.ICON_WIDTH_PX) / 2; int top = (Defines.ICON_TEXTURE_HEIGHT_PX - Defines.ICON_HEIGHT_PX) / 2; mDragController.startDrag(app.iconBitmap, screenX, screenY, left, top, Defines.ICON_WIDTH_PX, Defines.ICON_HEIGHT_PX, this, app, DragController.DRAG_ACTION_COPY); mLauncher.closeAllApps(true); } return true; } public void setDragController(DragController dragger) { mDragController = dragger; } public void onDropCompleted(View target, boolean success) { } private static final int SCALE_SCALE = 100000; public void setScale(float amount) { cancelLongPress(); mRollo.mState.read(); mRollo.clearSelectedIcon(); if (amount > 0.001f) { mRollo.mState.visible = 1; mRollo.mState.zoom = (int)(SCALE_SCALE*amount); } else { mRollo.mState.visible = 0; mRollo.mState.zoom = 0; } if (amount > 0.001f && amount < 0.999f) { mLocks |= LOCK_ZOOMING; } else { mLocks &= ~LOCK_ZOOMING; } mRollo.mState.save(); } public boolean isZooming() { return (mLocks & LOCK_ZOOMING) != 0; } public boolean isVisible() { return mRollo != null && mRollo.mState.visible != 0; } @Override public boolean onTrackballEvent(MotionEvent ev) { float x = ev.getX(); float y = ev.getY(); //Float tx = new Float(x); //Float ty = new Float(y); //Log.e("rs", "tbe " + tx.toString() + ", " + ty.toString()); return true; } public void setApps(ArrayList list) { mAllAppsList = list; if (mRollo != null) { mRollo.setApps(list); } mPageCount = countPages(list.size()); Log.d(TAG, "setApps mRollo=" + mRollo + " list=" + list); mLocks &= ~LOCK_ICONS_PENDING; } private void invokeIcon(int index) { Log.d(TAG, "launch it!!!! index=" + index); } private static int countPages(int iconCount) { int iconsPerPage = Defines.COLUMNS_PER_PAGE * Defines.ROWS_PER_PAGE; int pages = iconCount / iconsPerPage; if (pages*iconsPerPage != iconCount) { pages++; } return pages; } public class RolloRS { // Allocations ====== private int mWidth; private int mHeight; private Resources mRes; private Script mScript; private Sampler mSampler; private Sampler mSamplerText; private ProgramStore mPSBackground; private ProgramStore mPSText; private ProgramFragment mPFDebug; private ProgramFragment mPFImages; private ProgramFragment mPFText; private ProgramVertex mPV; private ProgramVertex.MatrixAllocation mPVAlloc; private ProgramVertex mPVOrtho; private ProgramVertex.MatrixAllocation mPVOrthoAlloc; private Allocation mScrollHandle; private Allocation[] mIcons; private int[] mIconIds; private Allocation mAllocIconID; private Allocation[] mLabels; private int[] mLabelIds; private Allocation mAllocLabelID; private Allocation mSelectedIcon; private int[] mTouchYBorders; private Allocation mAllocTouchYBorders; private int[] mTouchXBorders; private Allocation mAllocTouchXBorders; private Bitmap mSelectionBitmap; Params mParams; State mState; class Params extends IntAllocation { Params(RenderScript rs) { super(rs); } @AllocationIndex(0) public int bubbleWidth; @AllocationIndex(1) public int bubbleHeight; @AllocationIndex(2) public int bubbleBitmapWidth; @AllocationIndex(3) public int bubbleBitmapHeight; @AllocationIndex(4) public int scrollHandleId; @AllocationIndex(5) public int scrollHandleTextureWidth; @AllocationIndex(6) public int scrollHandleTextureHeight; } class State extends IntAllocation { State(RenderScript rs) { super(rs); } @AllocationIndex(0) public int iconCount; @AllocationIndex(1) public int scrollX; @AllocationIndex(2) public int flingTimeMs; @AllocationIndex(3) public int flingVelocityX; @AllocationIndex(4) public int adjustedDeceleration; @AllocationIndex(5) public int currentScrollX; @AllocationIndex(6) public int flingDuration; @AllocationIndex(7) public int flingEndPos; @AllocationIndex(8) public int startScrollX; @AllocationIndex(9) public int selectedIconIndex = -1; @AllocationIndex(10) public int selectedIconTexture; @AllocationIndex(11) public int visible; @AllocationIndex(12) public int zoom; } public RolloRS() { } public void init(Resources res, int width, int height) { mRes = res; mWidth = width; mHeight = height; initGl(); initData(); initTouchState(); initRs(); } private void initGl() { Sampler.Builder sb = new Sampler.Builder(mRS); sb.setMin(Sampler.Value.LINEAR);//_MIP_LINEAR); sb.setMag(Sampler.Value.LINEAR); sb.setWrapS(Sampler.Value.CLAMP); sb.setWrapT(Sampler.Value.CLAMP); mSampler = sb.create(); sb.setMin(Sampler.Value.NEAREST); sb.setMag(Sampler.Value.NEAREST); mSamplerText = sb.create(); ProgramFragment.Builder dbg = new ProgramFragment.Builder(mRS, null, null); mPFDebug = dbg.create(); mPFDebug.setName("PFDebug"); ProgramFragment.Builder bf = new ProgramFragment.Builder(mRS, null, null); bf.setTexEnable(true, 0); bf.setTexEnvMode(ProgramFragment.EnvMode.MODULATE, 0); mPFImages = bf.create(); mPFImages.setName("PF"); mPFImages.bindSampler(mSampler, 0); bf.setTexEnvMode(ProgramFragment.EnvMode.MODULATE, 0); //mPFText = bf.create(); mPFText = (new ProgramFragment.Builder(mRS, null, null)).create(); mPFText.setName("PFText"); mPFText.bindSampler(mSamplerText, 0); ProgramStore.Builder bs = new ProgramStore.Builder(mRS, null, null); bs.setDepthFunc(ProgramStore.DepthFunc.ALWAYS); bs.setDitherEnable(false); bs.setDepthMask(true); bs.setBlendFunc(ProgramStore.BlendSrcFunc.SRC_ALPHA, ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA); mPSBackground = bs.create(); mPSBackground.setName("PFS"); bs.setDepthFunc(ProgramStore.DepthFunc.ALWAYS); bs.setDepthMask(false); bs.setBlendFunc(ProgramStore.BlendSrcFunc.SRC_ALPHA, ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA); mPSText = bs.create(); mPSText.setName("PFSText"); mPVAlloc = new ProgramVertex.MatrixAllocation(mRS); mPVAlloc.setupProjectionNormalized(mWidth, mHeight); ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null); mPV = pvb.create(); mPV.setName("PV"); mPV.bindAllocation(mPVAlloc); mPVOrthoAlloc = new ProgramVertex.MatrixAllocation(mRS); mPVOrthoAlloc.setupOrthoWindow(mWidth, mHeight); pvb.setTextureMatrixEnable(true); mPVOrtho = pvb.create(); mPVOrtho.setName("PVOrtho"); mPVOrtho.bindAllocation(mPVOrthoAlloc); mRS.contextBindProgramVertex(mPV); mTouchXBorders = new int[Defines.COLUMNS_PER_PAGE+1]; mAllocTouchXBorders = Allocation.createSized(mRS, Element.USER_I32, mTouchXBorders.length); mAllocTouchXBorders.data(mTouchXBorders); mTouchYBorders = new int[Defines.ROWS_PER_PAGE+1]; mAllocTouchYBorders = Allocation.createSized(mRS, Element.USER_I32, mTouchYBorders.length); mAllocTouchYBorders.data(mTouchYBorders); Log.e("rs", "Done loading named"); } private void initData() { mParams = new Params(mRS); mState = new State(mRS); final Utilities.BubbleText bubble = new Utilities.BubbleText(getContext()); mParams.bubbleWidth = bubble.getBubbleWidth(); mParams.bubbleHeight = bubble.getMaxBubbleHeight(); mParams.bubbleBitmapWidth = bubble.getBitmapWidth(); mParams.bubbleBitmapHeight = bubble.getBitmapHeight(); mScrollHandle = Allocation.createFromBitmapResource(mRS, mRes, R.drawable.all_apps_button_pow2, Element.RGBA_8888, false); mScrollHandle.uploadToTexture(0); mParams.scrollHandleId = mScrollHandle.getID(); Log.d(TAG, "mParams.scrollHandleId=" + mParams.scrollHandleId); mParams.scrollHandleTextureWidth = 128; mParams.scrollHandleTextureHeight = 128; mParams.save(); mState.save(); mSelectionBitmap = Bitmap.createBitmap(Defines.ICON_WIDTH_PX, Defines.ICON_HEIGHT_PX, Bitmap.Config.ARGB_8888); Bitmap selectionBitmap = mSelectionBitmap; Paint paint = new Paint(); float radius = 12 * getContext().getResources().getDisplayMetrics().density; //paint.setMaskFilter(new BlurMaskFilter(radius, BlurMaskFilter.Blur.OUTER)); Canvas canvas = new Canvas(selectionBitmap); canvas.drawColor(0xffff0000); mSelectedIcon = Allocation.createFromBitmap(mRS, selectionBitmap, Element.RGBA_8888, false); mSelectedIcon.uploadToTexture(0); mState.selectedIconTexture = mSelectedIcon.getID(); Log.d(TAG, "initData calling mRollo.setApps"); setApps(null); } private void initRs() { ScriptC.Builder sb = new ScriptC.Builder(mRS); sb.setScript(mRes, R.raw.rollo); sb.setRoot(true); sb.addDefines(Defines.class); mScript = sb.create(); mScript.setClearColor(0.0f, 0.0f, 0.0f, 0.0f); mScript.bindAllocation(mParams.getAllocation(), Defines.ALLOC_PARAMS); mScript.bindAllocation(mState.getAllocation(), Defines.ALLOC_STATE); mScript.bindAllocation(mAllocIconID, Defines.ALLOC_ICON_IDS); mScript.bindAllocation(mAllocLabelID, Defines.ALLOC_LABEL_IDS); mScript.bindAllocation(mAllocTouchXBorders, Defines.ALLOC_X_BORDERS); mScript.bindAllocation(mAllocTouchYBorders, Defines.ALLOC_Y_BORDERS); mRS.contextBindRootScript(mScript); } private void setApps(ArrayList list) { final int count = list != null ? list.size() : 0; mIcons = new Allocation[count]; mIconIds = new int[count]; mAllocIconID = Allocation.createSized(mRS, Element.USER_I32, count); mLabels = new Allocation[count]; mLabelIds = new int[count]; mAllocLabelID = Allocation.createSized(mRS, Element.USER_I32, count); Element ie8888 = Element.RGBA_8888; Utilities.BubbleText bubble = new Utilities.BubbleText(getContext()); for (int i=0; i