diff options
Diffstat (limited to 'src')
37 files changed, 2619 insertions, 844 deletions
diff --git a/src/com/android/launcher2/AllApps2D.java b/src/com/android/launcher2/AllApps2D.java index 7ad5e4915..b993364bf 100644 --- a/src/com/android/launcher2/AllApps2D.java +++ b/src/com/android/launcher2/AllApps2D.java @@ -16,32 +16,32 @@ package com.android.launcher2; +import com.android.launcher.R; + import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; -import android.graphics.drawable.BitmapDrawable; import android.graphics.Bitmap; -import android.graphics.Color; +import android.graphics.PixelFormat; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; -import android.view.ViewGroup; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.view.animation.AnimationUtils; -import android.view.ViewConfiguration; import android.widget.AdapterView; -import android.widget.ImageButton; -import android.widget.TextView; import android.widget.ArrayAdapter; import android.widget.GridView; +import android.widget.ImageButton; import android.widget.RelativeLayout; +import android.widget.TextView; import java.util.ArrayList; import java.util.Collections; -import com.android.launcher.R; - public class AllApps2D extends RelativeLayout implements AllAppsView, @@ -68,6 +68,8 @@ public class AllApps2D private AppsAdapter mAppsAdapter; + private boolean mIsViewOpaque; + // ------------------------------------------------------------ public static class HomeButton extends ImageButton { @@ -126,24 +128,24 @@ public class AllApps2D @Override protected void onFinishInflate() { - setBackgroundColor(Color.BLACK); + mIsViewOpaque = super.isOpaque(); try { mGrid = (GridView)findViewWithTag("all_apps_2d_grid"); if (mGrid == null) throw new Resources.NotFoundException(); mGrid.setOnItemClickListener(this); mGrid.setOnItemLongClickListener(this); - mGrid.setBackgroundColor(Color.BLACK); - mGrid.setCacheColorHint(Color.BLACK); + // The home button is optional; some layouts might not use it ImageButton homeButton = (ImageButton) findViewWithTag("all_apps_2d_home"); - if (homeButton == null) throw new Resources.NotFoundException(); - homeButton.setOnClickListener( - new View.OnClickListener() { - public void onClick(View v) { - mLauncher.closeAllApps(true); - } - }); + if (homeButton != null) { + homeButton.setOnClickListener( + new View.OnClickListener() { + public void onClick(View v) { + mLauncher.closeAllApps(true); + } + }); + } } catch (Resources.NotFoundException e) { Log.e(TAG, "Can't find necessary layout elements for AllApps2D"); } @@ -252,7 +254,7 @@ public class AllApps2D @Override public boolean isOpaque() { - return mZoom > 0.999f; + return mIsViewOpaque && mZoom > 0.999f; } public void setApps(ArrayList<ApplicationInfo> list) { diff --git a/src/com/android/launcher2/AllApps3D.java b/src/com/android/launcher2/AllApps3D.java index b8aa8eccf..bb18870dc 100644 --- a/src/com/android/launcher2/AllApps3D.java +++ b/src/com/android/launcher2/AllApps3D.java @@ -16,6 +16,10 @@ package com.android.launcher2; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; + import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; @@ -29,12 +33,10 @@ import android.renderscript.ProgramFragment; import android.renderscript.ProgramStore; import android.renderscript.ProgramVertex; import android.renderscript.RSSurfaceView; -import android.renderscript.RenderScriptGL; import android.renderscript.RenderScript; +import android.renderscript.RenderScriptGL; import android.renderscript.Sampler; -import android.renderscript.Script; -import android.renderscript.ScriptC; -import android.renderscript.SimpleMesh; +import android.renderscript.Mesh; import android.renderscript.Type; import android.util.AttributeSet; import android.util.DisplayMetrics; @@ -48,10 +50,6 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityEvent; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; - import com.android.launcher.R; public class AllApps3D extends RSSurfaceView @@ -131,27 +129,16 @@ public class AllApps3D extends RSSurfaceView private boolean mSurrendered; private int mRestoreFocusIndex = -1; - + @SuppressWarnings({"UnusedDeclaration"}) static class Defines { - public static final int ALLOC_PARAMS = 0; - public static final int ALLOC_STATE = 1; - public static final int ALLOC_ICON_IDS = 3; - public static final int ALLOC_LABEL_IDS = 4; - public static final int ALLOC_VP_CONSTANTS = 5; - public static final int COLUMNS_PER_PAGE_PORTRAIT = 4; public static final int ROWS_PER_PAGE_PORTRAIT = 4; public static final int COLUMNS_PER_PAGE_LANDSCAPE = 6; public static final int ROWS_PER_PAGE_LANDSCAPE = 3; - public static final int ICON_WIDTH_PX = 64; - public static final int ICON_TEXTURE_WIDTH_PX = 74; public static final int SELECTION_TEXTURE_WIDTH_PX = 74 + 20; - - public static final int ICON_HEIGHT_PX = 64; - public static final int ICON_TEXTURE_HEIGHT_PX = 74; public static final int SELECTION_TEXTURE_HEIGHT_PX = 74 + 20; } @@ -159,7 +146,6 @@ public class AllApps3D extends RSSurfaceView super(context, attrs); setFocusable(true); setSoundEffectsEnabled(false); - getHolder().setFormat(PixelFormat.TRANSLUCENT); final ViewConfiguration config = ViewConfiguration.get(context); mSlop = config.getScaledTouchSlop(); mMaxFlingVelocity = config.getScaledMaximumFlingVelocity(); @@ -274,24 +260,25 @@ public class AllApps3D extends RSSurfaceView sRollo.dirtyCheck(); sRollo.resize(w, h); + Log.d(TAG, "sc " + sRS); if (sRS != null) { sRS.mMessageCallback = mMessageProc = new AAMessage(); } if (sRollo.mUniformAlloc != null) { - float tf[] = new float[] {72.f, 72.f, - 120.f, 120.f, 0.f, 0.f, - 120.f, 680.f, - (2.f / 480.f), 0, -((float)w / 2) - 0.25f, -380.25f}; + ScriptField_VpConsts.Item i = new ScriptField_VpConsts.Item(); + i.ScaleOffset.x = (2.f / 480.f); + i.ScaleOffset.y = 0; + i.ScaleOffset.z = -((float)w / 2) - 0.25f; + i.ScaleOffset.w = -380.25f; + i.BendPos.x = 120.f; + i.BendPos.y = 680.f; if (w > h) { - tf[6] = 40.f; - tf[7] = h - 40.f; - tf[9] = 1.f; - tf[10] = -((float)w / 2) - 0.25f; - tf[11] = -((float)h / 2) - 0.25f; + i.ScaleOffset.z = 40.f; + i.ScaleOffset.w = h - 40.f; + i.BendPos.y = 1.f; } - - sRollo.mUniformAlloc.data(tf); + sRollo.mUniformAlloc.set(i, 0, true); } //long endTime = SystemClock.uptimeMillis(); @@ -307,18 +294,17 @@ public class AllApps3D extends RSSurfaceView if (mArrowNavigation) { if (!hasWindowFocus) { // Clear selection when we lose window focus - mLastSelectedIcon = sRollo.mState.selectedIconIndex; + mLastSelectedIcon = sRollo.mScript.get_gSelectedIconIndex(); sRollo.setHomeSelected(SELECTED_NONE); sRollo.clearSelectedIcon(); - sRollo.mState.save(); } else { - if (sRollo.mState.iconCount > 0) { + if (sRollo.mScript.get_gIconCount() > 0) { if (mLastSelection == SELECTION_ICONS) { int selection = mLastSelectedIcon; final int firstIcon = Math.round(sRollo.mScrollPos) * mColumnsPerPage; if (selection < 0 || // No selection selection < firstIcon || // off the top of the screen - selection >= sRollo.mState.iconCount || // past last icon + selection >= sRollo.mScript.get_gIconCount() || // past last icon selection >= firstIcon + // past last icon on screen (mColumnsPerPage * mRowsPerPage)) { selection = firstIcon; @@ -326,10 +312,8 @@ public class AllApps3D extends RSSurfaceView // Select the first icon when we gain window focus sRollo.selectIcon(selection, SELECTED_FOCUSED); - sRollo.mState.save(); } else if (mLastSelection == SELECTION_HOME) { sRollo.setHomeSelected(SELECTED_FOCUSED); - sRollo.mState.save(); } } } @@ -356,7 +340,6 @@ public class AllApps3D extends RSSurfaceView // Clear selection when we lose focus sRollo.clearSelectedIcon(); sRollo.setHomeSelected(SELECTED_NONE); - sRollo.mState.save(); mArrowNavigation = false; } } else { @@ -366,11 +349,10 @@ public class AllApps3D extends RSSurfaceView } private void gainFocus() { - if (!mArrowNavigation && sRollo.mState.iconCount > 0) { + if (!mArrowNavigation && sRollo.mScript.get_gIconCount() > 0) { // Select the first icon when we gain keyboard focus mArrowNavigation = true; sRollo.selectIcon(Math.round(sRollo.mScrollPos) * mColumnsPerPage, SELECTED_FOCUSED); - sRollo.mState.save(); } } @@ -382,7 +364,7 @@ public class AllApps3D extends RSSurfaceView if (!isVisible()) { return false; } - final int iconCount = sRollo.mState.iconCount; + final int iconCount = sRollo.mScript.get_gIconCount(); if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) { if (mArrowNavigation) { @@ -390,7 +372,7 @@ public class AllApps3D extends RSSurfaceView reallyPlaySoundEffect(SoundEffectConstants.CLICK); mLauncher.closeAllApps(true); } else { - int whichApp = sRollo.mState.selectedIconIndex; + int whichApp = sRollo.mScript.get_gSelectedIconIndex(); if (whichApp >= 0) { ApplicationInfo app = mAllAppsList.get(whichApp); mLauncher.startActivitySafely(app.intent, app); @@ -402,10 +384,10 @@ public class AllApps3D extends RSSurfaceView if (iconCount > 0) { final boolean isPortrait = getWidth() < getHeight(); - + mArrowNavigation = true; - int currentSelection = sRollo.mState.selectedIconIndex; + int currentSelection = sRollo.mScript.get_gSelectedIconIndex(); int currentTopRow = Math.round(sRollo.mScrollPos); // The column of the current selection, in the range 0..COLUMNS_PER_PAGE_PORTRAIT-1 @@ -511,7 +493,6 @@ public class AllApps3D extends RSSurfaceView } if (newSelection != currentSelection) { sRollo.selectIcon(newSelection, SELECTED_FOCUSED); - sRollo.mState.save(); } } return handled; @@ -611,7 +592,6 @@ public class AllApps3D extends RSSurfaceView (!isPortrait && x > mTouchXBorders[mTouchXBorders.length-1])) { mTouchTracking = TRACKING_HOME; sRollo.setHomeSelected(SELECTED_PRESSED); - sRollo.mState.save(); mCurrentIconIndex = -1; } else { mTouchTracking = TRACKING_FLING; @@ -619,9 +599,6 @@ public class AllApps3D extends RSSurfaceView mMotionDownRawX = (int)ev.getRawX(); mMotionDownRawY = (int)ev.getRawY(); - sRollo.mState.newPositionX = ev.getRawY() / getHeight(); - sRollo.mState.newTouchDown = 1; - if (!sRollo.checkClickOK()) { sRollo.clearSelectedIcon(); } else { @@ -632,8 +609,7 @@ public class AllApps3D extends RSSurfaceView cancelLongPress(); } } - sRollo.mState.save(); - sRollo.move(); + sRollo.move(ev.getRawY() / getHeight()); mVelocityTracker = VelocityTracker.obtain(); mVelocityTracker.addMovement(ev); mStartedScrolling = false; @@ -646,7 +622,6 @@ public class AllApps3D extends RSSurfaceView y > mTouchYBorders[mTouchYBorders.length-1]) || (!isPortrait && x > mTouchXBorders[mTouchXBorders.length-1]) ? SELECTED_PRESSED : SELECTED_NONE); - sRollo.mState.save(); } else if (mTouchTracking == TRACKING_FLING) { int rawY = (int)ev.getRawY(); int slop; @@ -667,14 +642,11 @@ public class AllApps3D extends RSSurfaceView cancelLongPress(); mCurrentIconIndex = -1; } - sRollo.mState.newPositionX = ev.getRawY() / getHeight(); - sRollo.mState.newTouchDown = 1; - sRollo.move(); + sRollo.move(ev.getRawY() / getHeight()); mStartedScrolling = true; sRollo.clearSelectedIcon(); mVelocityTracker.addMovement(ev); - sRollo.mState.save(); } } break; @@ -688,18 +660,13 @@ public class AllApps3D extends RSSurfaceView mLauncher.closeAllApps(true); } sRollo.setHomeSelected(SELECTED_NONE); - sRollo.mState.save(); } mCurrentIconIndex = -1; } else if (mTouchTracking == TRACKING_FLING) { - sRollo.mState.newTouchDown = 0; - sRollo.mState.newPositionX = ev.getRawY() / getHeight(); - mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, mMaxFlingVelocity); - sRollo.mState.flingVelocity = mVelocityTracker.getYVelocity() / getHeight(); sRollo.clearSelectedIcon(); - sRollo.mState.save(); - sRollo.fling(); + sRollo.fling(ev.getRawY() / getHeight(), + mVelocityTracker.getYVelocity() / getHeight()); if (mVelocityTracker != null) { mVelocityTracker.recycle(); @@ -761,7 +728,7 @@ public class AllApps3D extends RSSurfaceView int pos = -1; switch (mLastSelection) { case SELECTION_ICONS: - index = sRollo.mState.selectedIconIndex; + index = sRollo.mScript.get_gSelectedIconIndex(); if (index >= 0) { ApplicationInfo info = mAllAppsList.get(index); if (info.title != null) { @@ -853,13 +820,12 @@ public class AllApps3D extends RSSurfaceView if (sRollo != null && reload) { sRollo.setApps(list); } - + if (hasFocus() && mRestoreFocusIndex != -1) { sRollo.selectIcon(mRestoreFocusIndex, SELECTED_FOCUSED); - sRollo.mState.save(); mRestoreFocusIndex = -1; } - + mLocks &= ~LOCK_ICONS_PENDING; } @@ -876,7 +842,7 @@ public class AllApps3D extends RSSurfaceView final int N = list.size(); if (sRollo != null) { sRollo.pause(); - sRollo.reallocAppsList(sRollo.mState.iconCount + N); + sRollo.reallocAppsList(sRollo.mScript.get_gIconCount() + N); } for (int i=0; i<N; i++) { @@ -946,17 +912,6 @@ public class AllApps3D extends RSSurfaceView return -1; } - /* - private static int countPages(int iconCount) { - int iconsPerPage = getColumnsCount() * Defines.ROWS_PER_PAGE_PORTRAIT; - int pages = iconCount / iconsPerPage; - if (pages*iconsPerPage != iconCount) { - pages++; - } - return pages; - } - */ - class AAMessage extends RenderScript.RSMessage { public void run() { sRollo.mScrollPos = ((float)mData[0]) / (1 << 16); @@ -988,23 +943,12 @@ public class AllApps3D extends RSSurfaceView private int mHeight; private Resources mRes; - private Script mScript; - private Script.Invokable mInvokeMove; - private Script.Invokable mInvokeMoveTo; - private Script.Invokable mInvokeFling; - private Script.Invokable mInvokeResetWAR; - private Script.Invokable mInvokeSetZoom; - - private ProgramStore mPSIcons; - private ProgramFragment mPFTexMip; - private ProgramFragment mPFTexMipAlpha; - private ProgramFragment mPFTexNearest; - private ProgramVertex mPV; - private ProgramVertex mPVCurve; - private SimpleMesh mMesh; + ScriptC_Allapps mScript; + + private Mesh mMesh; private ProgramVertex.MatrixAllocation mPVA; - private Allocation mUniformAlloc; + private ScriptField_VpConsts mUniformAlloc; private Allocation mHomeButtonNormal; private Allocation mHomeButtonFocused; @@ -1017,15 +961,11 @@ public class AllApps3D extends RSSurfaceView private Allocation[] mLabels; private int[] mLabelIds; private Allocation mAllocLabelIds; - private Allocation mSelectedIcon; private Bitmap mSelectionBitmap; private Canvas mSelectionCanvas; - - private float mScrollPos; - Params mParams; - State mState; + private float mScrollPos; AllApps3D mAllApps; boolean mInitialize; @@ -1056,41 +996,6 @@ public class AllApps3D extends RSSurfaceView } } - class Params extends BaseAlloc { - Params() { - mType = Type.createFromClass(sRS, Params.class, 1, "ParamsClass"); - mAlloc = Allocation.createTyped(sRS, mType); - save(); - } - public int bubbleWidth; - public int bubbleHeight; - public int bubbleBitmapWidth; - public int bubbleBitmapHeight; - - public int homeButtonWidth; - public int homeButtonHeight; - public int homeButtonTextureWidth; - public int homeButtonTextureHeight; - } - - class State extends BaseAlloc { - public float newPositionX; - public int newTouchDown; - public float flingVelocity; - public int iconCount; - public int selectedIconIndex = -1; - public int selectedIconTexture; - public float zoomTarget; - public int homeButtonId; - public float targetPos; - - State() { - mType = Type.createFromClass(sRS, State.class, 1, "StateClass"); - mAlloc = Allocation.createTyped(sRS, mType); - save(); - } - } - public RolloRS(AllApps3D allApps) { mAllApps = allApps; } @@ -1099,16 +1004,21 @@ public class AllApps3D extends RSSurfaceView mRes = res; mWidth = width; mHeight = height; + mScript = new ScriptC_Allapps(sRS, mRes, R.raw.allapps, true); + initProgramVertex(); initProgramFragment(); initProgramStore(); initGl(); initData(); - initRs(); + + mScript.bind_gIconIDs(mAllocIconIds); + mScript.bind_gLabelIDs(mAllocLabelIds); + sRS.contextBindRootScript(mScript); } public void initMesh() { - SimpleMesh.TriangleMeshBuilder tm = new SimpleMesh.TriangleMeshBuilder(sRS, 2, 0); + Mesh.TriangleMeshBuilder tm = new Mesh.TriangleMeshBuilder(sRS, 2, 0); for (int ct=0; ct < 16; ct++) { float pos = (1.f / (16.f - 1)) * ct; @@ -1119,8 +1029,8 @@ public class AllApps3D extends RSSurfaceView tm.addTriangle(ct, ct+1, ct+2); tm.addTriangle(ct+1, ct+3, ct+2); } - mMesh = tm.create(); - mMesh.setName("SMCell"); + mMesh = tm.create(true); + mScript.set_gSMCell(mMesh); } void resize(int w, int h) { @@ -1135,18 +1045,12 @@ public class AllApps3D extends RSSurfaceView ProgramVertex.Builder pvb = new ProgramVertex.Builder(sRS, null, null); pvb.setTextureMatrixEnable(true); - mPV = pvb.create(); - mPV.setName("PV"); - mPV.bindAllocation(mPVA); - - Element.Builder eb = new Element.Builder(sRS); - eb.add(Element.createVector(sRS, Element.DataType.FLOAT_32, 2), "ImgSize"); - eb.add(Element.createVector(sRS, Element.DataType.FLOAT_32, 4), "Position"); - eb.add(Element.createVector(sRS, Element.DataType.FLOAT_32, 2), "BendPos"); - eb.add(Element.createVector(sRS, Element.DataType.FLOAT_32, 4), "ScaleOffset"); - Element e = eb.create(); + ProgramVertex pv = pvb.create(); + pv.bindAllocation(mPVA); + sRS.contextBindProgramVertex(pv); - mUniformAlloc = Allocation.createSized(sRS, e, 1); + mUniformAlloc = new ScriptField_VpConsts(sRS, 1); + mScript.bind_vpConstants(mUniformAlloc); initMesh(); ProgramVertex.ShaderBuilder sb = new ProgramVertex.ShaderBuilder(sRS); @@ -1208,13 +1112,12 @@ public class AllApps3D extends RSSurfaceView "}\n"; sb.setShader(t); sb.addConstant(mUniformAlloc.getType()); - sb.addInput(mMesh.getVertexType(0).getElement()); - mPVCurve = sb.create(); - mPVCurve.setName("PVCurve"); - mPVCurve.bindAllocation(mPVA); - mPVCurve.bindConstants(mUniformAlloc, 1); + sb.addInput(mMesh.getVertexAllocation(0).getType().getElement()); + ProgramVertex pvc = sb.create(); + pvc.bindAllocation(mPVA); + pvc.bindConstants(mUniformAlloc.getAllocation(), 1); - sRS.contextBindProgramVertex(mPV); + mScript.set_gPVCurve(pvc); } private void initProgramFragment() { @@ -1232,20 +1135,20 @@ public class AllApps3D extends RSSurfaceView ProgramFragment.Builder bf = new ProgramFragment.Builder(sRS); bf.setTexture(ProgramFragment.Builder.EnvMode.MODULATE, ProgramFragment.Builder.Format.RGBA, 0); - mPFTexMip = bf.create(); - mPFTexMip.setName("PFTexMip"); - mPFTexMip.bindSampler(linear, 0); + ProgramFragment pfTexMip = bf.create(); + pfTexMip.bindSampler(linear, 0); - mPFTexNearest = bf.create(); - mPFTexNearest.setName("PFTexNearest"); - mPFTexNearest.bindSampler(nearest, 0); + ProgramFragment pfTexNearest = bf.create(); + pfTexNearest.bindSampler(nearest, 0); bf.setTexture(ProgramFragment.Builder.EnvMode.MODULATE, ProgramFragment.Builder.Format.ALPHA, 0); - mPFTexMipAlpha = bf.create(); - mPFTexMipAlpha.setName("PFTexMipAlpha"); - mPFTexMipAlpha.bindSampler(linear, 0); + ProgramFragment pfTexMipAlpha = bf.create(); + pfTexMipAlpha.bindSampler(linear, 0); + mScript.set_gPFTexNearest(pfTexNearest); + mScript.set_gPFTexMip(pfTexMip); + mScript.set_gPFTexMipAlpha(pfTexMipAlpha); } private void initProgramStore() { @@ -1255,23 +1158,17 @@ public class AllApps3D extends RSSurfaceView bs.setDitherEnable(true); bs.setBlendFunc(ProgramStore.BlendSrcFunc.SRC_ALPHA, ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA); - mPSIcons = bs.create(); - mPSIcons.setName("PSIcons"); + mScript.set_gPS(bs.create()); } private void initGl() { } private void initData() { - mParams = new Params(); - mState = new State(); - - final Utilities.BubbleText bubble = new Utilities.BubbleText(mAllApps.getContext()); - - mParams.bubbleWidth = bubble.getBubbleWidth(); - mParams.bubbleHeight = bubble.getMaxBubbleHeight(); - mParams.bubbleBitmapWidth = bubble.getBitmapWidth(); - mParams.bubbleBitmapHeight = bubble.getBitmapHeight(); + mScript.set_COLUMNS_PER_PAGE_PORTRAIT(Defines.COLUMNS_PER_PAGE_PORTRAIT); + mScript.set_ROWS_PER_PAGE_PORTRAIT(Defines.ROWS_PER_PAGE_PORTRAIT); + mScript.set_COLUMNS_PER_PAGE_LANDSCAPE(Defines.COLUMNS_PER_PAGE_LANDSCAPE); + mScript.set_ROWS_PER_PAGE_LANDSCAPE(Defines.ROWS_PER_PAGE_LANDSCAPE); mHomeButtonNormal = Allocation.createFromBitmapResource(sRS, mRes, R.drawable.home_button_normal, Element.RGBA_8888(sRS), false); @@ -1282,15 +1179,8 @@ public class AllApps3D extends RSSurfaceView mHomeButtonPressed = Allocation.createFromBitmapResource(sRS, mRes, R.drawable.home_button_pressed, Element.RGBA_8888(sRS), false); mHomeButtonPressed.uploadToTexture(0); - mParams.homeButtonWidth = 76; - mParams.homeButtonHeight = 68; - mParams.homeButtonTextureWidth = 128; - mParams.homeButtonTextureHeight = 128; - - mState.homeButtonId = mHomeButtonNormal.getID(); - mParams.save(); - mState.save(); + mScript.set_gHomeButton(mHomeButtonNormal); mSelectionBitmap = Bitmap.createBitmap(Defines.SELECTION_TEXTURE_WIDTH_PX, Defines.SELECTION_TEXTURE_HEIGHT_PX, Bitmap.Config.ARGB_8888); @@ -1299,30 +1189,6 @@ public class AllApps3D extends RSSurfaceView setApps(null); } - private void initRs() { - ScriptC.Builder sb = new ScriptC.Builder(sRS); - sb.setScript(mRes, R.raw.allapps); - sb.setRoot(true); - sb.addDefines(mAllApps.mDefines); - sb.setType(mParams.mType, "params", Defines.ALLOC_PARAMS); - sb.setType(mState.mType, "state", Defines.ALLOC_STATE); - sb.setType(mUniformAlloc.getType(), "vpConstants", Defines.ALLOC_VP_CONSTANTS); - mInvokeMove = sb.addInvokable("move"); - mInvokeFling = sb.addInvokable("fling"); - mInvokeMoveTo = sb.addInvokable("moveTo"); - mInvokeResetWAR = sb.addInvokable("resetHWWar"); - mInvokeSetZoom = sb.addInvokable("setZoom"); - mScript = sb.create(); - mScript.setClearColor(0.0f, 0.0f, 0.0f, 0.0f); - mScript.bindAllocation(mParams.mAlloc, Defines.ALLOC_PARAMS); - mScript.bindAllocation(mState.mAlloc, Defines.ALLOC_STATE); - mScript.bindAllocation(mAllocIconIds, Defines.ALLOC_ICON_IDS); - mScript.bindAllocation(mAllocLabelIds, Defines.ALLOC_LABEL_IDS); - mScript.bindAllocation(mUniformAlloc, Defines.ALLOC_VP_CONSTANTS); - - sRS.contextBindRootScript(mScript); - } - void dirtyCheck() { if (sZoomDirty) { setZoom(mAllApps.sNextZoom, mAllApps.sAnimateNextZoom); @@ -1340,20 +1206,21 @@ public class AllApps3D extends RSSurfaceView mIcons = new Allocation[count]; mIconIds = new int[allocCount]; - mAllocIconIds = Allocation.createSized(sRS, Element.USER_I32(sRS), allocCount); + mAllocIconIds = Allocation.createSized(sRS, Element.I32(sRS), allocCount); mLabels = new Allocation[count]; mLabelIds = new int[allocCount]; - mAllocLabelIds = Allocation.createSized(sRS, Element.USER_I32(sRS), allocCount); + mAllocLabelIds = Allocation.createSized(sRS, Element.I32(sRS), allocCount); - mState.iconCount = count; - for (int i=0; i < mState.iconCount; i++) { + mScript.set_gIconCount(count); + for (int i=0; i < count; i++) { createAppIconAllocations(i, list.get(i)); } - for (int i=0; i < mState.iconCount; i++) { + for (int i=0; i < count; i++) { uploadAppIcon(i, list.get(i)); } saveAppsList(); + android.util.Log.e("rs", "setApps"); sRollo.resume(); } @@ -1362,15 +1229,7 @@ public class AllApps3D extends RSSurfaceView sRollo.clearSelectedIcon(); sRollo.setHomeSelected(SELECTED_NONE); } - if (zoom > 0.001f) { - sRollo.mState.zoomTarget = zoom; - } else { - sRollo.mState.zoomTarget = 0; - } - sRollo.mState.save(); - if (!animate) { - sRollo.mInvokeSetZoom.execute(); - } + sRollo.mScript.invoke_setZoom(zoom, animate ? 1 : 0); } private void createAppIconAllocations(int index, ApplicationInfo item) { @@ -1400,13 +1259,13 @@ public class AllApps3D extends RSSurfaceView private void reallocAppsList(int count) { Allocation[] icons = new Allocation[count]; int[] iconIds = new int[count]; - mAllocIconIds = Allocation.createSized(sRS, Element.USER_I32(sRS), count); + mAllocIconIds = Allocation.createSized(sRS, Element.I32(sRS), count); Allocation[] labels = new Allocation[count]; int[] labelIds = new int[count]; - mAllocLabelIds = Allocation.createSized(sRS, Element.USER_I32(sRS), count); + mAllocLabelIds = Allocation.createSized(sRS, Element.I32(sRS), count); - final int oldCount = sRollo.mState.iconCount; + final int oldCount = sRollo.mScript.get_gIconCount(); System.arraycopy(mIcons, 0, icons, 0, oldCount); System.arraycopy(mIconIds, 0, iconIds, 0, oldCount); @@ -1423,7 +1282,7 @@ public class AllApps3D extends RSSurfaceView * Handle the allocations for the new app. Make sure you call saveAppsList when done. */ private void addApp(int index, ApplicationInfo item) { - final int count = mState.iconCount - index; + final int count = mScript.get_gIconCount() - index; final int dest = index + 1; System.arraycopy(mIcons, index, mIcons, dest, count); @@ -1433,14 +1292,15 @@ public class AllApps3D extends RSSurfaceView createAppIconAllocations(index, item); uploadAppIcon(index, item); - sRollo.mState.iconCount++; + + mScript.set_gIconCount(mScript.get_gIconCount() + 1); } /** * Handle the allocations for the removed app. Make sure you call saveAppsList when done. */ private void removeApp(int index) { - final int count = mState.iconCount - index - 1; + final int count = mScript.get_gIconCount() - index - 1; final int src = index + 1; System.arraycopy(mIcons, src, mIcons, index, count); @@ -1448,8 +1308,8 @@ public class AllApps3D extends RSSurfaceView System.arraycopy(mLabels, src, mLabels, index, count); System.arraycopy(mLabelIds, src, mLabelIds, index, count); - sRollo.mState.iconCount--; - final int last = mState.iconCount; + mScript.set_gIconCount(mScript.get_gIconCount() - 1); + final int last = mScript.get_gIconCount(); mIcons[last] = null; mIconIds[last] = 0; @@ -1466,31 +1326,21 @@ public class AllApps3D extends RSSurfaceView mAllocIconIds.data(mIconIds); mAllocLabelIds.data(mLabelIds); - mScript.bindAllocation(mAllocIconIds, Defines.ALLOC_ICON_IDS); - mScript.bindAllocation(mAllocLabelIds, Defines.ALLOC_LABEL_IDS); - - mState.save(); - - // Note: mScript may be null if we haven't initialized it yet. - // In that case, this is a no-op. - if (mInvokeResetWAR != null) { - mInvokeResetWAR.execute(); - } + mScript.bind_gIconIDs(mAllocIconIds); + mScript.bind_gLabelIDs(mAllocLabelIds); } } - void fling() { - mInvokeFling.execute(); + void fling(float pos, float v) { + mScript.invoke_fling(pos, v); } - void move() { - mInvokeMove.execute(); + void move(float pos) { + mScript.invoke_move(pos); } void moveTo(float row) { - mState.targetPos = row; - mState.save(); - mInvokeMoveTo.execute(); + mScript.invoke_moveTo(row); } /** @@ -1520,7 +1370,7 @@ public class AllApps3D extends RSSurfaceView if (mAllApps != null) { mAllApps.mRestoreFocusIndex = index; } - mState.selectedIconIndex = -1; + mScript.set_gSelectedIconIndex(-1); if (mAllApps.mLastSelection == SELECTION_ICONS) { mAllApps.mLastSelection = SELECTION_NONE; } @@ -1529,8 +1379,8 @@ public class AllApps3D extends RSSurfaceView mAllApps.mLastSelection = SELECTION_ICONS; } - int prev = mState.selectedIconIndex; - mState.selectedIconIndex = index; + int prev = mScript.get_gSelectedIconIndex(); + mScript.set_gSelectedIconIndex(index); ApplicationInfo info = appsList.get(index); Bitmap selectionBitmap = mSelectionBitmap; @@ -1539,10 +1389,10 @@ public class AllApps3D extends RSSurfaceView selectionBitmap.getWidth(), selectionBitmap.getHeight(), pressed == SELECTED_PRESSED, info.iconBitmap); - mSelectedIcon = Allocation.createFromBitmap(sRS, selectionBitmap, + Allocation si = Allocation.createFromBitmap(sRS, selectionBitmap, Element.RGBA_8888(sRS), false); - mSelectedIcon.uploadToTexture(0); - mState.selectedIconTexture = mSelectedIcon.getID(); + si.uploadToTexture(0); + mScript.set_gSelectedIconTexture(si); if (prev != index) { if (info.title != null && info.title.length() > 0) { @@ -1557,24 +1407,24 @@ public class AllApps3D extends RSSurfaceView * You need to call save() on mState on your own after calling this. */ void clearSelectedIcon() { - mState.selectedIconIndex = -1; + mScript.set_gSelectedIconIndex(-1); } void setHomeSelected(int mode) { final int prev = mAllApps.mLastSelection; switch (mode) { case SELECTED_NONE: - mState.homeButtonId = mHomeButtonNormal.getID(); + mScript.set_gHomeButton(mHomeButtonNormal); break; case SELECTED_FOCUSED: mAllApps.mLastSelection = SELECTION_HOME; - mState.homeButtonId = mHomeButtonFocused.getID(); + mScript.set_gHomeButton(mHomeButtonFocused); if (prev != SELECTION_HOME) { mAllApps.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); } break; case SELECTED_PRESSED: - mState.homeButtonId = mHomeButtonPressed.getID(); + mScript.set_gHomeButton(mHomeButtonPressed); break; } } @@ -1594,23 +1444,15 @@ public class AllApps3D extends RSSurfaceView Log.d(TAG, "sRollo.mLabelIds.length=" + mLabelIds.length); } Log.d(TAG, "sRollo.mLabelIds=" + Arrays.toString(mLabelIds)); - Log.d(TAG, "sRollo.mState.newPositionX=" + mState.newPositionX); - Log.d(TAG, "sRollo.mState.newTouchDown=" + mState.newTouchDown); - Log.d(TAG, "sRollo.mState.flingVelocity=" + mState.flingVelocity); - Log.d(TAG, "sRollo.mState.iconCount=" + mState.iconCount); - Log.d(TAG, "sRollo.mState.selectedIconIndex=" + mState.selectedIconIndex); - Log.d(TAG, "sRollo.mState.selectedIconTexture=" + mState.selectedIconTexture); - Log.d(TAG, "sRollo.mState.zoomTarget=" + mState.zoomTarget); - Log.d(TAG, "sRollo.mState.homeButtonId=" + mState.homeButtonId); - Log.d(TAG, "sRollo.mState.targetPos=" + mState.targetPos); - Log.d(TAG, "sRollo.mParams.bubbleWidth=" + mParams.bubbleWidth); - Log.d(TAG, "sRollo.mParams.bubbleHeight=" + mParams.bubbleHeight); - Log.d(TAG, "sRollo.mParams.bubbleBitmapWidth=" + mParams.bubbleBitmapWidth); - Log.d(TAG, "sRollo.mParams.bubbleBitmapHeight=" + mParams.bubbleBitmapHeight); - Log.d(TAG, "sRollo.mParams.homeButtonWidth=" + mParams.homeButtonWidth); - Log.d(TAG, "sRollo.mParams.homeButtonHeight=" + mParams.homeButtonHeight); - Log.d(TAG, "sRollo.mParams.homeButtonTextureWidth=" + mParams.homeButtonTextureWidth); - Log.d(TAG, "sRollo.mParams.homeButtonTextureHeight=" + mParams.homeButtonTextureHeight); + //Log.d(TAG, "sRollo.mState.newPositionX=" + mState.newPositionX); + //Log.d(TAG, "sRollo.mState.newTouchDown=" + mState.newTouchDown); + //Log.d(TAG, "sRollo.mState.flingVelocity=" + mState.flingVelocity); + //Log.d(TAG, "sRollo.mState.iconCount=" + mState.iconCount); + //Log.d(TAG, "sRollo.mState.selectedIconIndex=" + mState.selectedIconIndex); + //Log.d(TAG, "sRollo.mState.selectedIconTexture=" + mState.selectedIconTexture); + //Log.d(TAG, "sRollo.mState.zoomTarget=" + mState.zoomTarget); + //Log.d(TAG, "sRollo.mState.homeButtonId=" + mState.homeButtonId); + //Log.d(TAG, "sRollo.mState.targetPos=" + mState.targetPos); } } @@ -1641,5 +1483,3 @@ public class AllApps3D extends RSSurfaceView } } } - - diff --git a/src/com/android/launcher2/AllAppsList.java b/src/com/android/launcher2/AllAppsList.java index 3a5baeaa3..e5d878226 100644 --- a/src/com/android/launcher2/AllAppsList.java +++ b/src/com/android/launcher2/AllAppsList.java @@ -16,17 +16,16 @@ package com.android.launcher2; +import java.util.ArrayList; +import java.util.List; + import android.content.ComponentName; -import android.content.Intent; import android.content.Context; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - /** * Stores the list of all applications for the all apps view. diff --git a/src/com/android/launcher2/AllAppsTabbed.java b/src/com/android/launcher2/AllAppsTabbed.java new file mode 100644 index 000000000..67293d915 --- /dev/null +++ b/src/com/android/launcher2/AllAppsTabbed.java @@ -0,0 +1,124 @@ +/* + * 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.launcher2; + +import com.android.launcher.R; + +import android.content.Context; +import android.content.res.Resources; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.widget.TabHost; + +import java.util.ArrayList; + +/** + * Implements a tabbed version of AllApps2D. + */ +public class AllAppsTabbed extends TabHost implements AllAppsView { + + private static final String TAG = "Launcher.AllAppsTabbed"; + + private AllAppsView mAllApps2D; + + public AllAppsTabbed(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + try { + mAllApps2D = (AllAppsView)findViewById(R.id.all_apps_2d); + if (mAllApps2D == null) throw new Resources.NotFoundException(); + } catch (Resources.NotFoundException e) { + Log.e(TAG, "Can't find necessary layout elements for AllAppsTabbed"); + } + setup(); + + // This lets us share the same view between all tabs + TabContentFactory contentFactory = new TabContentFactory() { + public View createTabContent(String tag) { + return (View)mAllApps2D; + } + }; + + // TODO: Make these tabs show the appropriate content (they're no-ops for now) + addTab(newTabSpec("apps").setIndicator("All").setContent(contentFactory)); + addTab(newTabSpec("apps").setIndicator("Apps").setContent(contentFactory)); + addTab(newTabSpec("apps").setIndicator("Games").setContent(contentFactory)); + addTab(newTabSpec("apps").setIndicator("Downloaded").setContent(contentFactory)); + + setCurrentTab(0); + setVisibility(GONE); + } + + @Override + public void setLauncher(Launcher launcher) { + mAllApps2D.setLauncher(launcher); + } + + @Override + public void setDragController(DragController dragger) { + mAllApps2D.setDragController(dragger); + } + + @Override + public void zoom(float zoom, boolean animate) { + // NOTE: animate parameter is ignored for the TabHost itself + setVisibility((zoom == 0.0f) ? View.GONE : View.VISIBLE); + mAllApps2D.zoom(zoom, animate); + bringChildToFront((View)mAllApps2D); + getParent().bringChildToFront(this); + } + + @Override + public boolean isVisible() { + return mAllApps2D.isVisible(); + } + + @Override + public void setApps(ArrayList<ApplicationInfo> list) { + mAllApps2D.setApps(list); + } + + @Override + public void addApps(ArrayList<ApplicationInfo> list) { + mAllApps2D.addApps(list); + } + + @Override + public void removeApps(ArrayList<ApplicationInfo> list) { + mAllApps2D.removeApps(list); + } + + @Override + public void updateApps(ArrayList<ApplicationInfo> list) { + mAllApps2D.updateApps(list); + } + + @Override + public void dumpState() { + mAllApps2D.dumpState(); + } + + @Override + public void surrender() { + mAllApps2D.surrender(); + } + +} diff --git a/src/com/android/launcher2/ApplicationInfo.java b/src/com/android/launcher2/ApplicationInfo.java index 5bb503780..7b00f4fb1 100644 --- a/src/com/android/launcher2/ApplicationInfo.java +++ b/src/com/android/launcher2/ApplicationInfo.java @@ -16,17 +16,14 @@ package com.android.launcher2; +import java.util.ArrayList; + import android.content.ComponentName; -import android.content.ContentValues; -import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; import android.util.Log; -import java.util.ArrayList; - /** * Represents an app in AllAppsView. */ diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java index 9d39c2ca7..7ca549e84 100644 --- a/src/com/android/launcher2/CellLayout.java +++ b/src/com/android/launcher2/CellLayout.java @@ -16,36 +16,46 @@ package com.android.launcher2; +import com.android.launcher.R; + +import android.app.WallpaperManager; import android.content.Context; -import android.content.res.TypedArray; import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.RectF; -import android.graphics.Canvas; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.ContextMenu; import android.view.MotionEvent; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; -import android.app.WallpaperManager; +import android.view.animation.Animation; +import android.view.animation.LayoutAnimationController; import java.util.ArrayList; - -import com.android.launcher.R; +import java.util.Arrays; public class CellLayout extends ViewGroup { + static final String TAG = "CellLayout"; + private boolean mPortrait; private int mCellWidth; private int mCellHeight; - + private int mLongAxisStartPadding; private int mLongAxisEndPadding; - private int mShortAxisStartPadding; private int mShortAxisEndPadding; + private int mLeftPadding; + private int mRightPadding; + private int mTopPadding; + private int mBottomPadding; + private int mShortAxisCells; private int mLongAxisCells; @@ -53,17 +63,33 @@ public class CellLayout extends ViewGroup { private int mHeightGap; private final Rect mRect = new Rect(); + private final RectF mRectF = new RectF(); private final CellInfo mCellInfo = new CellInfo(); - - int[] mCellXY = new int[2]; + + // This is a temporary variable to prevent having to allocate a new object just to + // return an (x, y) value from helper functions. Do NOT use it to maintain other state. + private final int[] mTmpCellXY = new int[2]; + boolean[][] mOccupied; - private RectF mDragRect = new RectF(); + private final RectF mDragRect = new RectF(); + + // When dragging, used to indicate a vacant drop location + private Drawable mVacantDrawable; + + // When dragging, used to indicate an occupied drop location + private Drawable mOccupiedDrawable; + + // Updated to point to mVacantDrawable or mOccupiedDrawable, as appropriate + private Drawable mDragRectDrawable; + + // When a drag operation is in progress, holds the nearest cell to the touch point + private final int[] mDragCell = new int[2]; private boolean mDirtyTag; private boolean mLastDownOnOccupiedCell = false; - - private final WallpaperManager mWallpaperManager; + + private final WallpaperManager mWallpaperManager; public CellLayout(Context context) { this(context, null); @@ -75,20 +101,27 @@ public class CellLayout extends ViewGroup { public CellLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + + // A ViewGroup usually does not draw, but CellLayout needs to draw a rectangle to show + // the user where a dragged item will land when dropped. + setWillNotDraw(false); + mVacantDrawable = getResources().getDrawable(R.drawable.rounded_rect_green); + mOccupiedDrawable = getResources().getDrawable(R.drawable.rounded_rect_red); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0); mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10); mCellHeight = a.getDimensionPixelSize(R.styleable.CellLayout_cellHeight, 10); - - mLongAxisStartPadding = + + mLongAxisStartPadding = a.getDimensionPixelSize(R.styleable.CellLayout_longAxisStartPadding, 10); - mLongAxisEndPadding = + mLongAxisEndPadding = a.getDimensionPixelSize(R.styleable.CellLayout_longAxisEndPadding, 10); mShortAxisStartPadding = a.getDimensionPixelSize(R.styleable.CellLayout_shortAxisStartPadding, 10); - mShortAxisEndPadding = + mShortAxisEndPadding = a.getDimensionPixelSize(R.styleable.CellLayout_shortAxisEndPadding, 10); - + mShortAxisCells = a.getInt(R.styleable.CellLayout_shortAxisCells, 4); mLongAxisCells = a.getInt(R.styleable.CellLayout_longAxisCells, 4); @@ -96,14 +129,6 @@ public class CellLayout extends ViewGroup { setAlwaysDrawnWithCacheEnabled(false); - if (mOccupied == null) { - if (mPortrait) { - mOccupied = new boolean[mShortAxisCells][mLongAxisCells]; - } else { - mOccupied = new boolean[mLongAxisCells][mShortAxisCells]; - } - } - mWallpaperManager = WallpaperManager.getInstance(getContext()); } @@ -113,6 +138,18 @@ public class CellLayout extends ViewGroup { } @Override + protected void onDraw(Canvas canvas) { + if (!mDragRect.isEmpty()) { + mDragRectDrawable.setBounds( + (int)mDragRect.left, + (int)mDragRect.top, + (int)mDragRect.right, + (int)mDragRect.bottom); + mDragRectDrawable.draw(canvas); + } + } + + @Override public void cancelLongPress() { super.cancelLongPress(); @@ -132,14 +169,24 @@ public class CellLayout extends ViewGroup { return mPortrait ? mLongAxisCells : mShortAxisCells; } - @Override - public void addView(View child, int index, ViewGroup.LayoutParams params) { + // Takes canonical layout parameters + public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params) { + final LayoutParams lp = params; + // Generate an id for each view, this assumes we have at most 256x256 cells // per workspace screen - final LayoutParams cellParams = (LayoutParams) params; - cellParams.regenerateId = true; + if (lp.cellX >= 0 && lp.cellX <= getCountX() - 1 && lp.cellY >= 0 && lp.cellY <= getCountY() - 1) { + // If the horizontal or vertical span is set to -1, it is taken to + // mean that it spans the extent of the CellLayout + if (lp.cellHSpan < 0) lp.cellHSpan = getCountX(); + if (lp.cellVSpan < 0) lp.cellVSpan = getCountY(); - super.addView(child, index, params); + child.setId(childId); + + addView(child, index, lp); + return true; + } + return false; } @Override @@ -158,67 +205,73 @@ public class CellLayout extends ViewGroup { mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this); } + public void setTagToCellInfoForPoint(int touchX, int touchY) { + final CellInfo cellInfo = mCellInfo; + final Rect frame = mRect; + final int x = touchX + mScrollX; + final int y = touchY + mScrollY; + final int count = getChildCount(); + + boolean found = false; + for (int i = count - 1; i >= 0; i--) { + final View child = getChildAt(i); + + if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) { + child.getHitRect(frame); + if (frame.contains(x, y)) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + cellInfo.cell = child; + cellInfo.cellX = lp.cellX; + cellInfo.cellY = lp.cellY; + cellInfo.spanX = lp.cellHSpan; + cellInfo.spanY = lp.cellVSpan; + cellInfo.valid = true; + found = true; + mDirtyTag = false; + break; + } + } + } + + mLastDownOnOccupiedCell = found; + + if (!found) { + final int cellXY[] = mTmpCellXY; + pointToCellExact(x, y, cellXY); + + final boolean portrait = mPortrait; + final int xCount = portrait ? mShortAxisCells : mLongAxisCells; + final int yCount = portrait ? mLongAxisCells : mShortAxisCells; + + final boolean[][] occupied = mOccupied; + findOccupiedCells(xCount, yCount, occupied, null, true); + + cellInfo.cell = null; + cellInfo.cellX = cellXY[0]; + cellInfo.cellY = cellXY[1]; + cellInfo.spanX = 1; + cellInfo.spanY = 1; + cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount && + cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]]; + + // Instead of finding the interesting vacant cells here, wait until a + // caller invokes getTag() to retrieve the result. Finding the vacant + // cells is a bit expensive and can generate many new objects, it's + // therefore better to defer it until we know we actually need it. + + mDirtyTag = true; + } + setTag(cellInfo); + } + + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { final int action = ev.getAction(); final CellInfo cellInfo = mCellInfo; if (action == MotionEvent.ACTION_DOWN) { - final Rect frame = mRect; - final int x = (int) ev.getX() + mScrollX; - final int y = (int) ev.getY() + mScrollY; - final int count = getChildCount(); - - boolean found = false; - for (int i = count - 1; i >= 0; i--) { - final View child = getChildAt(i); - - if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) { - child.getHitRect(frame); - if (frame.contains(x, y)) { - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - cellInfo.cell = child; - cellInfo.cellX = lp.cellX; - cellInfo.cellY = lp.cellY; - cellInfo.spanX = lp.cellHSpan; - cellInfo.spanY = lp.cellVSpan; - cellInfo.valid = true; - found = true; - mDirtyTag = false; - break; - } - } - } - - mLastDownOnOccupiedCell = found; - - if (!found) { - int cellXY[] = mCellXY; - pointToCellExact(x, y, cellXY); - - final boolean portrait = mPortrait; - final int xCount = portrait ? mShortAxisCells : mLongAxisCells; - final int yCount = portrait ? mLongAxisCells : mShortAxisCells; - - final boolean[][] occupied = mOccupied; - findOccupiedCells(xCount, yCount, occupied, null); - - cellInfo.cell = null; - cellInfo.cellX = cellXY[0]; - cellInfo.cellY = cellXY[1]; - cellInfo.spanX = 1; - cellInfo.spanY = 1; - cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount && - cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]]; - - // Instead of finding the interesting vacant cells here, wait until a - // caller invokes getTag() to retrieve the result. Finding the vacant - // cells is a bit expensive and can generate many new objects, it's - // therefore better to defer it until we know we actually need it. - - mDirtyTag = true; - } - setTag(cellInfo); + setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY()); } else if (action == MotionEvent.ACTION_UP) { cellInfo.cell = null; cellInfo.cellX = -1; @@ -242,7 +295,7 @@ public class CellLayout extends ViewGroup { final int yCount = portrait ? mLongAxisCells : mShortAxisCells; final boolean[][] occupied = mOccupied; - findOccupiedCells(xCount, yCount, occupied, null); + findOccupiedCells(xCount, yCount, occupied, null, true); findIntersectingVacantCells(info, info.cellX, info.cellY, xCount, yCount, occupied); @@ -251,8 +304,8 @@ public class CellLayout extends ViewGroup { return info; } - private static void findIntersectingVacantCells(CellInfo cellInfo, int x, int y, - int xCount, int yCount, boolean[][] occupied) { + private static void findIntersectingVacantCells(CellInfo cellInfo, int x, + int y, int xCount, int yCount, boolean[][] occupied) { cellInfo.maxVacantSpanX = Integer.MIN_VALUE; cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE; @@ -324,6 +377,9 @@ public class CellLayout extends ViewGroup { cellInfo.vacantCells.add(cell); } + /** + * Check if the column 'x' is empty from rows 'top' to 'bottom', inclusive. + */ private static boolean isColumnEmpty(int x, int top, int bottom, boolean[][] occupied) { for (int y = top; y <= bottom; y++) { if (occupied[x][y]) { @@ -333,6 +389,9 @@ public class CellLayout extends ViewGroup { return true; } + /** + * Check if the row 'y' is empty from columns 'left' to 'right', inclusive. + */ private static boolean isRowEmpty(int y, int left, int right, boolean[][] occupied) { for (int x = left; x <= right; x++) { if (occupied[x][y]) { @@ -356,7 +415,7 @@ public class CellLayout extends ViewGroup { } } } else { - findOccupiedCells(xCount, yCount, occupied, ignoreView); + findOccupiedCells(xCount, yCount, occupied, ignoreView, true); } CellInfo cellInfo = new CellInfo(); @@ -387,21 +446,21 @@ public class CellLayout extends ViewGroup { // Assume the caller will perform their own cell searching, otherwise we // risk causing an unnecessary rebuild after findCellForSpan() - + return cellInfo; } /** - * Given a point, return the cell that strictly encloses that point + * Given a point, return the cell that strictly encloses that point * @param x X coordinate of the point * @param y Y coordinate of the point * @param result Array of 2 ints to hold the x and y coordinate of the cell */ void pointToCellExact(int x, int y, int[] result) { final boolean portrait = mPortrait; - - final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding; - final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding; + + final int hStartPadding = getLeftPadding(); + final int vStartPadding = getTopPadding(); result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap); result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap); @@ -414,7 +473,7 @@ public class CellLayout extends ViewGroup { if (result[1] < 0) result[1] = 0; if (result[1] >= yAxis) result[1] = yAxis - 1; } - + /** * Given a point, return the cell that most closely encloses that point * @param x X coordinate of the point @@ -427,18 +486,15 @@ public class CellLayout extends ViewGroup { /** * Given a cell coordinate, return the point that represents the upper left corner of that cell - * - * @param cellX X coordinate of the cell + * + * @param cellX X coordinate of the cell * @param cellY Y coordinate of the cell - * + * * @param result Array of 2 ints to hold the x and y coordinate of the point */ void cellToPoint(int cellX, int cellY, int[] result) { - final boolean portrait = mPortrait; - - final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding; - final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding; - + final int hStartPadding = getLeftPadding(); + final int vStartPadding = getTopPadding(); result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap); result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap); @@ -453,97 +509,117 @@ public class CellLayout extends ViewGroup { } int getLeftPadding() { - return mPortrait ? mShortAxisStartPadding : mLongAxisStartPadding; + return mLeftPadding; } int getTopPadding() { - return mPortrait ? mLongAxisStartPadding : mShortAxisStartPadding; + return mTopPadding; } int getRightPadding() { - return mPortrait ? mShortAxisEndPadding : mLongAxisEndPadding; + return mRightPadding; } int getBottomPadding() { - return mPortrait ? mLongAxisEndPadding : mShortAxisEndPadding; + return mBottomPadding; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO: currently ignoring padding - + int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); - int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); - + int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); + int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); - + if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) { throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions"); } final int shortAxisCells = mShortAxisCells; final int longAxisCells = mLongAxisCells; - final int longAxisStartPadding = mLongAxisStartPadding; - final int longAxisEndPadding = mLongAxisEndPadding; - final int shortAxisStartPadding = mShortAxisStartPadding; - final int shortAxisEndPadding = mShortAxisEndPadding; final int cellWidth = mCellWidth; final int cellHeight = mCellHeight; - mPortrait = heightSpecSize > widthSpecSize; + boolean portrait = heightSpecSize > widthSpecSize; + if (portrait != mPortrait || mOccupied == null) { + if (portrait) { + mOccupied = new boolean[mShortAxisCells][mLongAxisCells]; + } else { + mOccupied = new boolean[mLongAxisCells][mShortAxisCells]; + } + } + mPortrait = portrait; int numShortGaps = shortAxisCells - 1; int numLongGaps = longAxisCells - 1; if (mPortrait) { - int vSpaceLeft = heightSpecSize - longAxisStartPadding - longAxisEndPadding - - (cellHeight * longAxisCells); + int vSpaceLeft = heightSpecSize - mLongAxisStartPadding + - mLongAxisEndPadding - (cellHeight * longAxisCells); mHeightGap = vSpaceLeft / numLongGaps; - int hSpaceLeft = widthSpecSize - shortAxisStartPadding - shortAxisEndPadding - - (cellWidth * shortAxisCells); + int hSpaceLeft = widthSpecSize - mShortAxisStartPadding + - mShortAxisEndPadding - (cellWidth * shortAxisCells); if (numShortGaps > 0) { mWidthGap = hSpaceLeft / numShortGaps; } else { mWidthGap = 0; } + + if (LauncherApplication.isInPlaceRotationEnabled()) { + mWidthGap = mHeightGap = Math.min(mHeightGap, mWidthGap); + mLeftPadding = mRightPadding = (widthSpecSize - cellWidth + * shortAxisCells - (shortAxisCells - 1) * mWidthGap) / 2; + mTopPadding = mBottomPadding = (heightSpecSize - cellHeight + * longAxisCells - (longAxisCells - 1) * mHeightGap) / 2; + } else { + mLeftPadding = mShortAxisStartPadding; + mRightPadding = mShortAxisEndPadding; + mTopPadding = mLongAxisStartPadding; + mBottomPadding = mLongAxisEndPadding; + } } else { - int hSpaceLeft = widthSpecSize - longAxisStartPadding - longAxisEndPadding - - (cellWidth * longAxisCells); + int hSpaceLeft = widthSpecSize - mLongAxisStartPadding + - mLongAxisEndPadding - (cellWidth * longAxisCells); mWidthGap = hSpaceLeft / numLongGaps; - int vSpaceLeft = heightSpecSize - shortAxisStartPadding - shortAxisEndPadding - - (cellHeight * shortAxisCells); + int vSpaceLeft = heightSpecSize - mShortAxisStartPadding + - mShortAxisEndPadding - (cellHeight * shortAxisCells); if (numShortGaps > 0) { mHeightGap = vSpaceLeft / numShortGaps; } else { mHeightGap = 0; } + + if (LauncherApplication.isScreenXLarge()) { + mWidthGap = mHeightGap = Math.min(mHeightGap, mWidthGap); + mLeftPadding = mRightPadding = (widthSpecSize - cellWidth + * longAxisCells - (longAxisCells - 1) * mWidthGap) / 2 ; + mTopPadding = mBottomPadding = (heightSpecSize - cellHeight + * shortAxisCells - (shortAxisCells - 1) * mHeightGap) / 2; + } else { + mLeftPadding = mLongAxisStartPadding; + mRightPadding = mLongAxisEndPadding; + mTopPadding = mShortAxisStartPadding; + mBottomPadding = mShortAxisEndPadding; + } } - int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); LayoutParams lp = (LayoutParams) child.getLayoutParams(); + lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, + mLeftPadding, mTopPadding); - if (mPortrait) { - lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, shortAxisStartPadding, - longAxisStartPadding); - } else { - lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, longAxisStartPadding, - shortAxisStartPadding); - } - - if (lp.regenerateId) { - child.setId(((getId() & 0xFF) << 16) | (lp.cellX & 0xFF) << 8 | (lp.cellY & 0xFF)); - lp.regenerateId = false; - } + int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, + MeasureSpec.EXACTLY); + int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height, + MeasureSpec.EXACTLY); - int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY); - int childheightMeasureSpec = - MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY); child.measure(childWidthMeasureSpec, childheightMeasureSpec); } @@ -567,7 +643,7 @@ public class CellLayout extends ViewGroup { if (lp.dropped) { lp.dropped = false; - final int[] cellXY = mCellXY; + final int[] cellXY = mTmpCellXY; getLocationOnScreen(cellXY); mWallpaperManager.sendWallpaperCommand(getWindowToken(), "android.home.drop", cellXY[0] + childLeft + lp.width / 2, @@ -593,10 +669,110 @@ public class CellLayout extends ViewGroup { super.setChildrenDrawnWithCacheEnabled(enabled); } + private boolean isVacant(int originX, int originY, int spanX, int spanY) { + for (int i = 0; i < spanY; i++) { + if (!isRowEmpty(originY + i, originX, originX + spanX - 1, mOccupied)) { + return false; + } + } + return true; + } + + public View getChildAt(int x, int y) { + final int count = getChildCount(); + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + + if ((lp.cellX <= x) && (x < lp.cellX + lp.cellHSpan) && + (lp.cellY <= y) && (y < lp.cellY + lp.cellHSpan)) { + return child; + } + } + return null; + } + + /** + * Estimate the size that a child with the given dimensions will take in the layout. + */ + void estimateChildSize(int minWidth, int minHeight, int[] result) { + // Assuming it's placed at 0, 0, find where the bottom right cell will land + rectToCell(minWidth, minHeight, result); + + // Then figure out the rect it will occupy + cellToRect(0, 0, result[0], result[1], mRectF); + result[0] = (int)mRectF.width(); + result[1] = (int)mRectF.height(); + } + + /** + * Estimate where the top left cell of the dragged item will land if it is dropped. + * + * @param originX The X value of the top left corner of the item + * @param originY The Y value of the top left corner of the item + * @param spanX The number of horizontal cells that the item spans + * @param spanY The number of vertical cells that the item spans + * @param result The estimated drop cell X and Y. + */ + void estimateDropCell(int originX, int originY, int spanX, int spanY, int[] result) { + final int countX = getCountX(); + final int countY = getCountY(); + + pointToCellRounded(originX, originY, result); + + // If the item isn't fully on this screen, snap to the edges + int rightOverhang = result[0] + spanX - countX; + if (rightOverhang > 0) { + result[0] -= rightOverhang; // Snap to right + } + result[0] = Math.max(0, result[0]); // Snap to left + int bottomOverhang = result[1] + spanY - countY; + if (bottomOverhang > 0) { + result[1] -= bottomOverhang; // Snap to bottom + } + result[1] = Math.max(0, result[1]); // Snap to top + } + + void visualizeDropLocation(View view, int originX, int originY, int spanX, int spanY) { + final int[] originCell = mDragCell; + final int[] cellXY = mTmpCellXY; + estimateDropCell(originX, originY, spanX, spanY, cellXY); + + // Only recalculate the bounding rect when necessary + if (!Arrays.equals(cellXY, originCell)) { + originCell[0] = cellXY[0]; + originCell[1] = cellXY[1]; + + // Find the top left corner of the rect the object will occupy + final int[] topLeft = mTmpCellXY; + cellToPoint(originCell[0], originCell[1], topLeft); + final int left = topLeft[0]; + final int top = topLeft[1]; + + // Now find the bottom right + final int[] bottomRight = mTmpCellXY; + cellToPoint(originCell[0] + spanX - 1, originCell[1] + spanY - 1, bottomRight); + bottomRight[0] += mCellWidth; + bottomRight[1] += mCellHeight; + + final int countX = mPortrait ? mShortAxisCells : mLongAxisCells; + final int countY = mPortrait ? mLongAxisCells : mShortAxisCells; + // TODO: It's not necessary to do this every time, but it's not especially expensive + findOccupiedCells(countX, countY, mOccupied, view, false); + + boolean vacant = isVacant(originCell[0], originCell[1], spanX, spanY); + mDragRectDrawable = vacant ? mVacantDrawable : mOccupiedDrawable; + + // mDragRect will be rendered in onDraw() + mDragRect.set(left, top, bottomRight[0], bottomRight[1]); + invalidate(); + } + } + /** * Find a vacant area that will fit the given bounds nearest the requested * cell location. Uses Euclidean distance to score multiple vacant areas. - * + * * @param pixelX The X location at which you want to search for a vacant area. * @param pixelY The Y location at which you want to search for a vacant area. * @param spanX Horizontal span of the object. @@ -608,12 +784,11 @@ public class CellLayout extends ViewGroup { */ int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY, CellInfo vacantCells, int[] recycle) { - + // Keep track of best-scoring drop area final int[] bestXY = recycle != null ? recycle : new int[2]; - final int[] cellXY = mCellXY; double bestDistance = Double.MAX_VALUE; - + // Bail early if vacant cells aren't valid if (!vacantCells.valid) { return null; @@ -623,17 +798,18 @@ public class CellLayout extends ViewGroup { final int size = vacantCells.vacantCells.size(); for (int i = 0; i < size; i++) { final CellInfo.VacantCell cell = vacantCells.vacantCells.get(i); - + // Reject if vacant cell isn't our exact size if (cell.spanX != spanX || cell.spanY != spanY) { continue; } - - // Score is center distance from requested pixel + + // Score is distance from requested pixel to the top left of each cell + final int[] cellXY = mTmpCellXY; cellToPoint(cell.cellX, cell.cellY, cellXY); - - double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2) + - Math.pow(cellXY[1] - pixelY, 2)); + + double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2) + + Math.pow(cellXY[1] - pixelY, 2)); if (distance <= bestDistance) { bestDistance = distance; bestXY[0] = cell.cellX; @@ -641,44 +817,52 @@ public class CellLayout extends ViewGroup { } } - // Return null if no suitable location found + // Return null if no suitable location found if (bestDistance < Double.MAX_VALUE) { return bestXY; } else { return null; } } - + /** - * Drop a child at the specified position + * Called when a drag and drop operation has finished (successfully or not). + */ + void onDragComplete() { + // Invalidate the drag data + mDragCell[0] = -1; + mDragCell[1] = -1; + + mDragRect.setEmpty(); + invalidate(); + } + + /** + * Mark a child as having been dropped. * * @param child The child that is being dropped - * @param targetXY Destination area to move to */ - void onDropChild(View child, int[] targetXY) { + void onDropChild(View child) { if (child != null) { LayoutParams lp = (LayoutParams) child.getLayoutParams(); - lp.cellX = targetXY[0]; - lp.cellY = targetXY[1]; lp.isDragging = false; lp.dropped = true; mDragRect.setEmpty(); child.requestLayout(); - invalidate(); } + onDragComplete(); } void onDropAborted(View child) { if (child != null) { ((LayoutParams) child.getLayoutParams()).isDragging = false; - invalidate(); } - mDragRect.setEmpty(); + onDragComplete(); } /** * Start dragging the specified child - * + * * @param child The child that is being dragged */ void onDragChild(View child) { @@ -686,58 +870,44 @@ public class CellLayout extends ViewGroup { lp.isDragging = true; mDragRect.setEmpty(); } - - /** - * Drag a child over the specified position - * - * @param child The child that is being dropped - * @param cellX The child's new x cell location - * @param cellY The child's new y cell location - */ - void onDragOverChild(View child, int cellX, int cellY) { - int[] cellXY = mCellXY; - pointToCellRounded(cellX, cellY, cellXY); - LayoutParams lp = (LayoutParams) child.getLayoutParams(); - cellToRect(cellXY[0], cellXY[1], lp.cellHSpan, lp.cellVSpan, mDragRect); - invalidate(); - } - + /** * Computes a bounding rectangle for a range of cells - * + * * @param cellX X coordinate of upper left corner expressed as a cell position * @param cellY Y coordinate of upper left corner expressed as a cell position - * @param cellHSpan Width in cells + * @param cellHSpan Width in cells * @param cellVSpan Height in cells - * @param dragRect Rectnagle into which to put the results + * @param resultRect Rect into which to put the results */ - public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF dragRect) { + public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF resultRect) { final boolean portrait = mPortrait; final int cellWidth = mCellWidth; final int cellHeight = mCellHeight; final int widthGap = mWidthGap; final int heightGap = mHeightGap; - - final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding; - final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding; - + + final int hStartPadding = getLeftPadding(); + final int vStartPadding = getTopPadding(); + int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap); int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap); int x = hStartPadding + cellX * (cellWidth + widthGap); int y = vStartPadding + cellY * (cellHeight + heightGap); - - dragRect.set(x, y, x + width, y + height); + + resultRect.set(x, y, x + width, y + height); } - + /** - * Computes the required horizontal and vertical cell spans to always + * Computes the required horizontal and vertical cell spans to always * fit the given rectangle. - * + * * @param width Width in pixels * @param height Height in pixels + * @param result An array of length 2 in which to store the result (may be null). */ - public int[] rectToCell(int width, int height) { + public int[] rectToCell(int width, int height, int[] result) { // Always assume we're working with the smallest span to make sure we // reserve enough space in both orientations. final Resources resources = getResources(); @@ -749,7 +919,12 @@ public class CellLayout extends ViewGroup { int spanX = (width + smallerSize) / smallerSize; int spanY = (height + smallerSize) / smallerSize; - return new int[] { spanX, spanY }; + if (result == null) { + return new int[] { spanX, spanY }; + } + result[0] = spanX; + result[1] = spanY; + return result; } /** @@ -758,7 +933,7 @@ public class CellLayout extends ViewGroup { * @param vacant Holds the x and y coordinate of the vacant cell * @param spanX Horizontal cell span. * @param spanY Vertical cell span. - * + * * @return True if a vacant cell was found */ public boolean getVacantCell(int[] vacant, int spanX, int spanY) { @@ -767,7 +942,7 @@ public class CellLayout extends ViewGroup { final int yCount = portrait ? mLongAxisCells : mShortAxisCells; final boolean[][] occupied = mOccupied; - findOccupiedCells(xCount, yCount, occupied, null); + findOccupiedCells(xCount, yCount, occupied, null, true); return findVacantCell(vacant, spanX, spanY, xCount, yCount, occupied); } @@ -796,13 +971,16 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { return false; } - boolean[] getOccupiedCells() { + /** + * Update the array of occupied cells (mOccupied), and return a flattened copy of the array. + */ + boolean[] getOccupiedCellsFlattened() { final boolean portrait = mPortrait; final int xCount = portrait ? mShortAxisCells : mLongAxisCells; final int yCount = portrait ? mLongAxisCells : mShortAxisCells; final boolean[][] occupied = mOccupied; - findOccupiedCells(xCount, yCount, occupied, null); + findOccupiedCells(xCount, yCount, occupied, null, true); final boolean[] flat = new boolean[xCount * yCount]; for (int y = 0; y < yCount; y++) { @@ -814,7 +992,14 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { return flat; } - private void findOccupiedCells(int xCount, int yCount, boolean[][] occupied, View ignoreView) { + /** + * Update the array of occupied cells. + * @param ignoreView If non-null, the space occupied by this View is treated as vacant + * @param ignoreFolders If true, a cell occupied by a Folder is treated as vacant + */ + private void findOccupiedCells( + int xCount, int yCount, boolean[][] occupied, View ignoreView, boolean ignoreFolders) { + for (int x = 0; x < xCount; x++) { for (int y = 0; y < yCount; y++) { occupied[x][y] = false; @@ -824,7 +1009,7 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); - if (child instanceof Folder || child.equals(ignoreView)) { + if ((ignoreFolders && child instanceof Folder) || child.equals(ignoreView)) { continue; } LayoutParams lp = (LayoutParams) child.getLayoutParams(); @@ -852,6 +1037,17 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { return new CellLayout.LayoutParams(p); } + public static class CellLayoutAnimationController extends LayoutAnimationController { + public CellLayoutAnimationController(Animation animation, float delay) { + super(animation, delay); + } + + @Override + protected long getDelayForView(View view) { + return (int) (Math.random() * 150); + } + } + public static class LayoutParams extends ViewGroup.MarginLayoutParams { /** * Horizontal location of the item in the grid. @@ -876,7 +1072,7 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { */ @ViewDebug.ExportedProperty public int cellVSpan; - + /** * Is this item currently being dragged */ @@ -889,8 +1085,6 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { @ViewDebug.ExportedProperty int y; - boolean regenerateId; - boolean dropped; public LayoutParams(Context c, AttributeSet attrs) { @@ -904,7 +1098,15 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { cellHSpan = 1; cellVSpan = 1; } - + + public LayoutParams(LayoutParams source) { + super(source); + this.cellX = source.cellX; + this.cellY = source.cellY; + this.cellHSpan = source.cellHSpan; + this.cellVSpan = source.cellVSpan; + } + public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) { super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); this.cellX = cellX; @@ -915,12 +1117,12 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap, int hStartPadding, int vStartPadding) { - + final int myCellHSpan = cellHSpan; final int myCellVSpan = cellVSpan; final int myCellX = cellX; final int myCellY = cellY; - + width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) - leftMargin - rightMargin; height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) - @@ -929,14 +1131,18 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin; y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin; } + + public String toString() { + return "(" + this.cellX + ", " + this.cellY + ")"; + } } static final class CellInfo implements ContextMenu.ContextMenuInfo { /** - * See View.AttachInfo.InvalidateInfo for futher explanations about - * the recycling mechanism. In this case, we recycle the vacant cells - * instances because up to several hundreds can be instanciated when - * the user long presses an empty cell. + * See View.AttachInfo.InvalidateInfo for futher explanations about the + * recycling mechanism. In this case, we recycle the vacant cells + * instances because up to several hundreds can be instanciated when the + * user long presses an empty cell. */ static final class VacantCell { int cellX; @@ -947,7 +1153,7 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { // We can create up to 523 vacant cells on a 4x4 grid, 100 seems // like a reasonable compromise given the size of a VacantCell and // the fact that the user is not likely to touch an empty 4x4 grid - // very often + // very often private static final int POOL_LIMIT = 100; private static final Object sLock = new Object(); @@ -982,8 +1188,8 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { @Override public String toString() { - return "VacantCell[x=" + cellX + ", y=" + cellY + ", spanX=" + spanX + - ", spanY=" + spanY + "]"; + return "VacantCell[x=" + cellX + ", y=" + cellY + ", spanX=" + + spanX + ", spanY=" + spanY + "]"; } } @@ -1006,7 +1212,9 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { final ArrayList<VacantCell> list = vacantCells; final int count = list.size(); - for (int i = 0; i < count; i++) list.get(i).release(); + for (int i = 0; i < count; i++) { + list.get(i).release(); + } list.clear(); } @@ -1052,7 +1260,9 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { boolean found = false; - if (this.spanX >= spanX && this.spanY >= spanY) { + // return the span represented by the CellInfo only there is no view there + // (this.cell == null) and there is enough space + if (this.cell == null && this.spanX >= spanX && this.spanY >= spanY) { cellXY[0] = cellX; cellXY[1] = cellY; found = true; @@ -1080,15 +1290,17 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { } } - if (clear) clearVacantCells(); + if (clear) { + clearVacantCells(); + } return found; } @Override public String toString() { - return "Cell[view=" + (cell == null ? "null" : cell.getClass()) + ", x=" + cellX + - ", y=" + cellY + "]"; + return "Cell[view=" + (cell == null ? "null" : cell.getClass()) + + ", x=" + cellX + ", y=" + cellY + "]"; } } @@ -1096,5 +1308,3 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { return mLastDownOnOccupiedCell; } } - - diff --git a/src/com/android/launcher2/DeferredHandler.java b/src/com/android/launcher2/DeferredHandler.java index 7801642d2..0323c7f4d 100644 --- a/src/com/android/launcher2/DeferredHandler.java +++ b/src/com/android/launcher2/DeferredHandler.java @@ -16,13 +16,12 @@ package com.android.launcher2; +import java.util.LinkedList; + import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.MessageQueue; -import android.util.Log; - -import java.util.LinkedList; /** * Queue of things to run on a looper thread. Items posted with {@link #post} will not diff --git a/src/com/android/launcher2/DeleteZone.java b/src/com/android/launcher2/DeleteZone.java index 3a6c63d8b..b11b1dde6 100644 --- a/src/com/android/launcher2/DeleteZone.java +++ b/src/com/android/launcher2/DeleteZone.java @@ -254,4 +254,10 @@ public class DeleteZone extends ImageView implements DropTarget, DragController. return false; } } + + @Override + public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset, + DragView dragView, Object dragInfo) { + return null; + } } diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java index b4f972bb2..0130ba93c 100644 --- a/src/com/android/launcher2/DragController.java +++ b/src/com/android/launcher2/DragController.java @@ -395,6 +395,13 @@ public class DragController { final int[] coordinates = mCoordinatesTemp; DropTarget dropTarget = findDropTarget(screenX, screenY, coordinates); if (dropTarget != null) { + DropTarget delegate = dropTarget.getDropTargetDelegate( + mDragSource, coordinates[0], coordinates[1], + (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo); + if (delegate != null) { + dropTarget = delegate; + } + if (mLastDropTarget == dropTarget) { dropTarget.onDragOver(mDragSource, coordinates[0], coordinates[1], (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo); @@ -482,13 +489,25 @@ public class DragController { final ArrayList<DropTarget> dropTargets = mDropTargets; final int count = dropTargets.size(); for (int i=count-1; i>=0; i--) { - final DropTarget target = dropTargets.get(i); + DropTarget target = dropTargets.get(i); target.getHitRect(r); + + // Convert the hit rect to screen coordinates target.getLocationOnScreen(dropCoordinates); r.offset(dropCoordinates[0] - target.getLeft(), dropCoordinates[1] - target.getTop()); + if (r.contains(x, y)) { + DropTarget delegate = target.getDropTargetDelegate(mDragSource, + x, y, (int)mTouchOffsetX, (int)mTouchOffsetY, mDragView, mDragInfo); + if (delegate != null) { + target = delegate; + target.getLocationOnScreen(dropCoordinates); + } + + // Make dropCoordinates relative to the DropTarget dropCoordinates[0] = x - dropCoordinates[0]; dropCoordinates[1] = y - dropCoordinates[1]; + return target; } } diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java index c68320764..ab71670a2 100644 --- a/src/com/android/launcher2/DragLayer.java +++ b/src/com/android/launcher2/DragLayer.java @@ -18,13 +18,13 @@ package com.android.launcher2; import android.content.Context; import android.util.AttributeSet; -import android.view.MotionEvent; import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.View; import android.widget.FrameLayout; /** - * A ViewGroup that coordinated dragging across its dscendants + * A ViewGroup that coordinates dragging across its descendants */ public class DragLayer extends FrameLayout { DragController mDragController; @@ -33,7 +33,7 @@ public class DragLayer extends FrameLayout { * Used to create a new DragLayer from XML. * * @param context The application's context. - * @param attrs The attribtues set containing the Workspace's customization values. + * @param attrs The attributes set containing the Workspace's customization values. */ public DragLayer(Context context, AttributeSet attrs) { super(context, attrs); diff --git a/src/com/android/launcher2/DragView.java b/src/com/android/launcher2/DragView.java index 248712ed3..bae592cb0 100644 --- a/src/com/android/launcher2/DragView.java +++ b/src/com/android/launcher2/DragView.java @@ -18,20 +18,15 @@ package com.android.launcher2; import android.content.Context; -import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; -import android.graphics.Point; import android.os.IBinder; -import android.util.AttributeSet; -import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; -import android.view.KeyEvent; import android.view.WindowManager; import android.view.WindowManagerImpl; diff --git a/src/com/android/launcher2/DropTarget.java b/src/com/android/launcher2/DropTarget.java index 72eb33094..7e542312c 100644 --- a/src/com/android/launcher2/DropTarget.java +++ b/src/com/android/launcher2/DropTarget.java @@ -51,6 +51,26 @@ public interface DropTarget { DragView dragView, Object dragInfo); /** + * Allows a DropTarget to delegate drag and drop events to another object. + * + * Most subclasses will should just return null from this method. + * + * @param source DragSource where the drag started + * @param x X coordinate of the drop location + * @param y Y coordinate of the drop location + * @param xOffset Horizontal offset with the object being dragged where the original + * touch happened + * @param yOffset Vertical offset with the object being dragged where the original + * touch happened + * @param dragView The DragView that's being dragged around on screen. + * @param dragInfo Data associated with the object being dragged + * + * @return The DropTarget to delegate to, or null to not delegate to another object. + */ + DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset, + DragView dragView, Object dragInfo); + + /** * Check if a drop action can occur at, or near, the requested location. * This may be called repeatedly during a drag, so any calls should return * quickly. diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java index 7ff83284e..4d7c6668c 100644 --- a/src/com/android/launcher2/Folder.java +++ b/src/com/android/launcher2/Folder.java @@ -21,11 +21,11 @@ import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.View.OnClickListener; +import android.widget.AbsListView; import android.widget.AdapterView; +import android.widget.BaseAdapter; import android.widget.Button; import android.widget.LinearLayout; -import android.widget.AbsListView; -import android.widget.BaseAdapter; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemLongClickListener; diff --git a/src/com/android/launcher2/FolderChooser.java b/src/com/android/launcher2/FolderChooser.java new file mode 100644 index 000000000..b152ad5f5 --- /dev/null +++ b/src/com/android/launcher2/FolderChooser.java @@ -0,0 +1,37 @@ +package com.android.launcher2; + +import com.android.launcher.R; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.provider.LiveFolders; +import android.util.AttributeSet; +import android.view.View; +import android.widget.AdapterView; + +public class FolderChooser extends HomeCustomizationItemGallery { + + public FolderChooser(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { + // todo: this code sorta overlaps with other places + ResolveInfo info = (ResolveInfo)getAdapter().getItem(position); + mLauncher.prepareAddItemFromHomeCustomizationDrawer(); + + Intent createFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER); + if (info.labelRes == R.string.group_folder) { + // Create app shortcuts is a special built-in case of shortcuts + createFolderIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, getContext().getString(R.string.group_folder)); + } else { + ComponentName name = new ComponentName(info.activityInfo.packageName, info.activityInfo.name); + createFolderIntent.setComponent(name); + } + mLauncher.addLiveFolder(createFolderIntent); + + return true; + } +} diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java index 0013644ab..78a9516f6 100644 --- a/src/com/android/launcher2/FolderIcon.java +++ b/src/com/android/launcher2/FolderIcon.java @@ -91,7 +91,9 @@ public class FolderIcon extends BubbleTextView implements DropTarget { public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo) { - setCompoundDrawablesWithIntrinsicBounds(null, mOpenIcon, null, null); + if (acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) { + setCompoundDrawablesWithIntrinsicBounds(null, mOpenIcon, null, null); + } } public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, @@ -102,4 +104,10 @@ public class FolderIcon extends BubbleTextView implements DropTarget { DragView dragView, Object dragInfo) { setCompoundDrawablesWithIntrinsicBounds(null, mCloseIcon, null, null); } + + @Override + public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset, + DragView dragView, Object dragInfo) { + return null; + } } diff --git a/src/com/android/launcher2/FolderListAdapter.java b/src/com/android/launcher2/FolderListAdapter.java new file mode 100644 index 000000000..bdfeaeb3d --- /dev/null +++ b/src/com/android/launcher2/FolderListAdapter.java @@ -0,0 +1,20 @@ +package com.android.launcher2; + +import com.android.launcher.R; + +import android.content.Context; +import android.content.pm.ResolveInfo; + +public class FolderListAdapter extends IntentListAdapter { + + public FolderListAdapter(Context context, String actionFilter) { + super(context, actionFilter); + + // Manually create a separate entry for creating a folder in Launcher + ResolveInfo folder = new ResolveInfo(); + folder.icon = R.drawable.ic_launcher_folder; + folder.labelRes = R.string.group_folder; + folder.resolvePackageName = context.getPackageName(); + mIntentList.add(0, folder); + } +} diff --git a/src/com/android/launcher2/HomeCustomizationItemGallery.java b/src/com/android/launcher2/HomeCustomizationItemGallery.java new file mode 100644 index 000000000..df64d5e99 --- /dev/null +++ b/src/com/android/launcher2/HomeCustomizationItemGallery.java @@ -0,0 +1,50 @@ +package com.android.launcher2; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.widget.Gallery; + +public abstract class HomeCustomizationItemGallery extends Gallery + implements Gallery.OnItemLongClickListener { + + protected Context mContext; + + protected Launcher mLauncher; + + protected int mMotionDownRawX; + protected int mMotionDownRawY; + + public HomeCustomizationItemGallery(Context context, AttributeSet attrs) { + super(context, attrs); + setLongClickable(true); + setOnItemLongClickListener(this); + mContext = context; + + setCallbackDuringFling(false); + } + + public void setLauncher(Launcher launcher) { + mLauncher = launcher; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN && mLauncher.isAllAppsVisible()) { + return false; + } + + super.onTouchEvent(ev); + + int x = (int) ev.getX(); + int y = (int) ev.getY(); + + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + mMotionDownRawX = (int) ev.getRawX(); + mMotionDownRawY = (int) ev.getRawY(); + } + return true; + } +} + diff --git a/src/com/android/launcher2/InstallShortcutReceiver.java b/src/com/android/launcher2/InstallShortcutReceiver.java index 3fc568b9f..36380540d 100644 --- a/src/com/android/launcher2/InstallShortcutReceiver.java +++ b/src/com/android/launcher2/InstallShortcutReceiver.java @@ -16,11 +16,11 @@ package com.android.launcher2; +import java.util.ArrayList; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.ContentResolver; -import android.database.Cursor; import android.widget.Toast; import com.android.launcher.R; @@ -86,38 +86,24 @@ public class InstallShortcutReceiver extends BroadcastReceiver { private static boolean findEmptyCell(Context context, int[] xy, int screen) { final int xCount = Launcher.NUMBER_CELLS_X; final int yCount = Launcher.NUMBER_CELLS_Y; - boolean[][] occupied = new boolean[xCount][yCount]; - final ContentResolver cr = context.getContentResolver(); - Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, - new String[] { LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY, - LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY }, - LauncherSettings.Favorites.SCREEN + "=?", - new String[] { String.valueOf(screen) }, null); - - final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); - final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); - final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX); - final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY); - - try { - while (c.moveToNext()) { - int cellX = c.getInt(cellXIndex); - int cellY = c.getInt(cellYIndex); - int spanX = c.getInt(spanXIndex); - int spanY = c.getInt(spanYIndex); - + ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context); + ItemInfo item = null; + int cellX, cellY, spanX, spanY; + for (int i = 0; i < items.size(); ++i) { + item = items.get(i); + if (item.screen == screen) { + cellX = item.cellX; + cellY = item.cellY; + spanX = item.spanX; + spanY = item.spanY; for (int x = cellX; x < cellX + spanX && x < xCount; x++) { for (int y = cellY; y < cellY + spanY && y < yCount; y++) { occupied[x][y] = true; } } } - } catch (Exception e) { - return false; - } finally { - c.close(); } return CellLayout.findVacantCell(xy, 1, 1, xCount, yCount, occupied); diff --git a/src/com/android/launcher2/IntentListAdapter.java b/src/com/android/launcher2/IntentListAdapter.java new file mode 100644 index 000000000..7ebffd4f8 --- /dev/null +++ b/src/com/android/launcher2/IntentListAdapter.java @@ -0,0 +1,67 @@ +package com.android.launcher2; + +import com.android.launcher.R; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.graphics.drawable.Drawable; +import android.provider.LiveFolders; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import java.util.List; + +public class IntentListAdapter extends BaseAdapter { + private LayoutInflater mLayoutInflater; + private PackageManager mPackageManager; + protected List<ResolveInfo> mIntentList; + + public IntentListAdapter(Context context, String actionFilter) { + mLayoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mPackageManager = context.getPackageManager(); + + Intent createLiveFolderIntent = new Intent(actionFilter); + mIntentList = mPackageManager.queryIntentActivities(createLiveFolderIntent, 0); + } + + public int getCount() { + return mIntentList.size(); + } + + public Object getItem(int position) { + return mIntentList.get(position); + } + + public long getItemId(int position) { + return position; + } + + public View getView(int position, View convertView, ViewGroup parent) { + TextView textView; + + if (convertView == null) { + textView = (TextView) mLayoutInflater.inflate( + R.layout.home_customization_drawer_item, parent, false); + } else { + textView = (TextView) convertView; + } + + ResolveInfo info = mIntentList.get(position); + Drawable image = info.loadIcon(mPackageManager); + image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight()); + textView.setCompoundDrawables(null, image, null, null); + + CharSequence label = info.loadLabel(mPackageManager); + textView.setText(label); + + return textView; + } +} diff --git a/src/com/android/launcher2/ItemInfo.java b/src/com/android/launcher2/ItemInfo.java index a96d9aeb2..dc4575062 100644 --- a/src/com/android/launcher2/ItemInfo.java +++ b/src/com/android/launcher2/ItemInfo.java @@ -112,6 +112,11 @@ class ItemInfo { } } + void updateValuesWithCoordinates(ContentValues values, int cellX, int cellY) { + values.put(LauncherSettings.Favorites.CELLX, cellX); + values.put(LauncherSettings.Favorites.CELLY, cellY); + } + static byte[] flattenBitmap(Bitmap bitmap) { // Try go guesstimate how much space the icon will take when serialized // to avoid unnecessary allocations/copies during the write. diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java index a5988bf6c..f846b516c 100644 --- a/src/com/android/launcher2/Launcher.java +++ b/src/com/android/launcher2/Launcher.java @@ -17,6 +17,7 @@ package com.android.launcher2; import com.android.common.Search; +import com.android.launcher.R; import android.app.Activity; import android.app.AlertDialog; @@ -24,6 +25,8 @@ import android.app.Dialog; import android.app.SearchManager; import android.app.StatusBarManager; import android.app.WallpaperManager; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProviderInfo; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -41,10 +44,10 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.database.ContentObserver; import android.graphics.Bitmap; -import android.graphics.Rect; import android.graphics.Canvas; -import android.graphics.drawable.Drawable; +import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -63,34 +66,37 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; +import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; import android.view.View.OnLongClickListener; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.animation.Animation; +import android.view.animation.Animation.AnimationListener; +import android.view.animation.AnimationUtils; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; -import android.widget.TextView; -import android.widget.Toast; import android.widget.ImageView; -import android.widget.PopupWindow; import android.widget.LinearLayout; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProviderInfo; +import android.widget.PopupWindow; +import android.widget.TabHost; +import android.widget.TextView; +import android.widget.Toast; -import java.util.ArrayList; -import java.util.List; -import java.util.HashMap; +import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.DataInputStream; - -import com.android.launcher.R; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; /** * Default launcher application. */ public final class Launcher extends Activity - implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher { + implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, + AllAppsView.Watcher, View.OnTouchListener { static final String TAG = "Launcher"; static final boolean LOGD = false; @@ -182,6 +188,7 @@ public final class Launcher extends Activity private DeleteZone mDeleteZone; private HandleView mHandleView; private AllAppsView mAllAppsGrid; + private TabHost mHomeCustomizationDrawer; private Bundle mSavedState; @@ -211,10 +218,17 @@ public final class Launcher extends Activity private Drawable[] mHotseatIcons = null; private CharSequence[] mHotseatLabels = null; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + if (LauncherApplication.isInPlaceRotationEnabled()) { + // hide the status bar (temporary until we get the status bar design figured out) + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR); + } + LauncherApplication app = ((LauncherApplication)getApplication()); mModel = app.setLauncher(this); mIconCache = app.getIconCache(); @@ -232,8 +246,26 @@ public final class Launcher extends Activity loadHotseats(); checkForLocaleChange(); setWallpaperDimension(); - setContentView(R.layout.launcher); + mHomeCustomizationDrawer = (TabHost) findViewById(com.android.internal.R.id.tabhost); + if (mHomeCustomizationDrawer != null) { + mHomeCustomizationDrawer.setup(); + + String widgetsLabel = getString(R.string.widgets_tab_label); + mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec("widgets") + .setIndicator(widgetsLabel).setContent(R.id.widget_chooser)); + String foldersLabel = getString(R.string.folders_tab_label); + mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec("folders") + .setIndicator(foldersLabel).setContent(R.id.folder_chooser)); + String shortcutsLabel = getString(R.string.shortcuts_tab_label); + mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec("shortcuts") + .setIndicator(shortcutsLabel).setContent(R.id.shortcut_chooser)); + String wallpapersLabel = getString(R.string.wallpapers_tab_label); + mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec("wallpapers") + .setIndicator(wallpapersLabel).setContent(R.id.wallpaperstab)); + + mHomeCustomizationDrawer.setCurrentTab(0); + } setupViews(); registerContentObservers(); @@ -259,6 +291,19 @@ public final class Launcher extends Activity registerReceiver(mCloseSystemDialogsReceiver, filter); } + @Override + public void onConfigurationChanged(Configuration newConfig) { + // TODO Auto-generated method stub + super.onConfigurationChanged(newConfig); + + if (LauncherApplication.isInPlaceRotationEnabled()) { + mModel.updateOrientation(); + mWorkspace.refreshWorkspaceChildren(); + mWorkspace.rotateCurrentScreensChildren(); + } + } + + private void checkForLocaleChange() { final LocaleConfiguration localeConfiguration = new LocaleConfiguration(); readConfiguration(this, localeConfiguration); @@ -419,7 +464,7 @@ public final class Launcher extends Activity // note: if the user launches this without a default set, she // will always be taken to the default URL above; this is // unavoidable as we must specify a valid URL in order for the - // chooser to appear, and once the user selects something, that + // chooser to appear, and once the user selects something, that // URL is unavoidably sent to the chosen app. } else { try { @@ -429,7 +474,7 @@ public final class Launcher extends Activity // bogus; leave intent=null } } - + if (intent == null) { mHotseats[i] = null; mHotseatLabels[i] = getText(R.string.activity_not_found); @@ -437,15 +482,15 @@ public final class Launcher extends Activity } if (LOGD) { - Log.d(TAG, "loadHotseats: hotseat " + i - + " initial intent=[" + Log.d(TAG, "loadHotseats: hotseat " + i + + " initial intent=[" + intent.toUri(Intent.URI_INTENT_SCHEME) + "]"); } ResolveInfo bestMatch = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); List<ResolveInfo> allMatches = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); - if (LOGD) { + if (LOGD) { Log.d(TAG, "Best match for intent: " + bestMatch); Log.d(TAG, "All matches: "); for (ResolveInfo ri : allMatches) { @@ -454,8 +499,8 @@ public final class Launcher extends Activity } // did this resolve to a single app, or the resolver? if (allMatches.size() == 0 || bestMatch == null) { - // can't find any activity to handle this. let's leave the - // intent as-is and let Launcher show a toast when it fails + // can't find any activity to handle this. let's leave the + // intent as-is and let Launcher show a toast when it fails // to launch. mHotseats[i] = intent; @@ -471,7 +516,7 @@ public final class Launcher extends Activity break; } } - + if (!found) { if (LOGD) Log.d(TAG, "Multiple options, no default yet"); // the bestMatch is probably the ResolveActivity, meaning the @@ -496,8 +541,8 @@ public final class Launcher extends Activity } if (LOGD) { - Log.d(TAG, "loadHotseats: hotseat " + i - + " final intent=[" + Log.d(TAG, "loadHotseats: hotseat " + i + + " final intent=[" + ((mHotseats[i] == null) ? "null" : mHotseats[i].toUri(Intent.URI_INTENT_SCHEME)) @@ -536,10 +581,11 @@ public final class Launcher extends Activity completeAddLiveFolder(data, mAddItemCellInfo); break; case REQUEST_PICK_APPWIDGET: - addAppWidget(data); + addAppWidgetFromPick(data); break; case REQUEST_CREATE_APPWIDGET: - completeAddAppWidget(data, mAddItemCellInfo); + int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); + completeAddAppWidget(appWidgetId, mAddItemCellInfo); break; case REQUEST_PICK_WALLPAPER: // We just wanted the activity result here so we can clear mWaitingForResult @@ -572,8 +618,13 @@ public final class Launcher extends Activity @Override protected void onPause() { super.onPause(); - dismissPreview(mPreviousView); - dismissPreview(mNextView); + // Some launcher layouts don't have a previous and next view + if (mPreviousView != null) { + dismissPreview(mPreviousView); + } + if (mNextView != null) { + dismissPreview(mNextView); + } mDragController.cancelDrag(); } @@ -706,12 +757,17 @@ public final class Launcher extends Activity mAllAppsGrid.setDragController(dragController); ((View) mAllAppsGrid).setWillNotDraw(false); // We don't want a hole punched in our window. // Manage focusability manually since this thing is always visible - ((View) mAllAppsGrid).setFocusable(false); + ((View) mAllAppsGrid).setFocusable(false); mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace); final Workspace workspace = mWorkspace; workspace.setHapticFeedbackEnabled(false); + // We only intercept touch events to dismiss the home customization drawer; if it doesn't + // exist, then no need to do this + if (mHomeCustomizationDrawer != null) + workspace.setOnInterceptTouchListener(this); + DeleteZone deleteZone = (DeleteZone) dragLayer.findViewById(R.id.delete_zone); mDeleteZone = deleteZone; @@ -720,24 +776,42 @@ public final class Launcher extends Activity mHandleView.setOnClickListener(this); mHandleView.setOnLongClickListener(this); - ImageView hotseatLeft = (ImageView) findViewById(R.id.hotseat_left); - hotseatLeft.setContentDescription(mHotseatLabels[0]); - hotseatLeft.setImageDrawable(mHotseatIcons[0]); - ImageView hotseatRight = (ImageView) findViewById(R.id.hotseat_right); - hotseatRight.setContentDescription(mHotseatLabels[1]); - hotseatRight.setImageDrawable(mHotseatIcons[1]); + WidgetChooser widgetChooser = (WidgetChooser) findViewById(R.id.widget_chooser); + if (widgetChooser != null) { + WidgetListAdapter widgetGalleryAdapter = new WidgetListAdapter(this); + widgetChooser.setAdapter(widgetGalleryAdapter); + widgetChooser.setDragController(dragController); + widgetChooser.setLauncher(this); + + FolderChooser folderChooser = (FolderChooser) findViewById(R.id.folder_chooser); + IntentListAdapter folderTypes = new FolderListAdapter(this, LiveFolders.ACTION_CREATE_LIVE_FOLDER); + folderChooser.setAdapter(folderTypes); + folderChooser.setLauncher(this); + + ShortcutChooser shortcutChooser = (ShortcutChooser) findViewById(R.id.shortcut_chooser); + IntentListAdapter shortcutTypes = new ShortcutListAdapter(this, Intent.ACTION_CREATE_SHORTCUT); + shortcutChooser.setAdapter(shortcutTypes); + shortcutChooser.setLauncher(this); + } else { + ImageView hotseatLeft = (ImageView) findViewById(R.id.hotseat_left); + hotseatLeft.setContentDescription(mHotseatLabels[0]); + hotseatLeft.setImageDrawable(mHotseatIcons[0]); + ImageView hotseatRight = (ImageView) findViewById(R.id.hotseat_right); + hotseatRight.setContentDescription(mHotseatLabels[1]); + hotseatRight.setImageDrawable(mHotseatIcons[1]); - mPreviousView = (ImageView) dragLayer.findViewById(R.id.previous_screen); - mNextView = (ImageView) dragLayer.findViewById(R.id.next_screen); + mPreviousView = (ImageView) dragLayer.findViewById(R.id.previous_screen); + mNextView = (ImageView) dragLayer.findViewById(R.id.next_screen); - Drawable previous = mPreviousView.getDrawable(); - Drawable next = mNextView.getDrawable(); - mWorkspace.setIndicators(previous, next); + Drawable previous = mPreviousView.getDrawable(); + Drawable next = mNextView.getDrawable(); + mWorkspace.setIndicators(previous, next); - mPreviousView.setHapticFeedbackEnabled(false); - mPreviousView.setOnLongClickListener(this); - mNextView.setHapticFeedbackEnabled(false); - mNextView.setOnLongClickListener(this); + mPreviousView.setHapticFeedbackEnabled(false); + mPreviousView.setOnLongClickListener(this); + mNextView.setHapticFeedbackEnabled(false); + mNextView.setOnLongClickListener(this); + } workspace.setOnLongClickListener(this); workspace.setDragController(dragController); @@ -745,7 +819,8 @@ public final class Launcher extends Activity deleteZone.setLauncher(this); deleteZone.setDragController(dragController); - deleteZone.setHandle(findViewById(R.id.all_apps_button_cluster)); + int deleteZoneHandleId = LauncherApplication.isScreenXLarge() ? R.id.add_button : R.id.all_apps_button_cluster; + deleteZone.setHandle(findViewById(deleteZoneHandleId)); dragController.setDragScoller(workspace); dragController.setDragListener(deleteZone); @@ -792,7 +867,7 @@ public final class Launcher extends Activity ); } } - + /** * Creates a view representing a shortcut. * @@ -876,17 +951,12 @@ public final class Launcher extends Activity * @param data The intent describing the appWidgetId. * @param cellInfo The position on screen where to create the widget. */ - private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo) { - Bundle extras = data.getExtras(); - int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); - - if (LOGD) Log.d(TAG, "dumping extras content=" + extras.toString()); - + private void completeAddAppWidget(int appWidgetId, CellLayout.CellInfo cellInfo) { AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); // Calculate the grid spans needed to fit this widget CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen); - int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight); + int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight, null); // Try finding open space on Launcher screen final int[] xy = mCellCoordinates; @@ -960,10 +1030,13 @@ public final class Launcher extends Activity boolean alreadyOnHome = ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); boolean allAppsVisible = isAllAppsVisible(); + + // TODO: Figure out the right thing to do in XLarge mode here if (!mWorkspace.isDefaultScreenShowing()) { mWorkspace.moveToDefaultScreen(alreadyOnHome && !allAppsVisible); } closeAllApps(alreadyOnHome && allAppsVisible); + hideCustomizationDrawer(); final View v = getWindow().peekDecorView(); if (v != null && v.getWindowToken() != null) { @@ -978,6 +1051,13 @@ public final class Launcher extends Activity protected void onRestoreInstanceState(Bundle savedInstanceState) { // Do not call super here mSavedInstanceState = savedInstanceState; + + if (mHomeCustomizationDrawer != null) { + String cur = savedInstanceState.getString("currentTab"); + if (cur != null) { + mHomeCustomizationDrawer.setCurrentTabByTag(cur); + } + } } @Override @@ -1014,13 +1094,20 @@ public final class Launcher extends Activity outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_X, layout.getCountX()); outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y, layout.getCountY()); outState.putBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS, - layout.getOccupiedCells()); + layout.getOccupiedCellsFlattened()); } if (mFolderInfo != null && mWaitingForResult) { outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true); outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id); } + + if (mHomeCustomizationDrawer != null) { + String currentTabTag = mHomeCustomizationDrawer.getCurrentTabTag(); + if (currentTabTag != null) { + outState.putString("currentTab", currentTabTag); + } + } } @Override @@ -1040,9 +1127,14 @@ public final class Launcher extends Activity unbindDesktopItems(); getContentResolver().unregisterContentObserver(mWidgetObserver); - - dismissPreview(mPreviousView); - dismissPreview(mNextView); + + // Some launcher layouts don't have a previous and next view + if (mPreviousView != null) { + dismissPreview(mPreviousView); + } + if (mNextView != null) { + dismissPreview(mNextView); + } unregisterReceiver(mCloseSystemDialogsReceiver); } @@ -1131,6 +1223,13 @@ public final class Launcher extends Activity return true; } + // we need to initialize mAddItemCellInfo before adding something to the homescreen -- when + // using the settings menu to add an item, something similar happens in showAddDialog + public void prepareAddItemFromHomeCustomizationDrawer() { + mMenuAddInfo = mWorkspace.findAllVacantCells(null); + mAddItemCellInfo = mMenuAddInfo; + } + @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { @@ -1168,12 +1267,32 @@ public final class Launcher extends Activity private void addItems() { closeAllApps(true); - showAddDialog(mMenuAddInfo); + if (LauncherApplication.isScreenXLarge()) { + // Animate the widget chooser up from the bottom of the screen + if (!isCustomizationDrawerVisible()) { + showCustomizationDrawer(); + } + } else { + showAddDialog(mMenuAddInfo); + } + } + + void addAppWidgetFromDrop(ComponentName appWidgetProvider, CellLayout.CellInfo cellInfo) { + mAddItemCellInfo = cellInfo; + int appWidgetId = getAppWidgetHost().allocateAppWidgetId(); + AppWidgetManager.getInstance(this).bindAppWidgetId(appWidgetId, appWidgetProvider); + addAppWidgetImpl(appWidgetId); } - void addAppWidget(Intent data) { + void addAppWidgetFromPick(Intent data) { // TODO: catch bad widget exception when sent int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); + // TODO: Is this log message meaningful? + if (LOGD) Log.d(TAG, "dumping extras content=" + data.getExtras()); + addAppWidgetImpl(appWidgetId); + } + + void addAppWidgetImpl(int appWidgetId) { AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId); if (appWidget.configure != null) { @@ -1185,7 +1304,7 @@ public final class Launcher extends Activity startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET); } else { // Otherwise just add it - onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data); + completeAddAppWidget(appWidgetId, mAddItemCellInfo); } } @@ -1386,11 +1505,16 @@ public final class Launcher extends Activity public void onBackPressed() { if (isAllAppsVisible()) { closeAllApps(true); + } else if (isCustomizationDrawerVisible()) { + hideCustomizationDrawer(); } else { closeFolder(); } - dismissPreview(mPreviousView); - dismissPreview(mNextView); + // Some launcher layouts don't have a previous and next view + if (mPreviousView != null) { + dismissPreview(mPreviousView); + dismissPreview(mNextView); + } } private void closeFolder() { @@ -1456,6 +1580,23 @@ public final class Launcher extends Activity } } + public boolean onTouch(View v, MotionEvent event) { + // this is being forwarded from mWorkspace; + // clicking anywhere on the workspace causes the drawer to slide down + hideCustomizationDrawer(); + return false; + } + + /** + * Event handler for the "plus" button that appears on the home screen, which + * enters home screen customization mode. + * + * @param v The view that was clicked. + */ + public void onClickAddButton(View v) { + addItems(); + } + void startActivitySafely(Intent intent, Object tag) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { @@ -1471,7 +1612,7 @@ public final class Launcher extends Activity + "tag="+ tag + " intent=" + intent, e); } } - + void startActivityForResultSafely(Intent intent, int requestCode) { try { startActivityForResult(intent, requestCode); @@ -1516,7 +1657,7 @@ public final class Launcher extends Activity * * @param folderInfo The FolderInfo describing the folder to open. */ - private void openFolder(FolderInfo folderInfo) { + public void openFolder(FolderInfo folderInfo) { Folder openFolder; if (folderInfo instanceof UserFolderInfo) { @@ -1533,7 +1674,8 @@ public final class Launcher extends Activity openFolder.bind(folderInfo); folderInfo.opened = true; - mWorkspace.addInScreen(openFolder, folderInfo.screen, 0, 0, 4, 4); + mWorkspace.addInFullScreen(openFolder, folderInfo.screen); + openFolder.onOpen(); } @@ -1631,9 +1773,9 @@ public final class Launcher extends Activity final Workspace workspace = mWorkspace; CellLayout cell = ((CellLayout) workspace.getChildAt(start)); - + float max = workspace.getChildCount(); - + final Rect r = new Rect(); resources.getDrawable(R.drawable.preview_background).getPadding(r); int extraW = (int) ((r.left + r.right) * max); @@ -1684,7 +1826,7 @@ public final class Launcher extends Activity preview.addView(image, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); - bitmaps.add(bitmap); + bitmaps.add(bitmap); } final PopupWindow p = new PopupWindow(this); @@ -1705,7 +1847,7 @@ public final class Launcher extends Activity anchor.setTag(p); anchor.setTag(R.id.workspace, preview); - anchor.setTag(R.id.icon, bitmaps); + anchor.setTag(R.id.icon, bitmaps); } class PreviewTouchHandler implements View.OnClickListener, Runnable, View.OnFocusChangeListener { @@ -1721,7 +1863,7 @@ public final class Launcher extends Activity } public void run() { - dismissPreview(mAnchor); + dismissPreview(mAnchor); } public void onFocusChange(View v, boolean hasFocus) { @@ -1776,6 +1918,7 @@ public final class Launcher extends Activity } private void pickShortcut() { + // Insert extra item to handle picking application Bundle bundle = new Bundle(); ArrayList<String> shortcutNames = new ArrayList<String>(); @@ -1888,11 +2031,20 @@ public final class Launcher extends Activity } void showAllApps(boolean animated) { - mAllAppsGrid.zoom(1.0f, animated); + hideCustomizationDrawer(); + + if (LauncherApplication.isScreenXLarge() && animated) { + // Not really a zoom -- this just makes the view visible + mAllAppsGrid.zoom(1.0f, false); + Animation anim = AnimationUtils.loadAnimation(this, R.anim.all_apps_zoom_in); + ((View) mAllAppsGrid).startAnimation(anim); + } else { + mAllAppsGrid.zoom(1.0f, animated); + } ((View) mAllAppsGrid).setFocusable(true); ((View) mAllAppsGrid).requestFocus(); - + // TODO: fade these two too mDeleteZone.setVisibility(View.GONE); } @@ -1939,7 +2091,19 @@ public final class Launcher extends Activity void closeAllApps(boolean animated) { if (mAllAppsGrid.isVisible()) { mWorkspace.setVisibility(View.VISIBLE); - mAllAppsGrid.zoom(0.0f, animated); + if (LauncherApplication.isScreenXLarge() && animated) { + Animation anim = AnimationUtils.loadAnimation(this, R.anim.all_apps_zoom_out); + anim.setAnimationListener(new AnimationListener() { + public void onAnimationStart(Animation animation) {} + public void onAnimationRepeat(Animation animation) {} + public void onAnimationEnd(Animation animation) { + mAllAppsGrid.zoom(0.0f, false); + } + }); + ((View)mAllAppsGrid).startAnimation(anim); + } else { + mAllAppsGrid.zoom(0.0f, animated); + } ((View)mAllAppsGrid).setFocusable(false); mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus(); } @@ -1953,6 +2117,33 @@ public final class Launcher extends Activity // TODO } + private boolean isCustomizationDrawerVisible() { + return mHomeCustomizationDrawer != null && mHomeCustomizationDrawer.getVisibility() == View.VISIBLE; + } + + private void showCustomizationDrawer() { + if (isAllAppsVisible()) { + // TODO: Make a smoother transition here + closeAllApps(false); + } + mHomeCustomizationDrawer.setVisibility(View.VISIBLE); + mHomeCustomizationDrawer.startAnimation(AnimationUtils.loadAnimation(this, R.anim.home_customization_drawer_slide_up)); + } + + private void hideCustomizationDrawer() { + if (isCustomizationDrawerVisible()) { + Animation slideDownAnimation = AnimationUtils.loadAnimation(this, R.anim.home_customization_drawer_slide_down); + slideDownAnimation.setAnimationListener(new Animation.AnimationListener() { + public void onAnimationEnd(Animation animation) { + mHomeCustomizationDrawer.setVisibility(View.GONE); + } + public void onAnimationRepeat(Animation animation) {} + public void onAnimationStart(Animation animation) {} + }); + mHomeCustomizationDrawer.startAnimation(slideDownAnimation); + } + } + /** * Displays the shortcut creation dialog and launches, if necessary, the * appropriate activity. @@ -2005,7 +2196,6 @@ public final class Launcher extends Activity switch (which) { case AddAdapter.ITEM_SHORTCUT: { - // Insert extra item to handle picking application pickShortcut(); break; } @@ -2053,7 +2243,7 @@ public final class Launcher extends Activity } public void onShow(DialogInterface dialog) { - mWaitingForResult = true; + mWaitingForResult = true; } } diff --git a/src/com/android/launcher2/LauncherAppWidgetInfo.java b/src/com/android/launcher2/LauncherAppWidgetInfo.java index 8499ebb7c..32c92aabd 100644 --- a/src/com/android/launcher2/LauncherAppWidgetInfo.java +++ b/src/com/android/launcher2/LauncherAppWidgetInfo.java @@ -17,25 +17,50 @@ package com.android.launcher2; import android.appwidget.AppWidgetHostView; +import android.content.ComponentName; import android.content.ContentValues; /** - * Represents a widget, which just contains an identifier. + * Represents a widget (either instantiated or about to be) in the Launcher. */ class LauncherAppWidgetInfo extends ItemInfo { /** + * Indicates that the widget hasn't been instantiated yet. + */ + static final int NO_ID = -1; + + /** * Identifier for this widget when talking with * {@link android.appwidget.AppWidgetManager} for updates. */ - int appWidgetId; + int appWidgetId = NO_ID; + + ComponentName providerName; + // TODO: Are these necessary here? + int minWidth = -1; + int minHeight = -1; + /** * View that holds this widget after it's been created. This view isn't created * until Launcher knows it's needed. */ AppWidgetHostView hostView = null; + /** + * Constructor for use with AppWidgets that haven't been instantiated yet. + */ + LauncherAppWidgetInfo(ComponentName providerName) { + itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; + this.providerName = providerName; + + // Since the widget isn't instantiated yet, we don't know these values. Set them to -1 + // to indicate that they should be calculated based on the layout and minWidth/minHeight + spanX = -1; + spanY = -1; + } + LauncherAppWidgetInfo(int appWidgetId) { itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; this.appWidgetId = appWidgetId; @@ -52,7 +77,6 @@ class LauncherAppWidgetInfo extends ItemInfo { return "AppWidget(id=" + Integer.toString(appWidgetId) + ")"; } - @Override void unbind() { super.unbind(); diff --git a/src/com/android/launcher2/LauncherApplication.java b/src/com/android/launcher2/LauncherApplication.java index eda92d999..ca08378d8 100644 --- a/src/com/android/launcher2/LauncherApplication.java +++ b/src/com/android/launcher2/LauncherApplication.java @@ -20,6 +20,7 @@ import android.app.Application; import android.content.ContentResolver; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Configuration; import android.database.ContentObserver; import android.os.Handler; import dalvik.system.VMRuntime; @@ -27,6 +28,8 @@ import dalvik.system.VMRuntime; public class LauncherApplication extends Application { public LauncherModel mModel; public IconCache mIconCache; + private static boolean sIsScreenXLarge; + private static final boolean ENABLE_ROTATION = false; @Override public void onCreate() { @@ -36,6 +39,7 @@ public class LauncherApplication extends Application { mIconCache = new IconCache(this); mModel = new LauncherModel(this, mIconCache); + sIsScreenXLarge = (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE; // Register intent receivers IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); @@ -89,4 +93,12 @@ public class LauncherApplication extends Application { LauncherModel getModel() { return mModel; } + + public static boolean isInPlaceRotationEnabled() { + return sIsScreenXLarge && ENABLE_ROTATION; + } + + public static boolean isScreenXLarge() { + return sIsScreenXLarge; + } } diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java index eb341f68a..238fbdfe7 100644 --- a/src/com/android/launcher2/LauncherModel.java +++ b/src/com/android/launcher2/LauncherModel.java @@ -16,6 +16,15 @@ package com.android.launcher2; +import java.lang.ref.WeakReference; +import java.net.URISyntaxException; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.BroadcastReceiver; @@ -23,9 +32,9 @@ import android.content.ComponentName; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.ContentValues; +import android.content.Context; import android.content.Intent; import android.content.Intent.ShortcutIconResource; -import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; @@ -38,20 +47,10 @@ import android.net.Uri; import android.os.Handler; import android.os.HandlerThread; import android.os.Parcelable; -import android.os.RemoteException; -import android.util.Log; import android.os.Process; +import android.os.RemoteException; import android.os.SystemClock; - -import java.lang.ref.WeakReference; -import java.net.URISyntaxException; -import java.text.Collator; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; +import android.util.Log; import com.android.launcher.R; @@ -91,6 +90,8 @@ public class LauncherModel extends BroadcastReceiver { private Bitmap mDefaultIcon; + private static LauncherModelOrientationHelper mModelOrientationHelper; + public interface Callbacks { public int getCurrentWorkspaceScreen(); public void startBinding(); @@ -109,6 +110,7 @@ public class LauncherModel extends BroadcastReceiver { mApp = app; mAllAppsList = new AllAppsList(iconCache); mIconCache = iconCache; + mModelOrientationHelper = new LauncherModelOrientationHelper(mApp); mDefaultIcon = Utilities.createIconBitmap( app.getPackageManager().getDefaultActivityIcon(), app); @@ -141,11 +143,20 @@ public class LauncherModel extends BroadcastReceiver { } } + static int getCurrentOrientation() { + return mModelOrientationHelper.getCurrentOrientation(); + } + + static int getPreviousOrientationRelativeToCurrent() { + return mModelOrientationHelper.getPreviousOrientationRelativeToCurrent(); + } + /** * Move an item in the DB to a new <container, screen, cellX, cellY> */ static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen, int cellX, int cellY) { + item.container = container; item.screen = screen; item.cellX = cellX; @@ -153,10 +164,11 @@ public class LauncherModel extends BroadcastReceiver { final ContentValues values = new ContentValues(); final ContentResolver cr = context.getContentResolver(); + final LauncherModelOrientationHelper.Coordinates coord = mModelOrientationHelper.getCanonicalCoordinates(item); values.put(LauncherSettings.Favorites.CONTAINER, item.container); - values.put(LauncherSettings.Favorites.CELLX, item.cellX); - values.put(LauncherSettings.Favorites.CELLY, item.cellY); + values.put(LauncherSettings.Favorites.CELLX, coord.x); + values.put(LauncherSettings.Favorites.CELLY, coord.y); values.put(LauncherSettings.Favorites.SCREEN, item.screen); cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null); @@ -181,6 +193,48 @@ public class LauncherModel extends BroadcastReceiver { } /** + * Returns an ItemInfo array containing all the items in the LauncherModel. + * The ItemInfo.id is not set through this function. + */ + static ArrayList<ItemInfo> getItemsInLocalCoordinates(Context context) { + ArrayList<ItemInfo> items = new ArrayList<ItemInfo>(); + final ContentResolver cr = context.getContentResolver(); + Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] { + LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER, + LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY, + LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY }, null, null, null); + + final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE); + final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER); + final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN); + final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); + final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); + final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX); + final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY); + + try { + while (c.moveToNext()) { + ItemInfo item = new ItemInfo(); + item.cellX = c.getInt(cellXIndex); + item.cellY = c.getInt(cellYIndex); + item.spanX = c.getInt(spanXIndex); + item.spanY = c.getInt(spanYIndex); + item.container = c.getInt(containerIndex); + item.itemType = c.getInt(itemTypeIndex); + item.screen = c.getInt(screenIndex); + + items.add(item); + } + } catch (Exception e) { + items.clear(); + } finally { + c.close(); + } + + return items; + } + + /** * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList. */ FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) { @@ -210,12 +264,13 @@ public class LauncherModel extends BroadcastReceiver { break; } + final LauncherModelOrientationHelper.Coordinates coord = mModelOrientationHelper.getLocalCoordinates(c.getInt(cellXIndex), c.getInt(cellYIndex), 1, 1); folderInfo.title = c.getString(titleIndex); folderInfo.id = id; folderInfo.container = c.getInt(containerIndex); folderInfo.screen = c.getInt(screenIndex); - folderInfo.cellX = c.getInt(cellXIndex); - folderInfo.cellY = c.getInt(cellYIndex); + folderInfo.cellX = coord.x; + folderInfo.cellY = coord.y; return folderInfo; } @@ -239,9 +294,12 @@ public class LauncherModel extends BroadcastReceiver { final ContentValues values = new ContentValues(); final ContentResolver cr = context.getContentResolver(); - item.onAddToDatabase(values); + // update the values to be written with their canonical counterparts + final LauncherModelOrientationHelper.Coordinates coord = mModelOrientationHelper.getCanonicalCoordinates(item); + item.updateValuesWithCoordinates(values, coord.x, coord.y); + Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI : LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values); @@ -251,6 +309,44 @@ public class LauncherModel extends BroadcastReceiver { } /** + * Creates a new unique child id, for a given cell span across all layouts. + */ + static int getCanonicalCellLayoutChildId(int cellId, int screen, int localCellX, int localCellY, int spanX, int spanY) { + if (LauncherApplication.isInPlaceRotationEnabled()) { + LauncherModelOrientationHelper.Coordinates coord = mModelOrientationHelper.getCanonicalCoordinates(localCellX, localCellY, spanX, spanY); + return ((screen & 0xFF) << 16) | (coord.x & 0xFF) << 8 | (coord.y & 0xFF); + } else { + return ((cellId & 0xFF) << 16) | (localCellX & 0xFF) << 8 | (localCellY & 0xFF); + } + } + + /* + * Convenience functions to help return the local device width and height. + */ + static int getLocalDeviceWidth() { + return mModelOrientationHelper.getLocalDeviceWidth(); + } + + static int getLocalDeviceHeight() { + return mModelOrientationHelper.getLocalDeviceHeight(); + } + + /** + * Return the new local coordinates given the local coordinates from the previous orientation. + */ + static LauncherModelOrientationHelper.Coordinates getLocalCoordinatesFromPreviousLocalCoordinates(CellLayout.LayoutParams lp) { + return mModelOrientationHelper.getLocalCoordinatesFromPreviousLocalCoordinates(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan); + } + + /** + * Updates the model orientation helper to take into account the current layout dimensions + * when performing local/canonical coordinate transformations. + */ + static void updateWorkspaceLayoutCells(int shortAxisCellCount, int longAxisCellCount) { + mModelOrientationHelper.updateDeviceDimensions(shortAxisCellCount, longAxisCellCount); + } + + /** * Update an item to the database in a specified container. */ static void updateItemInDatabase(Context context, ItemInfo item) { @@ -259,6 +355,10 @@ public class LauncherModel extends BroadcastReceiver { item.onAddToDatabase(values); + // update the values to be written with their canonical counterparts + final LauncherModelOrientationHelper.Coordinates coord = mModelOrientationHelper.getCanonicalCoordinates(item); + item.updateValuesWithCoordinates(values, coord.x, coord.y); + cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null); } @@ -293,13 +393,18 @@ public class LauncherModel extends BroadcastReceiver { } } + public void updateOrientation() { + // we update the LauncherModelOrientationHelper orientation whenever we re-initialize + mModelOrientationHelper.updateOrientation(mApp); + } + /** * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and * ACTION_PACKAGE_CHANGED. */ public void onReceive(Context context, Intent intent) { if (DEBUG_LOADERS) Log.d(TAG, "onReceive intent=" + intent); - + final String action = intent.getAction(); if (Intent.ACTION_PACKAGE_CHANGED.equals(action) @@ -343,7 +448,6 @@ public class LauncherModel extends BroadcastReceiver { String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); enqueuePackageUpdated(new PackageUpdatedTask( PackageUpdatedTask.OP_UNAVAILABLE, packages)); - } } @@ -449,7 +553,7 @@ public class LauncherModel extends BroadcastReceiver { } if (DEBUG_LOADERS) { Log.d(TAG, "waited " - + (SystemClock.uptimeMillis()-workspaceWaitTime) + + (SystemClock.uptimeMillis()-workspaceWaitTime) + "ms for previous step to finish binding"); } } @@ -469,7 +573,6 @@ public class LauncherModel extends BroadcastReceiver { android.os.Process.setThreadPriority(mIsLaunching ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND); } - if (loadWorkspaceFirst) { if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace"); loadAndBindWorkspace(); @@ -571,14 +674,13 @@ public class LauncherModel extends BroadcastReceiver { if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) { return true; } - for (int x = item.cellX; x < (item.cellX+item.spanX); x++) { for (int y = item.cellY; y < (item.cellY+item.spanY); y++) { if (occupied[item.screen][x][y] != null) { Log.e(TAG, "Error loading shortcut " + item - + " into cell (" + item.screen + ":" + + " into cell (" + item.screen + ":" + x + "," + y - + ") occupied by " + + ") occupied by " + occupied[item.screen][x][y]); return false; } @@ -645,6 +747,11 @@ public class LauncherModel extends BroadcastReceiver { final int displayModeIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.DISPLAY_MODE); + + LauncherModelOrientationHelper.Coordinates localCoords; + int cellX; + int cellY; + ShortcutInfo info; String intentDescription; LauncherAppWidgetInfo appWidgetInfo; @@ -678,13 +785,17 @@ public class LauncherModel extends BroadcastReceiver { if (info != null) { updateSavedIcon(context, info, c, iconIndex); + cellX = c.getInt(cellXIndex); + cellY = c.getInt(cellYIndex); + localCoords = mModelOrientationHelper.getLocalCoordinates(cellX, cellY, 1, 1); + info.intent = intent; info.id = c.getLong(idIndex); container = c.getInt(containerIndex); info.container = container; info.screen = c.getInt(screenIndex); - info.cellX = c.getInt(cellXIndex); - info.cellY = c.getInt(cellYIndex); + info.cellX = localCoords.x; + info.cellY = localCoords.y; // check & update map of what's occupied if (!checkItemPlacement(occupied, info)) { @@ -718,20 +829,22 @@ public class LauncherModel extends BroadcastReceiver { id = c.getLong(idIndex); UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id); - folderInfo.title = c.getString(titleIndex); + cellX = c.getInt(cellXIndex); + cellY = c.getInt(cellYIndex); + localCoords = mModelOrientationHelper.getLocalCoordinates(cellX, cellY, 1, 1); + folderInfo.title = c.getString(titleIndex); folderInfo.id = id; container = c.getInt(containerIndex); folderInfo.container = container; folderInfo.screen = c.getInt(screenIndex); - folderInfo.cellX = c.getInt(cellXIndex); - folderInfo.cellY = c.getInt(cellYIndex); + folderInfo.cellX = localCoords.x; + folderInfo.cellY = localCoords.y; // check & update map of what's occupied if (!checkItemPlacement(occupied, folderInfo)) { break; } - switch (container) { case LauncherSettings.Favorites.CONTAINER_DESKTOP: mItems.add(folderInfo); @@ -754,7 +867,6 @@ public class LauncherModel extends BroadcastReceiver { itemsToRemove.add(id); } else { LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id); - intentDescription = c.getString(intentIndex); intent = null; if (intentDescription != null) { @@ -765,14 +877,18 @@ public class LauncherModel extends BroadcastReceiver { } } + cellX = c.getInt(cellXIndex); + cellY = c.getInt(cellYIndex); + localCoords = mModelOrientationHelper.getLocalCoordinates(cellX, cellY, 1, 1); + liveFolderInfo.title = c.getString(titleIndex); liveFolderInfo.id = id; liveFolderInfo.uri = uri; container = c.getInt(containerIndex); liveFolderInfo.container = container; liveFolderInfo.screen = c.getInt(screenIndex); - liveFolderInfo.cellX = c.getInt(cellXIndex); - liveFolderInfo.cellY = c.getInt(cellYIndex); + liveFolderInfo.cellX = localCoords.x; + liveFolderInfo.cellY = localCoords.y; liveFolderInfo.baseIntent = intent; liveFolderInfo.displayMode = c.getInt(displayModeIndex); @@ -800,20 +916,26 @@ public class LauncherModel extends BroadcastReceiver { final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(appWidgetId); - + if (!isSafeMode && (provider == null || provider.provider == null || provider.provider.getPackageName() == null)) { Log.e(TAG, "Deleting widget that isn't installed anymore: id=" + id + " appWidgetId=" + appWidgetId); itemsToRemove.add(id); } else { + cellX = c.getInt(cellXIndex); + cellY = c.getInt(cellYIndex); + int spanX = c.getInt(spanXIndex); + int spanY = c.getInt(spanYIndex); + localCoords = mModelOrientationHelper.getLocalCoordinates(cellX, cellY, spanX, spanY); + appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId); appWidgetInfo.id = id; appWidgetInfo.screen = c.getInt(screenIndex); - appWidgetInfo.cellX = c.getInt(cellXIndex); - appWidgetInfo.cellY = c.getInt(cellYIndex); - appWidgetInfo.spanX = c.getInt(spanXIndex); - appWidgetInfo.spanY = c.getInt(spanYIndex); + appWidgetInfo.cellX = localCoords.x; + appWidgetInfo.cellY = localCoords.y; + appWidgetInfo.spanX = spanX; + appWidgetInfo.spanY = spanY; container = c.getInt(containerIndex); if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) { diff --git a/src/com/android/launcher2/LauncherModelOrientationHelper.java b/src/com/android/launcher2/LauncherModelOrientationHelper.java new file mode 100644 index 000000000..6a9473d86 --- /dev/null +++ b/src/com/android/launcher2/LauncherModelOrientationHelper.java @@ -0,0 +1,180 @@ +/* + * 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.launcher2; + +import android.content.Context; +import android.view.Display; +import android.view.Surface; +import android.view.WindowManager; + +public class LauncherModelOrientationHelper { + + static final String TAG = "LauncherModelOrientationHelper"; + + public class Coordinates { + public Coordinates(int newX, int newY) { + x = newX; + y = newY; + } + + public int x; + public int y; + } + + private int mOrientation; + private int mLocalDeviceWidth; + private int mLocalDeviceHeight; + private int mPreviousOrientation; + private int mPreviousLocalDeviceWidth; + private int mPreviousLocalDeviceHeight; + private int mCanonicalDeviceWidth; + private int mCanonicalDeviceHeight; + + protected LauncherModelOrientationHelper(Context ctx) { + updateOrientation(ctx); + } + + public int getCurrentOrientation() { + return mOrientation; + } + + public int getPreviousOrientationRelativeToCurrent() { + int orientationDifference = -(mOrientation - mPreviousOrientation); + + if (Math.abs(orientationDifference) > 180) { + orientationDifference = (int) -Math.signum(orientationDifference) + * (360 - Math.abs(orientationDifference)); + } + return orientationDifference; + } + + private void updateLocalDeviceDimensions() { + mPreviousLocalDeviceHeight = mLocalDeviceHeight; + mPreviousLocalDeviceWidth = mLocalDeviceWidth; + + if (mOrientation % 180 != 0) { + mLocalDeviceWidth = mCanonicalDeviceHeight; + mLocalDeviceHeight = mCanonicalDeviceWidth; + } else { + mLocalDeviceWidth = mCanonicalDeviceWidth; + mLocalDeviceHeight = mCanonicalDeviceHeight; + } + } + + public void updateOrientation(Context ctx) { + Display display = ((WindowManager) ctx + .getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + + mPreviousOrientation = mOrientation; + switch (display.getRotation()) { + case Surface.ROTATION_0: + mOrientation = 0; + break; + case Surface.ROTATION_90: + mOrientation = 90; + break; + case Surface.ROTATION_180: + mOrientation = 180; + break; + case Surface.ROTATION_270: + mOrientation = 270; + break; + } + updateLocalDeviceDimensions(); + } + + public void updateDeviceDimensions(int deviceWidth, int deviceHeight) { + mCanonicalDeviceWidth = deviceWidth; + mCanonicalDeviceHeight = deviceHeight; + + updateLocalDeviceDimensions(); + } + + public Coordinates getLocalCoordinatesFromPreviousLocalCoordinates( + int cellX, int cellY, int spanX, int spanY) { + return getTransformedLayoutParams(cellX, cellY, spanX, spanY, + getPreviousOrientationRelativeToCurrent(), + mPreviousLocalDeviceWidth, mPreviousLocalDeviceHeight); + } + + public Coordinates getCanonicalCoordinates(ItemInfo localItem) { + return getTransformedLayoutParams(localItem.cellX, localItem.cellY, + localItem.spanX, localItem.spanY, mOrientation, + mLocalDeviceWidth, mLocalDeviceHeight); + } + + public Coordinates getCanonicalCoordinates(int cellX, int cellY, + int spanX, int spanY) { + return getTransformedLayoutParams(cellX, cellY, spanX, spanY, + mOrientation, mLocalDeviceWidth, mLocalDeviceHeight); + } + + public Coordinates getLocalCoordinates(int cellX, int cellY, int spanX, + int spanY) { + return getTransformedLayoutParams(cellX, cellY, spanX, spanY, + -mOrientation, mCanonicalDeviceWidth, mCanonicalDeviceHeight); + } + + public int getLocalDeviceWidth() { + return mLocalDeviceWidth; + } + + public int getLocalDeviceHeight() { + return mLocalDeviceHeight; + } + + /** + * Transform the coordinates based on the current device rotation + */ + private Coordinates getTransformedLayoutParams(int cellX, int cellY, + int spanX, int spanY, int deviceRotationClockwise, + int initialDeviceWidth, int initialDeviceHeight) { + if (LauncherApplication.isScreenXLarge()) { + int x = cellX; + int y = cellY; + int width = spanX; + int height = spanY; + int finalDeviceWidth = initialDeviceWidth; + int finalDeviceHeight = initialDeviceHeight; + + // item rotation is opposite of device rotation to maintain an + // absolute + // spatial layout + double phi = Math.toRadians(-deviceRotationClockwise); + + double x1 = x + width / 2.0f - initialDeviceWidth / 2.0f; + double y1 = y + height / 2.0f - initialDeviceHeight / 2.0f; + + // multiply x and y by a clockwise rotation matrix + double x2 = x1 * Math.cos(phi) + y1 * Math.sin(phi); + double y2 = -x1 * Math.sin(phi) + y1 * Math.cos(phi); + + // Get the rotated device dimensions + if (deviceRotationClockwise % 180 != 0) { + finalDeviceWidth = initialDeviceHeight; + finalDeviceHeight = initialDeviceWidth; + } + + x2 = x2 + finalDeviceWidth / 2.0f - width / 2.0f; + y2 = y2 + finalDeviceHeight / 2.0f - height / 2.0f; + + return new Coordinates((int) Math.round(x2), (int) Math.round(y2)); + } else { + return new Coordinates(cellX, cellY); + } + } +} diff --git a/src/com/android/launcher2/LiveFolderInfo.java b/src/com/android/launcher2/LiveFolderInfo.java index 7d0a0f58f..74b021758 100644 --- a/src/com/android/launcher2/LiveFolderInfo.java +++ b/src/com/android/launcher2/LiveFolderInfo.java @@ -18,7 +18,6 @@ package com.android.launcher2; import android.content.ContentValues; import android.content.Intent; -import android.graphics.drawable.Drawable; import android.graphics.Bitmap; import android.net.Uri; diff --git a/src/com/android/launcher2/ShortcutChooser.java b/src/com/android/launcher2/ShortcutChooser.java new file mode 100644 index 000000000..1e3e5d0b1 --- /dev/null +++ b/src/com/android/launcher2/ShortcutChooser.java @@ -0,0 +1,37 @@ +package com.android.launcher2; + +import com.android.launcher.R; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.util.AttributeSet; +import android.view.View; +import android.widget.AdapterView; + +public class ShortcutChooser extends HomeCustomizationItemGallery { + + public ShortcutChooser(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { + // todo: this code sorta overlaps with other places + ResolveInfo info = (ResolveInfo)getAdapter().getItem(position); + mLauncher.prepareAddItemFromHomeCustomizationDrawer(); + + Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); + if (info.labelRes == R.string.group_applications) { + // Create app shortcuts is a special built-in case of shortcuts + createShortcutIntent.putExtra( + Intent.EXTRA_SHORTCUT_NAME,getContext().getString(R.string.group_applications)); + } else { + ComponentName name = new ComponentName(info.activityInfo.packageName, info.activityInfo.name); + createShortcutIntent.setComponent(name); + } + mLauncher.processShortcut(createShortcutIntent); + + return true; + } +} diff --git a/src/com/android/launcher2/ShortcutInfo.java b/src/com/android/launcher2/ShortcutInfo.java index 5c322baaa..b0da3a5b9 100644 --- a/src/com/android/launcher2/ShortcutInfo.java +++ b/src/com/android/launcher2/ShortcutInfo.java @@ -16,16 +16,14 @@ package com.android.launcher2; +import java.util.ArrayList; + import android.content.ComponentName; import android.content.ContentValues; -import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; import android.util.Log; -import java.util.ArrayList; - /** * Represents a launchable icon on the workspaces and in folders. */ diff --git a/src/com/android/launcher2/ShortcutListAdapter.java b/src/com/android/launcher2/ShortcutListAdapter.java new file mode 100644 index 000000000..be05ca4bb --- /dev/null +++ b/src/com/android/launcher2/ShortcutListAdapter.java @@ -0,0 +1,26 @@ +package com.android.launcher2; + +import com.android.launcher.R; + +import android.content.Context; +import android.content.Intent; +import android.content.Intent.ShortcutIconResource; +import android.content.pm.ResolveInfo; + +import java.util.ArrayList; + + +public class ShortcutListAdapter extends IntentListAdapter { + + public ShortcutListAdapter(Context context, String actionFilter) { + super(context, actionFilter); + + // Manually create a separate entry for creating an Application shortcut + ResolveInfo folder = new ResolveInfo(); + + folder.icon = R.drawable.ic_launcher_application; + folder.labelRes = R.string.group_applications; + folder.resolvePackageName = context.getPackageName(); + mIntentList.add(0, folder); + } +} diff --git a/src/com/android/launcher2/ShortcutsAdapter.java b/src/com/android/launcher2/ShortcutsAdapter.java index 19c3af0a8..93c500a44 100644 --- a/src/com/android/launcher2/ShortcutsAdapter.java +++ b/src/com/android/launcher2/ShortcutsAdapter.java @@ -16,16 +16,15 @@ package com.android.launcher2; +import java.util.ArrayList; + import android.content.Context; -import android.content.pm.PackageManager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; -import java.util.ArrayList; - import com.android.launcher.R; /** diff --git a/src/com/android/launcher2/SymmetricalLinearTween.java b/src/com/android/launcher2/SymmetricalLinearTween.java index 2e0ed8f03..da02242c8 100644 --- a/src/com/android/launcher2/SymmetricalLinearTween.java +++ b/src/com/android/launcher2/SymmetricalLinearTween.java @@ -17,9 +17,7 @@ package com.android.launcher2; import android.os.Handler; -import android.os.Message; import android.os.SystemClock; -import android.util.Log; /** * Provides an animation between 0.0f and 1.0f over a given duration. diff --git a/src/com/android/launcher2/UserFolder.java b/src/com/android/launcher2/UserFolder.java index d49c27aea..b46e92414 100644 --- a/src/com/android/launcher2/UserFolder.java +++ b/src/com/android/launcher2/UserFolder.java @@ -3,10 +3,8 @@ package com.android.launcher2; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; -import android.widget.ArrayAdapter; import com.android.launcher.R; @@ -91,4 +89,10 @@ public class UserFolder extends Folder implements DropTarget { super.onOpen(); requestFocus(); } + + @Override + public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset, + DragView dragView, Object dragInfo) { + return null; + } } diff --git a/src/com/android/launcher2/Utilities.java b/src/com/android/launcher2/Utilities.java index 757e48e30..c67ff99de 100644 --- a/src/com/android/launcher2/Utilities.java +++ b/src/com/android/launcher2/Utilities.java @@ -16,9 +16,8 @@ package com.android.launcher2; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.PaintDrawable; +import android.content.Context; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BlurMaskFilter; import android.graphics.Canvas; @@ -26,19 +25,19 @@ import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; -import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.TableMaskFilter; import android.graphics.Typeface; -import android.text.Layout.Alignment; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.PaintDrawable; import android.text.StaticLayout; import android.text.TextPaint; +import android.text.Layout.Alignment; import android.util.DisplayMetrics; import android.util.Log; -import android.content.res.Resources; -import android.content.Context; import com.android.launcher.R; diff --git a/src/com/android/launcher2/WidgetChooser.java b/src/com/android/launcher2/WidgetChooser.java new file mode 100644 index 000000000..ea6888e14 --- /dev/null +++ b/src/com/android/launcher2/WidgetChooser.java @@ -0,0 +1,47 @@ +package com.android.launcher2; + +import android.appwidget.AppWidgetProviderInfo; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.View; +import android.widget.AdapterView; +import android.widget.TextView; + +public class WidgetChooser extends HomeCustomizationItemGallery implements DragSource { + private DragController mDragController; + + public WidgetChooser(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void setDragController(DragController dragger) { + mDragController = dragger; + } + + public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { + Drawable[] drawables = ((TextView)view).getCompoundDrawables(); + Bitmap bmp = ((BitmapDrawable)drawables[1]).getBitmap(); + final int w = bmp.getWidth(); + final int h = bmp.getHeight(); + + // We don't really have an accurate location to use. This will do. + int screenX = mMotionDownRawX - (w / 2); + int screenY = mMotionDownRawY - h; + + AppWidgetProviderInfo info = (AppWidgetProviderInfo)getAdapter().getItem(position); + LauncherAppWidgetInfo dragInfo = new LauncherAppWidgetInfo(info.provider); + // TODO: Is this really the best place to do this? + dragInfo.minWidth = info.minWidth; + dragInfo.minHeight = info.minHeight; + mDragController.startDrag(bmp, screenX, screenY, + 0, 0, w, h, this, dragInfo, DragController.DRAG_ACTION_COPY); + return true; + } + + public void onDropCompleted(View target, boolean success) { + } +} + diff --git a/src/com/android/launcher2/WidgetListAdapter.java b/src/com/android/launcher2/WidgetListAdapter.java new file mode 100644 index 000000000..597ecf947 --- /dev/null +++ b/src/com/android/launcher2/WidgetListAdapter.java @@ -0,0 +1,131 @@ +/* + * 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.launcher2; + +import com.android.launcher.R; + +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProviderInfo; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.Region.Op; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import java.util.List; + +public class WidgetListAdapter extends BaseAdapter { + private final Launcher mLauncher; + private List<AppWidgetProviderInfo> mWidgets; + private final Canvas mCanvas = new Canvas(); + + private final int[] mTempSize = new int[2]; + private final Rect mTempRect = new Rect(); + + private static final String TAG = "Launcher.WidgetGalleryAdapter"; + + WidgetListAdapter(Launcher launcher) { + mLauncher = launcher; + mWidgets = AppWidgetManager.getInstance(launcher).getInstalledProviders(); + } + + public int getCount() { + return mWidgets.size(); + } + + public Object getItem(int position) { + return mWidgets.get(position); + } + + public long getItemId(int position) { + return position; + } + + public View getView(int position, View convertView, ViewGroup parent) { + TextView textView; + + if (convertView == null) { + LayoutInflater inflater = + (LayoutInflater)mLauncher.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + textView = (TextView) inflater.inflate( + R.layout.home_customization_drawer_item, parent, false); + } else { + textView = (TextView) convertView; + } + + AppWidgetProviderInfo info = mWidgets.get(position); + PackageManager packageManager = mLauncher.getPackageManager(); + String packageName = info.provider.getPackageName(); + Drawable drawable = null; + if (info.previewImage != 0) { + drawable = packageManager.getDrawable(packageName, info.previewImage, null); + if (drawable == null) { + Log.w(TAG, "Can't load icon drawable 0x" + Integer.toHexString(info.icon) + + " for provider: " + info.provider); + } + } + // If we don't have a preview image, create a default one + if (drawable == null) { + Resources resources = mLauncher.getResources(); + + // Determine the size the widget will take in the layout + mLauncher.getWorkspace().estimateChildSize(info.minWidth, info.minHeight, mTempSize); + + // Create a new bitmap to hold the widget preview + final int width = mTempSize[0]; + final int height = mTempSize[1]; + Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); + mCanvas.setBitmap(bitmap); + // For some reason, we must re-set the clip rect here, otherwise it will be wrong + mCanvas.clipRect(0, 0, width, height, Op.REPLACE); + + Drawable background = resources.getDrawable(R.drawable.default_widget_preview); + background.setBounds(0, 0, width, height); + background.draw(mCanvas); + + // Draw the icon vertically centered, flush left + Drawable icon = packageManager.getDrawable(packageName, info.icon, null); + background.getPadding(mTempRect); + + final int left = mTempRect.left; + final int top = (height - icon.getIntrinsicHeight()) / 2; + icon.setBounds( + left, top, left + icon.getIntrinsicWidth(), top + icon.getIntrinsicHeight()); + icon.draw(mCanvas); + + drawable = new BitmapDrawable(resources, bitmap); + } + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + textView.setCompoundDrawables(null, drawable, null, null); + textView.setText(info.label); + // Store the widget info on the associated view so we can easily fetch it later + textView.setTag(info); + + return textView; + } +} diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java index c337c3078..79fb4a9ae 100644 --- a/src/com/android/launcher2/Workspace.java +++ b/src/com/android/launcher2/Workspace.java @@ -16,8 +16,7 @@ package com.android.launcher2; -import java.util.ArrayList; -import java.util.HashSet; +import com.android.launcher.R; import android.app.WallpaperManager; import android.appwidget.AppWidgetManager; @@ -29,6 +28,7 @@ import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -43,29 +43,35 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewParent; +import android.view.animation.Animation; +import android.view.animation.Animation.AnimationListener; import android.view.animation.Interpolator; +import android.view.animation.RotateAnimation; import android.widget.Scroller; import android.widget.TextView; +import android.widget.Toast; -import com.android.launcher.R; +import java.util.ArrayList; +import java.util.HashSet; /** - * The workspace is a wide area with a wallpaper and a finite number of screens. Each - * screen contains a number of icons, folders or widgets the user can interact with. - * A workspace is meant to be used with a fixed width only. + * The workspace is a wide area with a wallpaper and a finite number of screens. + * Each screen contains a number of icons, folders or widgets the user can + * interact with. A workspace is meant to be used with a fixed width only. */ public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller { @SuppressWarnings({"UnusedDeclaration"}) private static final String TAG = "Launcher.Workspace"; private static final int INVALID_SCREEN = -1; - + /** - * The velocity at which a fling gesture will cause us to snap to the next screen + * The velocity at which a fling gesture will cause us to snap to the next + * screen */ private static final int SNAP_VELOCITY = 600; private final WallpaperManager mWallpaperManager; - + private int mDefaultScreen; private boolean mFirstLayout = true; @@ -79,31 +85,37 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag * CellInfo for the cell that is currently being dragged */ private CellLayout.CellInfo mDragInfo; - + /** * Target drop area calculated during last acceptDrop call. */ private int[] mTargetCell = null; + /** + * The CellLayout that is currently being dragged over + */ + private CellLayout mDragTargetLayout = null; + private float mLastMotionX; private float mLastMotionY; - + private final static int TOUCH_STATE_REST = 0; private final static int TOUCH_STATE_SCROLLING = 1; private int mTouchState = TOUCH_STATE_REST; + private OnTouchListener mInterceptTouchListener; private OnLongClickListener mLongClickListener; private Launcher mLauncher; private IconCache mIconCache; private DragController mDragController; - + /** * Cache of vacant cells, used during drag events and invalidated as needed. */ private CellLayout.CellInfo mVacantCache = null; - + private int[] mTempCell = new int[2]; private int[] mTempEstimate = new int[2]; @@ -111,14 +123,14 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag private int mTouchSlop; private int mMaximumVelocity; - + private static final int INVALID_POINTER = -1; private int mActivePointerId = INVALID_POINTER; - + private Drawable mPreviousIndicator; private Drawable mNextIndicator; - + private static final float NANOTIME_DIV = 1000000000.0f; private static final float SMOOTHING_SPEED = 0.75f; private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED)); @@ -129,7 +141,9 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag private static final float BASELINE_FLING_VELOCITY = 2500.f; private static final float FLING_VELOCITY_INFLUENCE = 0.4f; - + + private Paint mDropIndicatorPaint; + private static class WorkspaceOvershootInterpolator implements Interpolator { private static final float DEFAULT_TENSION = 1.3f; private float mTension; @@ -137,7 +151,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag public WorkspaceOvershootInterpolator() { mTension = DEFAULT_TENSION; } - + public void setDistance(int distance) { mTension = distance > 0 ? DEFAULT_TENSION / distance : DEFAULT_TENSION; } @@ -153,7 +167,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag return t * t * ((mTension + 1) * t + mTension) + 1.0f; } } - + /** * Used to inflate the Workspace from XML. * @@ -175,11 +189,16 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag super(context, attrs, defStyle); mWallpaperManager = WallpaperManager.getInstance(context); - - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Workspace, defStyle, 0); + + TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.Workspace, defStyle, 0); + int canonicalDeviceWidth = a.getInt(R.styleable.Workspace_canonicalDeviceWidth, 4); + int canonicalDeviceHeight = a.getInt(R.styleable.Workspace_canonicalDeviceHeight, 4); mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1); a.recycle(); + LauncherModel.updateWorkspaceLayoutCells(canonicalDeviceWidth, + canonicalDeviceHeight); setHapticFeedbackEnabled(false); initWorkspace(); } @@ -249,9 +268,10 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag int count = currentScreen.getChildCount(); for (int i = 0; i < count; i++) { View child = currentScreen.getChildAt(i); - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); - if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { - return (Folder) child; + if (child instanceof Folder) { + Folder folder = (Folder) child; + if (folder.getInfo().opened) + return folder; } } return null; @@ -266,9 +286,12 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag int count = currentScreen.getChildCount(); for (int i = 0; i < count; i++) { View child = currentScreen.getChildAt(i); - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); - if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { - folders.add((Folder) child); + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child + .getLayoutParams(); + if (child instanceof Folder) { + Folder folder = (Folder) child; + if (folder.getInfo().opened) + folders.add(folder); break; } } @@ -296,11 +319,15 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag * @param currentScreen */ void setCurrentScreen(int currentScreen) { - if (!mScroller.isFinished()) mScroller.abortAnimation(); + if (!mScroller.isFinished()) + mScroller.abortAnimation(); clearVacantCache(); mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1)); - mPreviousIndicator.setLevel(mCurrentScreen); - mNextIndicator.setLevel(mCurrentScreen); + if (mPreviousIndicator != null) { + mPreviousIndicator.setLevel(mCurrentScreen); + mNextIndicator.setLevel(mCurrentScreen); + } + scrollTo(mCurrentScreen * getWidth(), 0); updateWallpaperOffset(); invalidate(); @@ -350,6 +377,85 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag addInScreen(child, screen, x, y, spanX, spanY, false); } + void addInFullScreen(View child, int screen) { + addInScreen(child, screen, 0, 0, -1, -1); + } + + public void rotateCurrentScreensChildren() { + + // close all the folders first + final ArrayList<Folder> openFolders = getOpenFolders(); + + WorkspaceOvershootInterpolator wi = new WorkspaceOvershootInterpolator(); + RotateAnimation ra = new RotateAnimation((float) LauncherModel + .getPreviousOrientationRelativeToCurrent(), 0, + Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, + 0.5f); + ra.setInterpolator(wi); + CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen); + ra.setStartOffset(150); + ra.setDuration(650 + (int) (Math.random() * 400) - 200); + + CellLayout.CellLayoutAnimationController animationController = new CellLayout.CellLayoutAnimationController( + ra, 0.0f); + currentScreen.setLayoutAnimation(animationController); + currentScreen.setLayoutAnimationListener(new AnimationListener() { + public void onAnimationStart(Animation animation) { + // do nothing + } + + public void onAnimationRepeat(Animation animation) { + // do nothing + } + + public void onAnimationEnd(Animation animation) { + for (int j = 0; j < openFolders.size(); ++j) { + Folder folder = openFolders.get(j); + if (!folder.getInfo().opened) { + mLauncher.openFolder(folder.getInfo()); + } + } + } + }); + animationController.start(); + + for (int j = 0; j < openFolders.size(); ++j) { + mLauncher.closeFolder(openFolders.get(j)); + } + } + + public void refreshWorkspaceChildren() { + final int count = getChildCount(); + View child; + + CellLayout.LayoutParams lp; + int widthMeasureSpec = MeasureSpec.makeMeasureSpec(LauncherModel + .getLocalDeviceWidth(), MeasureSpec.EXACTLY); + int heightMeasureSpec = MeasureSpec.makeMeasureSpec(LauncherModel + .getLocalDeviceHeight(), MeasureSpec.EXACTLY); + + clearVacantCache(); + + for (int i = 0; i < count; i++) { + final CellLayout layout = (CellLayout) getChildAt(i); + int numChildren = layout.getChildCount(); + + // save reference to all current children + for (int j = 0; j < numChildren; j++) { + child = layout.getChildAt(j); + + lp = (CellLayout.LayoutParams) child.getLayoutParams(); + LauncherModelOrientationHelper.Coordinates localCoord = LauncherModel + .getLocalCoordinatesFromPreviousLocalCoordinates(lp); + + lp.cellX = localCoord.x; + lp.cellY = localCoord.y; + } + + layout.measure(widthMeasureSpec, heightMeasureSpec); + } + } + /** * Adds the specified child in the specified screen. The position and dimension of * the child are defined by x, y, spanX and spanY. @@ -381,13 +487,22 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag lp.cellHSpan = spanX; lp.cellVSpan = spanY; } - group.addView(child, insert ? 0 : -1, lp); + + // Get the canonical child id to uniquely represent this view in this screen + int childId = LauncherModel.getCanonicalCellLayoutChildId(child.getId(), screen, x, y, spanX, spanY); + if (!group.addViewToCellLayout(child, insert ? 0 : -1, childId, lp)) { + // TODO: This branch occurs when the workspace is adding views + // outside of the defined grid + // maybe we should be deleting these items from the LauncherModel? + Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout"); + } + if (!(child instanceof Folder)) { child.setHapticFeedbackEnabled(false); child.setOnLongClickListener(mLongClickListener); } if (child instanceof DropTarget) { - mDragController.addDropTarget((DropTarget)child); + mDragController.addDropTarget((DropTarget) child); } } @@ -406,6 +521,10 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag } } + public void setOnInterceptTouchListener(View.OnTouchListener listener) { + mInterceptTouchListener = listener; + } + /** * Registers the specified listener on each screen contained in this workspace. * @@ -432,14 +551,14 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0); } } - + @Override public void scrollTo(int x, int y) { super.scrollTo(x, y); mTouchX = x; mSmoothingTime = System.nanoTime() / NANOTIME_DIV; } - + @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { @@ -450,8 +569,10 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag postInvalidate(); } else if (mNextScreen != INVALID_SCREEN) { mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1)); - mPreviousIndicator.setLevel(mCurrentScreen); - mNextIndicator.setLevel(mCurrentScreen); + if (mPreviousIndicator != null) { + mPreviousIndicator.setLevel(mCurrentScreen); + mNextIndicator.setLevel(mCurrentScreen); + } Launcher.setScreen(mCurrentScreen); mNextScreen = INVALID_SCREEN; clearChildrenCache(); @@ -529,7 +650,6 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); } - if (mFirstLayout) { setHorizontalScrollBarEnabled(false); scrollTo(mCurrentScreen * width, 0); @@ -552,6 +672,12 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag childLeft += childWidth; } } + + if (LauncherApplication.isInPlaceRotationEnabled()) { + // When the device is rotated, the scroll position of the current screen + // needs to be refreshed + setCurrentScreen(getCurrentScreen()); + } } @Override @@ -611,7 +737,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag if (mCurrentScreen > 0) { getChildAt(mCurrentScreen - 1).addFocusables(views, direction); } - } else if (direction == View.FOCUS_RIGHT){ + } else if (direction == View.FOCUS_RIGHT) { if (mCurrentScreen < getChildCount() - 1) { getChildAt(mCurrentScreen + 1).addFocusables(views, direction); } @@ -634,6 +760,9 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag @Override public boolean onInterceptTouchEvent(MotionEvent ev) { + if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) { + return true; + } final boolean workspaceLocked = mLauncher.isWorkspaceLocked(); final boolean allAppsVisible = mLauncher.isAllAppsVisible(); if (workspaceLocked || allAppsVisible) { @@ -660,7 +789,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); - + switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_MOVE: { /* @@ -681,9 +810,9 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag final int touchSlop = mTouchSlop; boolean xMoved = xDiff > touchSlop; boolean yMoved = yDiff > touchSlop; - + if (xMoved || yMoved) { - + if (xMoved) { // Scroll if the user moved far enough along the X axis mTouchState = TOUCH_STATE_SCROLLING; @@ -705,14 +834,14 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag break; } - case MotionEvent.ACTION_DOWN: { - final float x = ev.getX(); - final float y = ev.getY(); - // Remember location of down touch - mLastMotionX = x; - mLastMotionY = y; - mActivePointerId = ev.getPointerId(0); - mAllowLongPress = true; + case MotionEvent.ACTION_DOWN: { + final float x = ev.getX(); + final float y = ev.getY(); + // Remember location of down touch + mLastMotionX = x; + mLastMotionY = y; + mActivePointerId = ev.getPointerId(0); + mAllowLongPress = true; /* * If being flinged and user touches the screen, initiate drag; @@ -725,36 +854,36 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: - + if (mTouchState != TOUCH_STATE_SCROLLING) { final CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen); if (!currentScreen.lastDownOnOccupiedCell()) { getLocationOnScreen(mTempCell); // Send a tap to the wallpaper if the last down was on empty space final int pointerIndex = ev.findPointerIndex(mActivePointerId); - mWallpaperManager.sendWallpaperCommand(getWindowToken(), + mWallpaperManager.sendWallpaperCommand(getWindowToken(), "android.wallpaper.tap", mTempCell[0] + (int) ev.getX(pointerIndex), mTempCell[1] + (int) ev.getY(pointerIndex), 0, null); } } - + // Release the drag clearChildrenCache(); mTouchState = TOUCH_STATE_REST; mActivePointerId = INVALID_POINTER; mAllowLongPress = false; - + if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } - break; - - case MotionEvent.ACTION_POINTER_UP: - onSecondaryPointerUp(ev); - break; + break; + + case MotionEvent.ACTION_POINTER_UP: + onSecondaryPointerUp(ev); + break; } /* @@ -763,7 +892,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag */ return mTouchState != TOUCH_STATE_REST; } - + private void onSecondaryPointerUp(MotionEvent ev) { final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; @@ -803,7 +932,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag } ViewParent parent = v.getParent(); if (parent instanceof View) { - v = (View)v.getParent(); + v = (View) v.getParent(); } else { return; } @@ -816,7 +945,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag fromScreen = toScreen; toScreen = temp; } - + final int count = getChildCount(); fromScreen = Math.max(fromScreen, 0); @@ -839,7 +968,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag @Override public boolean onTouchEvent(MotionEvent ev) { - + if (mLauncher.isWorkspaceLocked()) { return false; // We don't want the events. Let them fall through to the all apps view. } @@ -908,11 +1037,11 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); final int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId); - + final int screenWidth = getWidth(); final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth; final float scrolledPos = (float) mScrollX / screenWidth; - + if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) { // Fling hard enough to move left. // Don't fling across more than one screen at a time. @@ -948,30 +1077,32 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag return true; } - + void snapToScreen(int whichScreen) { snapToScreen(whichScreen, 0, false); } private void snapToScreen(int whichScreen, int velocity, boolean settle) { - //if (!mScroller.isFinished()) return; + // if (!mScroller.isFinished()) return; whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1)); - + clearVacantCache(); enableChildrenCache(mCurrentScreen, whichScreen); mNextScreen = whichScreen; - mPreviousIndicator.setLevel(mNextScreen); - mNextIndicator.setLevel(mNextScreen); + if (mPreviousIndicator != null) { + mPreviousIndicator.setLevel(mNextScreen); + mNextIndicator.setLevel(mNextScreen); + } View focusedChild = getFocusedChild(); if (focusedChild != null && whichScreen != mCurrentScreen && focusedChild == getChildAt(mCurrentScreen)) { focusedChild.clearFocus(); } - + final int screenDelta = Math.max(1, Math.abs(whichScreen - mCurrentScreen)); final int newX = whichScreen * getWidth(); final int delta = newX - mScrollX; @@ -980,13 +1111,13 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag if (!mScroller.isFinished()) { mScroller.abortAnimation(); } - + if (settle) { mScrollInterpolator.setDistance(screenDelta); } else { mScrollInterpolator.disableSettle(); } - + velocity = Math.abs(velocity); if (velocity > 0) { duration += (duration / (velocity / BASELINE_FLING_VELOCITY)) @@ -1002,15 +1133,15 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag void startDrag(CellLayout.CellInfo cellInfo) { View child = cellInfo.cell; - + // Make sure the drag was started by a long press as opposed to a long click. if (!child.isInTouchMode()) { return; } - + mDragInfo = cellInfo; mDragInfo.screen = mCurrentScreen; - + CellLayout current = ((CellLayout) getChildAt(mCurrentScreen)); current.onDragChild(child); @@ -1057,81 +1188,164 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag // Move internally if (mDragInfo != null) { final View cell = mDragInfo.cell; - int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen; + int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen; if (index != mDragInfo.screen) { final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen); originalCellLayout.removeView(cell); - cellLayout.addView(cell); + addInScreen(cell, index, mDragInfo.cellX, mDragInfo.cellY, + mDragInfo.spanX, mDragInfo.spanY); } mTargetCell = estimateDropCell(x - xOffset, y - yOffset, - mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, mTargetCell); - cellLayout.onDropChild(cell, mTargetCell); + mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, + mTargetCell); + cellLayout.onDropChild(cell); + // update the item's position after drop final ItemInfo info = (ItemInfo) cell.getTag(); - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell + .getLayoutParams(); + lp.cellX = mTargetCell[0]; + lp.cellY = mTargetCell[1]; + LauncherModel.moveItemInDatabase(mLauncher, info, - LauncherSettings.Favorites.CONTAINER_DESKTOP, index, lp.cellX, lp.cellY); + LauncherSettings.Favorites.CONTAINER_DESKTOP, index, + lp.cellX, lp.cellY); } } } - public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset, - DragView dragView, Object dragInfo) { + public void onDragEnter(DragSource source, int x, int y, int xOffset, + int yOffset, DragView dragView, Object dragInfo) { clearVacantCache(); } - public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, + public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo) { + + // We may need to delegate the drag to a child view. If a 1x1 item + // would land in a cell occupied by a DragTarget (e.g. a Folder), + // then drag events should be handled by that child. + + ItemInfo item = (ItemInfo)dragInfo; + CellLayout currentLayout = getCurrentDropLayout(); + + int dragPointX, dragPointY; + if (item.spanX == 1 && item.spanY == 1) { + // For a 1x1, calculate the drop cell exactly as in onDragOver + dragPointX = x - xOffset; + dragPointY = y - yOffset; + } else { + // Otherwise, use the exact drag coordinates + dragPointX = x; + dragPointY = y; + } + + // If we are dragging over a cell that contains a DropTarget that will + // accept the drop, delegate to that DropTarget. + final int[] cellXY = mTempCell; + currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY); + View child = currentLayout.getChildAt(cellXY[0], cellXY[1]); + if (child instanceof DropTarget) { + DropTarget target = (DropTarget)child; + if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) { + return target; + } + } + return null; } - public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset, + public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo) { + + ItemInfo item = (ItemInfo)dragInfo; + CellLayout currentLayout = getCurrentDropLayout(); + + if (dragInfo instanceof LauncherAppWidgetInfo) { + LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo; + + if (widgetInfo.spanX == -1) { + // Calculate the grid spans needed to fit this widget + int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight, null); + item.spanX = spans[0]; + item.spanY = spans[1]; + } + } + if (currentLayout != mDragTargetLayout) { + if (mDragTargetLayout != null) { + mDragTargetLayout.onDragComplete(); + } + mDragTargetLayout = currentLayout; + } + + // Find the top left corner of the item + int originX = x - xOffset; + int originY = y - yOffset; + + final View child = (mDragInfo == null) ? null : mDragInfo.cell; + currentLayout.visualizeDropLocation(child, originX, originY, item.spanX, item.spanY); + } + + public void onDragExit(DragSource source, int x, int y, int xOffset, + int yOffset, DragView dragView, Object dragInfo) { clearVacantCache(); + if (mDragTargetLayout != null) { + mDragTargetLayout.onDragComplete(); + mDragTargetLayout = null; + } } - private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout) { + private void onDropExternal(int x, int y, Object dragInfo, + CellLayout cellLayout) { onDropExternal(x, y, dragInfo, cellLayout, false); } - - private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout, - boolean insertAtFirst) { + + private void onDropExternal(int x, int y, Object dragInfo, + CellLayout cellLayout, boolean insertAtFirst) { // Drag from somewhere else ItemInfo info = (ItemInfo) dragInfo; - View view; + View view = null; switch (info.itemType) { case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: if (info.container == NO_ID && info instanceof ApplicationInfo) { // Came from all apps -- make a copy - info = new ShortcutInfo((ApplicationInfo)info); + info = new ShortcutInfo((ApplicationInfo) info); } - view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo)info); + view = mLauncher.createShortcut(R.layout.application, cellLayout, + (ShortcutInfo) info); break; case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER: view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, - (ViewGroup) getChildAt(mCurrentScreen), ((UserFolderInfo) info)); + (ViewGroup) getChildAt(mCurrentScreen), + ((UserFolderInfo) info)); + break; + case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: + cellLayout.setTagToCellInfoForPoint(x, y); + mLauncher.addAppWidgetFromDrop(((LauncherAppWidgetInfo)dragInfo).providerName, cellLayout.getTag()); break; default: - throw new IllegalStateException("Unknown item type: " + info.itemType); + throw new IllegalStateException("Unknown item type: " + + info.itemType); } - cellLayout.addView(view, insertAtFirst ? 0 : -1); - view.setHapticFeedbackEnabled(false); - view.setOnLongClickListener(mLongClickListener); - if (view instanceof DropTarget) { - mDragController.addDropTarget((DropTarget) view); - } - - mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell); - cellLayout.onDropChild(view, mTargetCell); - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); + // If the view is null, it has already been added. + if (view == null) { + cellLayout.onDragComplete(); + } else { + mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell); + addInScreen(view, indexOfChild(cellLayout), mTargetCell[0], + mTargetCell[1], info.spanX, info.spanY, insertAtFirst); + cellLayout.onDropChild(view); + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); - LauncherModel.addOrMoveItemInDatabase(mLauncher, info, - LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen, lp.cellX, lp.cellY); + LauncherModel.addOrMoveItemInDatabase(mLauncher, info, + LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen, + lp.cellX, lp.cellY); + } } - + /** * Return the current {@link CellLayout}, correctly picking the destination * screen while a scroll is in progress. @@ -1156,39 +1370,44 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag mVacantCache = layout.findAllVacantCells(null, ignoreView); } - return mVacantCache.findCellForSpan(mTempEstimate, spanX, spanY, false); + if (mVacantCache.findCellForSpan(mTempEstimate, spanX, spanY, false)) { + return true; + } else { + Toast.makeText(getContext(), getContext().getString(R.string.out_of_space), Toast.LENGTH_SHORT).show(); + return false; + } } - + /** * {@inheritDoc} */ public Rect estimateDropLocation(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo, Rect recycle) { final CellLayout layout = getCurrentDropLayout(); - + final CellLayout.CellInfo cellInfo = mDragInfo; final int spanX = cellInfo == null ? 1 : cellInfo.spanX; final int spanY = cellInfo == null ? 1 : cellInfo.spanY; final View ignoreView = cellInfo == null ? null : cellInfo.cell; - + final Rect location = recycle != null ? recycle : new Rect(); - + // Find drop cell and convert into rectangle - int[] dropCell = estimateDropCell(x - xOffset, y - yOffset, - spanX, spanY, ignoreView, layout, mTempCell); - + int[] dropCell = estimateDropCell(x - xOffset, y - yOffset, spanX, + spanY, ignoreView, layout, mTempCell); + if (dropCell == null) { return null; } - + layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate); location.left = mTempEstimate[0]; location.top = mTempEstimate[1]; - + layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate); location.right = mTempEstimate[0]; location.bottom = mTempEstimate[1]; - + return location; } @@ -1197,16 +1416,27 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag */ private int[] estimateDropCell(int pixelX, int pixelY, int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) { + + final int[] cellXY = mTempCell; + layout.estimateDropCell(pixelX, pixelY, spanX, spanY, cellXY); + layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate); + // Create vacant cell cache if none exists if (mVacantCache == null) { mVacantCache = layout.findAllVacantCells(null, ignoreView); } // Find the best target drop location - return layout.findNearestVacantArea(pixelX, pixelY, - spanX, spanY, mVacantCache, recycle); + return layout.findNearestVacantArea(mTempEstimate[0], mTempEstimate[1], spanX, spanY, mVacantCache, recycle); } - + + /** + * Estimate the size that a child with the given dimensions will take in the current screen. + */ + void estimateChildSize(int minWidth, int minHeight, int[] result) { + ((CellLayout)getChildAt(mCurrentScreen)).estimateChildSize(minWidth, minHeight, result); + } + void setLauncher(Launcher launcher) { mLauncher = launcher; } @@ -1218,14 +1448,14 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag public void onDropCompleted(View target, boolean success) { clearVacantCache(); - if (success){ + if (success) { if (target != this && mDragInfo != null) { final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); cellLayout.removeView(mDragInfo.cell); if (mDragInfo.cell instanceof DropTarget) { mDragController.removeDropTarget((DropTarget)mDragInfo.cell); } - //final Object tag = mDragInfo.cell.getTag(); + // final Object tag = mDragInfo.cell.getTag(); } } else { if (mDragInfo != null) { @@ -1240,18 +1470,22 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag public void scrollLeft() { clearVacantCache(); if (mScroller.isFinished()) { - if (mCurrentScreen > 0) snapToScreen(mCurrentScreen - 1); + if (mCurrentScreen > 0) + snapToScreen(mCurrentScreen - 1); } else { - if (mNextScreen > 0) snapToScreen(mNextScreen - 1); + if (mNextScreen > 0) + snapToScreen(mNextScreen - 1); } } public void scrollRight() { clearVacantCache(); if (mScroller.isFinished()) { - if (mCurrentScreen < getChildCount() -1) snapToScreen(mCurrentScreen + 1); + if (mCurrentScreen < getChildCount() - 1) + snapToScreen(mCurrentScreen + 1); } else { - if (mNextScreen < getChildCount() -1) snapToScreen(mNextScreen + 1); + if (mNextScreen < getChildCount() - 1) + snapToScreen(mNextScreen + 1); } } @@ -1279,7 +1513,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) { Folder f = (Folder) child; - if (f.getInfo() == tag) { + if (f.getInfo() == tag && f.getInfo().opened) { return f; } } @@ -1309,7 +1543,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag public boolean allowLongPress() { return mAllowLongPress; } - + /** * Set true to allow long-press events to be triggered, usually checked by * {@link Launcher} to accept or block dpad-initiated long-presses. @@ -1337,17 +1571,17 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag public void run() { final ArrayList<View> childrenToRemove = new ArrayList<View>(); childrenToRemove.clear(); - + int childCount = layout.getChildCount(); for (int j = 0; j < childCount; j++) { final View view = layout.getChildAt(j); Object tag = view.getTag(); - + if (tag instanceof ShortcutInfo) { final ShortcutInfo info = (ShortcutInfo) tag; final Intent intent = info.intent; final ComponentName name = intent.getComponent(); - + if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { for (String packageName: packageNames) { if (packageName.equals(name.getPackageName())) { @@ -1363,12 +1597,12 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1); final int contentsCount = contents.size(); boolean removedFromFolder = false; - + for (int k = 0; k < contentsCount; k++) { final ShortcutInfo appInfo = contents.get(k); final Intent intent = appInfo.intent; final ComponentName name = intent.getComponent(); - + if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { for (String packageName: packageNames) { if (packageName.equals(name.getPackageName())) { @@ -1381,11 +1615,12 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag } } } - + contents.removeAll(toRemove); if (removedFromFolder) { final Folder folder = getOpenFolder(); - if (folder != null) folder.notifyDataSetChanged(); + if (folder != null) + folder.notifyDataSetChanged(); } } else if (tag instanceof LiveFolderInfo) { final LiveFolderInfo info = (LiveFolderInfo) tag; @@ -1398,7 +1633,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag if (packageName.equals(providerInfo.packageName)) { // TODO: This should probably be done on a worker thread LauncherModel.deleteItemFromDatabase(mLauncher, info); - childrenToRemove.add(view); + childrenToRemove.add(view); } } } @@ -1411,13 +1646,13 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag if (packageName.equals(provider.provider.getPackageName())) { // TODO: This should probably be done on a worker thread LauncherModel.deleteItemFromDatabase(mLauncher, info); - childrenToRemove.add(view); + childrenToRemove.add(view); } } } } } - + childCount = childrenToRemove.size(); for (int j = 0; j < childCount; j++) { View child = childrenToRemove.get(j); @@ -1426,7 +1661,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag mDragController.removeDropTarget((DropTarget)child); } } - + if (childCount > 0) { layout.requestLayout(); layout.invalidate(); @@ -1456,7 +1691,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION && Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) { final int appCount = apps.size(); - for (int k=0; k<appCount; k++) { + for (int k = 0; k < appCount; k++) { ApplicationInfo app = apps.get(k); if (app.componentName.equals(name)) { info.setIcon(mIconCache.getIcon(info.intent)); diff --git a/src/com/android/launcher2/allapps.rs b/src/com/android/launcher2/allapps.rs new file mode 100644 index 000000000..fad8907a7 --- /dev/null +++ b/src/com/android/launcher2/allapps.rs @@ -0,0 +1,390 @@ +#pragma version(1) + +#pragma rs java_package_name(com.android.launcher2) + +#include "rs_graphics.rsh" + +#define PI 3.14159f + +// Constants from Java +int COLUMNS_PER_PAGE_PORTRAIT; +int ROWS_PER_PAGE_PORTRAIT; +int COLUMNS_PER_PAGE_LANDSCAPE; +int ROWS_PER_PAGE_LANDSCAPE; + +int gIconCount; +int gSelectedIconIndex = -1; +rs_allocation gSelectedIconTexture; +rs_allocation gHomeButton; + +rs_program_fragment gPFTexNearest; +rs_program_fragment gPFTexMip; +rs_program_fragment gPFTexMipAlpha; +rs_program_vertex gPVCurve; +rs_program_store gPS; +rs_mesh gSMCell; + +rs_allocation *gIconIDs; +rs_allocation *gLabelIDs; + +typedef struct VpConsts { + float4 Position; + float4 ScaleOffset; + float2 BendPos; + float2 ImgSize; +} VpConsts_t; +VpConsts_t *vpConstants; + + +#pragma rs export_var(COLUMNS_PER_PAGE_PORTRAIT, ROWS_PER_PAGE_PORTRAIT, COLUMNS_PER_PAGE_LANDSCAPE, ROWS_PER_PAGE_LANDSCAPE, gIconCount, gSelectedIconIndex, gSelectedIconTexture, gHomeButton, gTargetPos, gPFTexNearest, gPFTexMip, gPFTexMipAlpha, gPVCurve, gPS, gSMCell, gIconIDs, gLabelIDs, vpConstants) +#pragma rs export_func(move, moveTo, setZoom, fling) + + +// Attraction to center values from page edge to page center. +static float g_AttractionTable[9] = {20.f, 20.f, 20.f, 10.f, -10.f, -20.f, -20.f, -20.f, -20.f}; +static float g_FrictionTable[9] = {10.f, 10.f, 11.f, 15.f, 15.f, 11.f, 10.f, 10.f, 10.f}; +static float g_PhysicsTableSize = 7; + +static float gZoomTarget; +static float gTargetPos; +static float g_PosPage = 0.f; +static float g_PosVelocity = 0.f; +static float g_LastPositionX = 0.f; +static bool g_LastTouchDown = false; +static float g_DT; +static int64_t g_LastTime; +static int g_PosMax; +static float g_Zoom = 0.f; +static float g_Animation = 1.f; +static float g_OldPosPage; +static float g_OldPosVelocity; +static float g_OldZoom; +static float g_MoveToTotalTime = 0.2f; +static float g_MoveToTime = 0.f; +static float g_MoveToOldPos = 0.f; + +static int g_Cols; +static int g_Rows; + +// Drawing constants, should be parameters ====== +#define VIEW_ANGLE 1.28700222f + + +static void updateReadback() { + if ((g_OldPosPage != g_PosPage) || + (g_OldPosVelocity != g_PosVelocity) || + (g_OldZoom != g_Zoom)) { + + g_OldPosPage = g_PosPage; + g_OldPosVelocity = g_PosVelocity; + g_OldZoom = g_Zoom; + + int i[3]; + i[0] = g_PosPage * (1 << 16); + i[1] = g_PosVelocity * (1 << 16); + i[2] = g_OldZoom * (1 << 16); + rsSendToClient(&i[0], 1, 12, 1); + } +} + +void init() { +} + +void move(float newPos) { + if (g_LastTouchDown) { + float dx = -(newPos - g_LastPositionX); + g_PosVelocity = 0; + g_PosPage += dx * 5.2f; + + float pmin = -0.49f; + float pmax = g_PosMax + 0.49f; + g_PosPage = clamp(g_PosPage, pmin, pmax); + } + g_LastTouchDown = true; + g_LastPositionX = newPos; + g_MoveToTime = 0; +} + +void moveTo(float targetPos) { + gTargetPos = targetPos; + g_MoveToTime = g_MoveToTotalTime; + g_PosVelocity = 0; + g_MoveToOldPos = g_PosPage; +} + +void setZoom(float z, /*bool*/ int animate) { + gZoomTarget = z; + if (gZoomTarget < 0.001f) { + gZoomTarget = 0; + } + if (!animate) { + g_Zoom = gZoomTarget; + } + updateReadback(); +} + +void fling(float newPos, float vel) { + move(newPos); + + g_LastTouchDown = false; + g_PosVelocity = -vel * 4; + float av = fabs(g_PosVelocity); + float minVel = 3.5f; + + minVel *= 1.f - (fabs(rsFrac(g_PosPage + 0.5f) - 0.5f) * 0.45f); + + if (av < minVel && av > 0.2f) { + if (g_PosVelocity > 0) { + g_PosVelocity = minVel; + } else { + g_PosVelocity = -minVel; + } + } + + if (g_PosPage <= 0) { + g_PosVelocity = max(0.f, g_PosVelocity); + } + if (g_PosPage > g_PosMax) { + g_PosVelocity = min(0.f, g_PosVelocity); + } +} + +// Interpolates values in the range 0..1 to a curve that eases in +// and out. +static float getInterpolation(float input) { + return (cos((input + 1) * PI) * 0.5f) + 0.5f; +} + + +static void updatePos() { + if (g_LastTouchDown) { + return; + } + + float tablePosNorm = rsFrac(g_PosPage + 0.5f); + float tablePosF = tablePosNorm * g_PhysicsTableSize; + int tablePosI = tablePosF; + float tablePosFrac = tablePosF - tablePosI; + float accel = mix(g_AttractionTable[tablePosI], + g_AttractionTable[tablePosI + 1], + tablePosFrac) * g_DT; + float friction = mix(g_FrictionTable[tablePosI], + g_FrictionTable[tablePosI + 1], + tablePosFrac) * g_DT; + + if (g_MoveToTime) { + // New position is old posiition + (total distance) * (interpolated time) + g_PosPage = g_MoveToOldPos + (gTargetPos - g_MoveToOldPos) * getInterpolation((g_MoveToTotalTime - g_MoveToTime) / g_MoveToTotalTime); + g_MoveToTime -= g_DT; + if (g_MoveToTime <= 0) { + g_MoveToTime = 0; + g_PosPage = gTargetPos; + } + return; + } + + // If our velocity is low OR acceleration is opposing it, apply it. + if (fabs(g_PosVelocity) < 4.0f || (g_PosVelocity * accel) < 0) { + g_PosVelocity += accel; + } + //RS_DEBUG(g_PosPage); + //RS_DEBUG(g_PosVelocity); + //RS_DEBUG(friction); + //RS_DEBUG(accel); + + // Normal physics + if (g_PosVelocity > 0) { + g_PosVelocity -= friction; + g_PosVelocity = max(g_PosVelocity, 0.f); + } else { + g_PosVelocity += friction; + g_PosVelocity = min(g_PosVelocity, 0.f); + } + + if ((friction > fabs(g_PosVelocity)) && (friction > fabs(accel))) { + // Special get back to center and overcome friction physics. + float t = tablePosNorm - 0.5f; + if (fabs(t) < (friction * g_DT)) { + // really close, just snap + g_PosPage = round(g_PosPage); + g_PosVelocity = 0; + } else { + if (t > 0) { + g_PosVelocity = -friction; + } else { + g_PosVelocity = friction; + } + } + } + + // Check for out of boundry conditions. + if (g_PosPage < 0 && g_PosVelocity < 0) { + float damp = 1.0f + (g_PosPage * 4); + damp = clamp(damp, 0.f, 0.9f); + g_PosVelocity *= damp; + } + if (g_PosPage > g_PosMax && g_PosVelocity > 0) { + float damp = 1.0f - ((g_PosPage - g_PosMax) * 4); + damp = clamp(damp, 0.f, 0.9f); + g_PosVelocity *= damp; + } + + g_PosPage += g_PosVelocity * g_DT; + g_PosPage = clamp(g_PosPage, -0.49f, g_PosMax + 0.49f); +} + +static void +draw_home_button() +{ + color(1.0f, 1.0f, 1.0f, 1.0f); + rsgBindTexture(gPFTexNearest, 0, gHomeButton); + + float w = rsgGetWidth(); + float h = rsgGetHeight(); + float tw = rsAllocationGetDimX(gHomeButton); + float th = rsAllocationGetDimY(gHomeButton); + + float x; + float y; + if (w > h) { + x = w - (tw * (1 - g_Animation)) + 20; + y = (h - th) * 0.5f; + } else { + x = (w - tw) / 2; + y = -g_Animation * th; + y -= 30; // move the house to the edge of the screen as it doesn't fill the texture. + } + + rsgDrawSpriteScreenspace(x, y, 0, tw, th); +} + +static void drawFrontGrid(float rowOffset, float p) +{ + float h = rsgGetHeight(); + float w = rsgGetWidth(); + + int intRowOffset = rowOffset; + float rowFrac = rowOffset - intRowOffset; + float colWidth = 120.f;//w / 4; + float rowHeight = colWidth + 25.f; + float yoff = 0.5f * h + 1.5f * rowHeight; + + int row, col; + int colCount = 4; + if (w > h) { + colCount = 6; + rowHeight -= 12.f; + yoff = 0.47f * h + 1.0f * rowHeight; + } + + int iconNum = (intRowOffset - 5) * colCount; + + rsgBindProgramVertex(gPVCurve); + + vpConstants->Position.z = p; + + color(1.0f, 1.0f, 1.0f, 1.0f); + for (row = -5; row < 15; row++) { + float y = yoff - ((-rowFrac + row) * rowHeight); + + for (col=0; col < colCount; col++) { + if (iconNum >= gIconCount) { + return; + } + + if (iconNum >= 0) { + float x = colWidth * col + (colWidth / 2); + vpConstants->Position.x = x + 0.2f; + + if (gSelectedIconIndex == iconNum && !p && gSelectedIconTexture.p) { + rsgBindProgramFragment(gPFTexNearest); + rsgBindTexture(gPFTexNearest, 0, gSelectedIconTexture); + vpConstants->ImgSize.x = rsAllocationGetDimX(gSelectedIconTexture); + vpConstants->ImgSize.y = rsAllocationGetDimY(gSelectedIconTexture); + vpConstants->Position.y = y - (rsAllocationGetDimY(gSelectedIconTexture) + - rsAllocationGetDimY(gIconIDs[iconNum])) * 0.5f; + rsgDrawMesh(gSMCell); + } + + rsgBindProgramFragment(gPFTexMip); + vpConstants->ImgSize.x = rsAllocationGetDimX(gIconIDs[iconNum]); + vpConstants->ImgSize.y = rsAllocationGetDimY(gIconIDs[iconNum]); + vpConstants->Position.y = y - 0.2f; + rsgBindTexture(gPFTexMip, 0, gIconIDs[iconNum]); + rsgDrawMesh(gSMCell); + + rsgBindProgramFragment(gPFTexMipAlpha); + vpConstants->ImgSize.x = rsAllocationGetDimX(gLabelIDs[iconNum]); + vpConstants->ImgSize.y = rsAllocationGetDimY(gLabelIDs[iconNum]); + vpConstants->Position.y = y - 64.f - 0.2f; + rsgBindTexture(gPFTexMipAlpha, 0, gLabelIDs[iconNum]); + rsgDrawMesh(gSMCell); + } + iconNum++; + } + } +} + + +int root() +{ + // Compute dt in seconds. + int64_t newTime = rsUptimeMillis(); + g_DT = (newTime - g_LastTime) * 0.001f; + g_LastTime = newTime; + + // physics may break if DT is large. + g_DT = min(g_DT, 0.1f); + + if (g_Zoom != gZoomTarget) { + float dz = g_DT * 1.7f; + if (gZoomTarget < 0.5f) { + dz = -dz; + } + if (fabs(g_Zoom - gZoomTarget) < fabs(dz)) { + g_Zoom = gZoomTarget; + } else { + g_Zoom += dz; + } + updateReadback(); + } + g_Animation = pow(1.f - g_Zoom, 3.f); + + // Set clear value to dim the background based on the zoom position. + if ((g_Zoom < 0.001f) && (gZoomTarget < 0.001f)) { + rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f); + // When we're zoomed out and not tracking motion events, reset the pos to 0. + if (!g_LastTouchDown) { + g_PosPage = 0; + } + return 0; + } else { + rsgClearColor(0.0f, 0.0f, 0.0f, g_Zoom); + } + + rsgBindProgramStore(gPS); + + // icons & labels + if (rsgGetWidth() > rsgGetHeight()) { + g_Cols = COLUMNS_PER_PAGE_LANDSCAPE; + g_Rows = ROWS_PER_PAGE_LANDSCAPE; + } else { + g_Cols = COLUMNS_PER_PAGE_PORTRAIT; + g_Rows = ROWS_PER_PAGE_PORTRAIT; + } + + g_PosMax = ((gIconCount + (g_Cols-1)) / g_Cols) - g_Rows; + if (g_PosMax < 0) g_PosMax = 0; + + updatePos(); + updateReadback(); + + // Draw the icons ======================================== + drawFrontGrid(g_PosPage, g_Animation); + + rsgBindProgramFragment(gPFTexNearest); + draw_home_button(); + return (g_PosVelocity != 0) || rsFrac(g_PosPage) || g_Zoom != gZoomTarget || (g_MoveToTime != 0); +} + + |