diff --git a/Android.mk b/Android.mk index dfe6788f6c..89e626bddb 100644 --- a/Android.mk +++ b/Android.mk @@ -30,7 +30,7 @@ LOCAL_CERTIFICATE := shared LOCAL_OVERRIDES_PACKAGES := Home -LOCAL_PROGUARD_FLAGS := -include $(LOCAL_PATH)/proguard.flags +LOCAL_PROGUARD_FLAG_FILES := proguard.flags include $(BUILD_PACKAGE) diff --git a/print_db.py b/print_db.py new file mode 100755 index 0000000000..ebcba6cee9 --- /dev/null +++ b/print_db.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python2.5 + +import cgi +import os +import shutil +import sys +import sqlite3 + +SCREENS = 5 +COLUMNS = 4 +ROWS = 4 +CELL_SIZE = 110 + +DIR = "db_files" +AUTO_FILE = DIR + "/launcher.db" +INDEX_FILE = DIR + "/index.html" + +def usage(): + print "usage: print_db.py launcher.db -- prints a launcher.db" + print "usage: print_db.py -- adb pulls a launcher.db from a device" + print " and prints it" + print + print "The dump will be created in a directory called db_files in cwd." + print "This script will delete any db_files directory you have now" + + +def make_dir(): + shutil.rmtree(DIR, True) + os.makedirs(DIR) + +def pull_file(fn): + print "pull_file: " + fn + rv = os.system("adb pull" + + " /data/data/com.android.launcher/databases/launcher.db" + + " " + fn); + if rv != 0: + print "adb pull failed" + sys.exit(1) + +def get_favorites(conn): + c = conn.cursor() + c.execute("SELECT * FROM favorites") + columns = [d[0] for d in c.description] + rows = [] + for row in c: + rows.append(row) + return columns,rows + +def print_intent(out, id, i, cell): + if cell: + out.write("""shortcut""" % ( + cgi.escape(cell, True) + )) + + +def print_icon(out, id, i, cell): + if cell: + icon_fn = "icon_%d.png" % id + out.write("""""" % ( icon_fn )) + f = file(DIR + "/" + icon_fn, "w") + f.write(cell) + f.close() + +def print_cell(out, id, i, cell): + if not cell is None: + out.write(cgi.escape(str(cell))) + +FUNCTIONS = { + "intent": print_intent, + "icon": print_icon +} + +def process_file(fn): + print "process_file: " + fn + conn = sqlite3.connect(fn) + columns,rows = get_favorites(conn) + data = [dict(zip(columns,row)) for row in rows] + + out = file(INDEX_FILE, "w") + out.write(""" + + + + +""") + + # Data table + out.write("Favorites table
\n") + out.write(""" + + +""") + print_functions = [] + for col in columns: + print_functions.append(FUNCTIONS.get(col, print_cell)) + for i in range(0,len(columns)): + col = columns[i] + out.write(""" +""" % ( col )) + out.write(""" + +""") + for row in rows: + out.write(""" +""") + for i in range(0,len(row)): + cell = row[i] + # row[0] is always _id + out.write(""" +""") + out.write(""" +""") + out.write("""
%s
""") + print_functions[i](out, row[0], row, cell) + out.write("""
+""") + + # Pages + screens = [] + for i in range(0,SCREENS): + screen = [] + for j in range(0,ROWS): + m = [] + for k in range(0,COLUMNS): + m.append(None) + screen.append(m) + screens.append(screen) + occupied = "occupied" + for row in data: + screen = screens[row["screen"]] + # desktop + if row["container"] != -100: + continue + cellX = row["cellX"] + cellY = row["cellY"] + spanX = row["spanX"] + spanY = row["spanY"] + for j in range(cellY, cellY+spanY): + for k in range(cellX, cellX+spanX): + screen[j][k] = occupied + screen[cellY][cellX] = row + i=0 + for screen in screens: + out.write("
Screen %d
\n" % i) + out.write("\n") + for m in screen: + out.write(" \n") + for cell in m: + if cell is None: + out.write(" \n" % + (CELL_SIZE, CELL_SIZE)) + elif cell == occupied: + pass + else: + cellX = cell["cellX"] + cellY = cell["cellY"] + spanX = cell["spanX"] + spanY = cell["spanY"] + intent = cell["intent"] + if intent: + title = "title=\"%s\"" % cgi.escape(cell["intent"], True) + else: + title = "" + out.write((" \n") + out.write("\n") + out.write("
") % ( + spanX, spanY, + (CELL_SIZE*spanX), (CELL_SIZE*spanY), + title)) + itemType = cell["itemType"] + if itemType == 0: + out.write("""\n""" % ( cell["_id"] )) + out.write("
\n") + out.write(cgi.escape(cell["title"]) + "
(app)") + elif itemType == 1: + out.write("""\n""" % ( cell["_id"] )) + out.write("
\n") + out.write(cgi.escape(cell["title"]) + "
(shortcut)") + elif itemType == 2: + out.write("""folder""") + elif itemType == 3: + out.write("""live folder""") + elif itemType == 4: + out.write("widget %d
\n" % cell["appWidgetId"]) + elif itemType == 1000: + out.write("""clock""") + elif itemType == 1001: + out.write("""search""") + elif itemType == 1002: + out.write("""photo frame""") + else: + out.write("unknown type: %d" % itemType) + out.write("
\n") + i=i+1 + + out.write(""" + + +""") + + out.close() + +def main(argv): + if len(argv) == 1: + make_dir() + pull_file(AUTO_FILE) + process_file(AUTO_FILE) + elif len(argv) == 2: + make_dir() + process_file(argv[1]) + else: + usage() + +if __name__=="__main__": + main(sys.argv) diff --git a/res/drawable-hdpi/all_apps_button_focused.png b/res/drawable-hdpi/all_apps_button_focused.png index 55574ed63f..ace493bb06 100644 Binary files a/res/drawable-hdpi/all_apps_button_focused.png and b/res/drawable-hdpi/all_apps_button_focused.png differ diff --git a/res/drawable-hdpi/all_apps_button_normal.png b/res/drawable-hdpi/all_apps_button_normal.png index 7e7ec868dd..cde455d2d9 100644 Binary files a/res/drawable-hdpi/all_apps_button_normal.png and b/res/drawable-hdpi/all_apps_button_normal.png differ diff --git a/res/drawable-hdpi/all_apps_button_pressed.png b/res/drawable-hdpi/all_apps_button_pressed.png index 3ccb5afb7b..d5f9f54615 100644 Binary files a/res/drawable-hdpi/all_apps_button_pressed.png and b/res/drawable-hdpi/all_apps_button_pressed.png differ diff --git a/res/drawable-hdpi/hotseat_bg_center.9.png b/res/drawable-hdpi/hotseat_bg_center.9.png index 468e766c4d..c69bc870a7 100644 Binary files a/res/drawable-hdpi/hotseat_bg_center.9.png and b/res/drawable-hdpi/hotseat_bg_center.9.png differ diff --git a/res/drawable-hdpi/hotseat_bg_left.9.png b/res/drawable-hdpi/hotseat_bg_left.9.png index 433a10e1ba..ca901da8ec 100644 Binary files a/res/drawable-hdpi/hotseat_bg_left.9.png and b/res/drawable-hdpi/hotseat_bg_left.9.png differ diff --git a/res/drawable-hdpi/hotseat_bg_right.9.png b/res/drawable-hdpi/hotseat_bg_right.9.png index 4ea2a734b2..810c4d489c 100644 Binary files a/res/drawable-hdpi/hotseat_bg_right.9.png and b/res/drawable-hdpi/hotseat_bg_right.9.png differ diff --git a/res/drawable-hdpi/hotseat_browser_focused.png b/res/drawable-hdpi/hotseat_browser_focused.png index 6717ad28c0..4020a8909d 100644 Binary files a/res/drawable-hdpi/hotseat_browser_focused.png and b/res/drawable-hdpi/hotseat_browser_focused.png differ diff --git a/res/drawable-hdpi/hotseat_browser_normal.png b/res/drawable-hdpi/hotseat_browser_normal.png index d02fdd95b1..ebdab6273d 100644 Binary files a/res/drawable-hdpi/hotseat_browser_normal.png and b/res/drawable-hdpi/hotseat_browser_normal.png differ diff --git a/res/drawable-hdpi/hotseat_browser_pressed.png b/res/drawable-hdpi/hotseat_browser_pressed.png index 71df2d1980..bfa23b371d 100644 Binary files a/res/drawable-hdpi/hotseat_browser_pressed.png and b/res/drawable-hdpi/hotseat_browser_pressed.png differ diff --git a/res/drawable-hdpi/hotseat_left.png b/res/drawable-hdpi/hotseat_left.png deleted file mode 100644 index 5dabf57ec9..0000000000 Binary files a/res/drawable-hdpi/hotseat_left.png and /dev/null differ diff --git a/res/drawable-hdpi/hotseat_phone_focused.png b/res/drawable-hdpi/hotseat_phone_focused.png index 3e84a583bf..f81f0a8127 100644 Binary files a/res/drawable-hdpi/hotseat_phone_focused.png and b/res/drawable-hdpi/hotseat_phone_focused.png differ diff --git a/res/drawable-hdpi/hotseat_phone_normal.png b/res/drawable-hdpi/hotseat_phone_normal.png index e8a869c52f..2a16f9c0d3 100644 Binary files a/res/drawable-hdpi/hotseat_phone_normal.png and b/res/drawable-hdpi/hotseat_phone_normal.png differ diff --git a/res/drawable-hdpi/hotseat_phone_pressed.png b/res/drawable-hdpi/hotseat_phone_pressed.png index dc4ad6e6ee..a6c2baf6c1 100644 Binary files a/res/drawable-hdpi/hotseat_phone_pressed.png and b/res/drawable-hdpi/hotseat_phone_pressed.png differ diff --git a/res/drawable-hdpi/hotseat_right.png b/res/drawable-hdpi/hotseat_right.png deleted file mode 100644 index 114bcb588d..0000000000 Binary files a/res/drawable-hdpi/hotseat_right.png and /dev/null differ diff --git a/res/drawable-xlarge/home_screen_bg.9.png b/res/drawable-xlarge/home_screen_bg.9.png new file mode 100644 index 0000000000..d939d5c1ba Binary files /dev/null and b/res/drawable-xlarge/home_screen_bg.9.png differ diff --git a/res/values-xlarge/config.xml b/res/values-xlarge/config.xml index f6f66461b1..c7c68e22f6 100644 --- a/res/values-xlarge/config.xml +++ b/res/values-xlarge/config.xml @@ -38,4 +38,11 @@ the drag view should be offset from the position of the original view. --> 0 12 + + + 900 + + + 180 diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 790f835999..c83986be22 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -37,4 +37,8 @@ 14dip + + + 20dp diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java index 84b26f21c3..0f1d46998b 100644 --- a/src/com/android/launcher2/CellLayout.java +++ b/src/com/android/launcher2/CellLayout.java @@ -31,18 +31,19 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.util.AttributeSet; -import android.util.Log; import android.view.ContextMenu; import android.view.MotionEvent; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.animation.Animation; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; import android.view.animation.LayoutAnimationController; import java.util.Arrays; -public class CellLayout extends ViewGroup { +public class CellLayout extends ViewGroup implements Dimmable { static final String TAG = "CellLayout"; private int mCellWidth; @@ -75,17 +76,27 @@ public class CellLayout extends ViewGroup { private float mBackgroundAlpha; private final Rect mBackgroundLayoutRect = new Rect(); + private Drawable mBackground; - private Drawable mBackgroundHover; - // If we're actively dragging something over this screen and it's small, - // mHover is true + private Drawable mBackgroundMini; + private Drawable mBackgroundMiniHover; + // If we're actively dragging something over this screen and it's small, mHover is true private boolean mHover = false; - private final RectF mDragRect = new RectF(); private final Point mDragCenter = new Point(); private Drawable mDragRectDrawable; + // These arrays are used to implement the drag visualization on x-large screens. + // They are used as circular arrays, indexed by mDragRectCurrent. + private Rect[] mDragRects = new Rect[8]; + private int[] mDragRectAlphas = new int[mDragRects.length]; + private InterruptibleInOutAnimator[] mDragRectAnims = + new InterruptibleInOutAnimator[mDragRects.length]; + + // Used as an index into the above 3 arrays; indicates which is the most current value. + private int mDragRectCurrent = 0; + private Drawable mCrosshairsDrawable = null; private ValueAnimator mCrosshairsAnimator = null; private float mCrosshairsVisibility = 0.0f; @@ -137,13 +148,18 @@ public class CellLayout extends ViewGroup { if (LauncherApplication.isScreenXLarge()) { final Resources res = getResources(); - mBackground = res.getDrawable(R.drawable.mini_home_screen_bg); + mBackgroundMini = res.getDrawable(R.drawable.mini_home_screen_bg); + mBackgroundMini.setFilterBitmap(true); + mBackground = res.getDrawable(R.drawable.home_screen_bg); mBackground.setFilterBitmap(true); - mBackgroundHover = res.getDrawable(R.drawable.mini_home_screen_bg_hover); - mBackgroundHover.setFilterBitmap(true); + mBackgroundMiniHover = res.getDrawable(R.drawable.mini_home_screen_bg_hover); + mBackgroundMiniHover.setFilterBitmap(true); + + // Initialize the data structures used for the drag visualization. mDragRectDrawable = res.getDrawable(R.drawable.rounded_rect_green); mCrosshairsDrawable = res.getDrawable(R.drawable.gardening_crosshairs); + Interpolator interp = new DecelerateInterpolator(2.5f); // Quint ease out // Set up the animation for fading the crosshairs in and out int animDuration = res.getInteger(R.integer.config_crosshairsFadeInTime); @@ -154,6 +170,32 @@ public class CellLayout extends ViewGroup { CellLayout.this.invalidate(); } }); + mCrosshairsAnimator.setInterpolator(interp); + + for (int i = 0; i < mDragRects.length; i++) { + mDragRects[i] = new Rect(); + } + + // When dragging things around the home screens, we show a green outline of + // where the item will land. The outlines gradually fade out, leaving a trail + // behind the drag path. + // Set up all the animations that are used to implement this fading. + final int duration = res.getInteger(R.integer.config_dragOutlineFadeTime); + final int fromAlphaValue = 0; + final int toAlphaValue = res.getInteger(R.integer.config_dragOutlineMaxAlpha); + for (int i = 0; i < mDragRectAnims.length; i++) { + final InterruptibleInOutAnimator anim = + new InterruptibleInOutAnimator(duration, fromAlphaValue, toAlphaValue); + anim.setInterpolator(interp); + final int thisIndex = i; + anim.addUpdateListener(new AnimatorUpdateListener() { + public void onAnimationUpdate(ValueAnimator animation) { + mDragRectAlphas[thisIndex] = (Integer) animation.getAnimatedValue(); + CellLayout.this.invalidate(mDragRects[thisIndex]); + } + }); + mDragRectAnims[i] = anim; + } } } @@ -176,7 +218,14 @@ public class CellLayout extends ViewGroup { @Override public void dispatchDraw(Canvas canvas) { if (mBackgroundAlpha > 0.0f) { - final Drawable bg = mHover ? mBackgroundHover : mBackground; + Drawable bg; + if (mHover && getScaleX() < 0.5f) { + bg = mBackgroundMiniHover; + } else if (getScaleX() < 0.5f) { + bg = mBackgroundMini; + } else { + bg = mBackground; + } bg.setAlpha((int) (mBackgroundAlpha * 255)); bg.draw(canvas); } @@ -189,16 +238,6 @@ public class CellLayout extends ViewGroup { final int countX = mCountX; final int countY = mCountY; - if (!mDragRect.isEmpty()) { - mDragRectDrawable.setBounds( - (int)mDragRect.left, - (int)mDragRect.top, - (int)mDragRect.right, - (int)mDragRect.bottom); - mDragRectDrawable.setAlpha((int) (mCrosshairsVisibility * 255)); - mDragRectDrawable.draw(canvas); - } - final float MAX_ALPHA = 0.4f; final int MAX_VISIBLE_DISTANCE = 600; final float DISTANCE_MULTIPLIER = 0.002f; @@ -225,9 +264,32 @@ public class CellLayout extends ViewGroup { } x += mCellWidth + mWidthGap; } + + for (int i = 0; i < mDragRects.length; i++) { + int alpha = mDragRectAlphas[i]; + if (alpha > 0) { + mDragRectDrawable.setAlpha(alpha); + mDragRectDrawable.setBounds(mDragRects[i]); + mDragRectDrawable.draw(canvas); + } + } } } + public void setDimmableProgress(float progress) { + for (int i = 0; i < getChildCount(); i++) { + Dimmable d = (Dimmable) getChildAt(i); + d.setDimmableProgress(progress); + } + } + + public float getDimmableProgress() { + if (getChildCount() > 0) { + return ((Dimmable) getChildAt(0)).getDimmableProgress(); + } + return 0.0f; + } + @Override public void cancelLongPress() { super.cancelLongPress(); @@ -581,8 +643,11 @@ public class CellLayout extends ViewGroup { if (mBackground != null) { mBackground.setBounds(mBackgroundLayoutRect); } - if (mBackgroundHover != null) { - mBackgroundHover.setBounds(mBackgroundLayoutRect); + if (mBackgroundMiniHover != null) { + mBackgroundMiniHover.setBounds(mBackgroundLayoutRect); + } + if (mBackgroundMini != null) { + mBackgroundMini.setBounds(mBackgroundLayoutRect); } } @@ -723,13 +788,23 @@ public class CellLayout extends ViewGroup { final int left = topLeft[0]; final int top = topLeft[1]; - // Now find the bottom right - final int[] bottomRight = mTmpPoint; - cellToPoint(nearest[0] + spanX - 1, nearest[1] + spanY - 1, bottomRight); - bottomRight[0] += mCellWidth; - bottomRight[1] += mCellHeight; - mDragRect.set(left, top, bottomRight[0], bottomRight[1]); - invalidate(); + final Rect dragRect = mDragRects[mDragRectCurrent]; + + if (dragRect.isEmpty() || left != dragRect.left || top != dragRect.top) { + // Now find the bottom right + final int[] bottomRight = mTmpPoint; + cellToPoint(nearest[0] + spanX - 1, nearest[1] + spanY - 1, bottomRight); + bottomRight[0] += mCellWidth; + bottomRight[1] += mCellHeight; + + final int oldIndex = mDragRectCurrent; + mDragRectCurrent = (oldIndex + 1) % mDragRects.length; + + mDragRects[mDragRectCurrent].set(left, top, bottomRight[0], bottomRight[1]); + + mDragRectAnims[oldIndex].animateOut(); + mDragRectAnims[mDragRectCurrent].animateIn(); + } } } @@ -766,9 +841,9 @@ public class CellLayout extends ViewGroup { */ int[] findNearestVacantArea( int pixelX, int pixelY, int spanX, int spanY, View ignoreView, int[] result) { - if (ignoreView != null) { - markCellsAsUnoccupiedForView(ignoreView); - } + // mark space take by ignoreView as available (method checks if ignoreView is null) + markCellsAsUnoccupiedForView(ignoreView); + // Keep track of best-scoring drop area final int[] bestXY = result != null ? result : new int[2]; double bestDistance = Double.MAX_VALUE; @@ -802,9 +877,8 @@ public class CellLayout extends ViewGroup { } } } - if (ignoreView != null) { - markCellsAsOccupiedForView(ignoreView); - } + // re-mark space taken by ignoreView as occupied + markCellsAsOccupiedForView(ignoreView); // Return null if no suitable location found if (bestDistance < Double.MAX_VALUE) { @@ -872,9 +946,8 @@ public class CellLayout extends ViewGroup { */ boolean findCellForSpanThatIntersectsIgnoring(int[] cellXY, int spanX, int spanY, int intersectX, int intersectY, View ignoreView) { - if (ignoreView != null) { - markCellsAsUnoccupiedForView(ignoreView); - } + // mark space take by ignoreView as available (method checks if ignoreView is null) + markCellsAsUnoccupiedForView(ignoreView); boolean foundCell = false; while (true) { @@ -927,9 +1000,8 @@ public class CellLayout extends ViewGroup { } } - if (ignoreView != null) { - markCellsAsOccupiedForView(ignoreView); - } + // re-mark space taken by ignoreView as occupied + markCellsAsOccupiedForView(ignoreView); return foundCell; } @@ -948,6 +1020,10 @@ public class CellLayout extends ViewGroup { if (mCrosshairsAnimator != null) { animateCrosshairsTo(0.0f); } + + mDragRectAnims[mDragRectCurrent].animateOut(); + mDragRectCurrent = (mDragRectCurrent + 1) % mDragRects.length; + mDragRects[mDragRectCurrent].setEmpty(); } /** @@ -990,7 +1066,6 @@ public class CellLayout extends ViewGroup { * or it may have begun on another layout. */ void onDragEnter(View dragView) { - mDragRect.setEmpty(); // Fade in the drag indicators if (mCrosshairsAnimator != null) { animateCrosshairsTo(1.0f); @@ -1123,11 +1198,13 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { } private void markCellsAsOccupiedForView(View view) { + if (view == null || view.getParent() != this) return; LayoutParams lp = (LayoutParams) view.getLayoutParams(); markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true); } private void markCellsAsUnoccupiedForView(View view) { + if (view == null || view.getParent() != this) return; LayoutParams lp = (LayoutParams) view.getLayoutParams(); markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false); } diff --git a/src/com/android/launcher2/Dimmable.java b/src/com/android/launcher2/Dimmable.java new file mode 100644 index 0000000000..df43b3c8e6 --- /dev/null +++ b/src/com/android/launcher2/Dimmable.java @@ -0,0 +1,6 @@ +package com.android.launcher2; + +public interface Dimmable { + public void setDimmableProgress(float progress); + public float getDimmableProgress(); +} diff --git a/src/com/android/launcher2/DimmableAppWidgetHostView.java b/src/com/android/launcher2/DimmableAppWidgetHostView.java index b5ec8cd082..1f512a3f93 100644 --- a/src/com/android/launcher2/DimmableAppWidgetHostView.java +++ b/src/com/android/launcher2/DimmableAppWidgetHostView.java @@ -21,52 +21,21 @@ import com.android.launcher.R; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; import android.view.View; -public class DimmableAppWidgetHostView extends LauncherAppWidgetHostView { +public class DimmableAppWidgetHostView extends LauncherAppWidgetHostView implements Dimmable { public DimmableAppWidgetHostView(Context context) { super(context); mPaint.setFilterBitmap(true); } private final Paint mPaint = new Paint(); - private int mAlpha; - private int mDimmedAlpha; private Bitmap mDimmedView; private Canvas mDimmedViewCanvas; private boolean isDimmedViewUpdatePass; - - private static float cubic(float r) { - return (float) (Math.pow(r-1, 3) + 1); - } - - /** - * Returns the interpolated holographic highlight alpha for the effect we want when scrolling - * pages. - */ - public static float highlightAlphaInterpolator(float r) { - final float pivot = 0.3f; - if (r < pivot) { - return Math.max(0.5f, 0.65f*cubic(r/pivot)); - } else { - return Math.min(1.0f, 0.65f*cubic(1 - (r-pivot)/(1-pivot))); - } - } - - /** - * Returns the interpolated view alpha for the effect we want when scrolling pages. - */ - public static float viewAlphaInterpolator(float r) { - final float pivot = 0.6f; - if (r < pivot) { - return r/pivot; - } else { - return 1.0f; - } - } + private float mDimmableProgress; private void setChildAlpha(float alpha) { if (getChildCount() > 0) { @@ -83,20 +52,18 @@ public class DimmableAppWidgetHostView extends LauncherAppWidgetHostView { setChildAlpha(getAlpha()); } - @Override + //@Override public boolean onSetAlpha(int alpha) { super.onSetAlpha(alpha); return true; } - @Override - public void setAlpha(float alpha) { - final float viewAlpha = viewAlphaInterpolator(alpha); - final float dimmedAlpha = highlightAlphaInterpolator(alpha); - mAlpha = (int) (viewAlpha * 255); - mDimmedAlpha = (int) (dimmedAlpha * 255); - super.setAlpha(viewAlpha); - setChildAlpha(viewAlpha); + public void setDimmableProgress(float progress) { + mDimmableProgress = progress; + } + + public float getDimmableProgress() { + return mDimmableProgress; } private void updateDimmedView() { @@ -113,13 +80,14 @@ public class DimmableAppWidgetHostView extends LauncherAppWidgetHostView { int dimmedColor = getContext().getResources().getColor(R.color.dimmed_view_color); mDimmedViewCanvas.drawColor(dimmedColor, PorterDuff.Mode.SRC_IN); isDimmedViewUpdatePass = false; + invalidate(); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - if (mDimmedView == null && mDimmedAlpha > 0.0f) { + if (mDimmedView == null && mDimmableProgress > 0.0f) { updateDimmedView(); } } @@ -134,9 +102,9 @@ public class DimmableAppWidgetHostView extends LauncherAppWidgetHostView { canvas.restore(); setAlpha(alpha); } else { - if (mDimmedView != null && mDimmedAlpha > 0) { + if (mDimmedView != null && mDimmableProgress > 0) { // draw the dimmed version of this widget - mPaint.setAlpha(mDimmedAlpha); + mPaint.setAlpha((int) (mDimmableProgress * 255)); canvas.drawBitmap(mDimmedView, 0, 0, mPaint); } diff --git a/src/com/android/launcher2/DimmableBubbleTextView.java b/src/com/android/launcher2/DimmableBubbleTextView.java index 66cc97a1ba..cb3b8efd6f 100644 --- a/src/com/android/launcher2/DimmableBubbleTextView.java +++ b/src/com/android/launcher2/DimmableBubbleTextView.java @@ -21,18 +21,17 @@ import com.android.launcher.R; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; import android.util.AttributeSet; -public class DimmableBubbleTextView extends BubbleTextView { +public class DimmableBubbleTextView extends BubbleTextView implements Dimmable { private Paint mDimmedPaint = new Paint(); private int mAlpha; - private int mDimmedAlpha; private Bitmap mDimmedView; private Canvas mDimmedViewCanvas; private boolean isDimmedViewUpdatePass; + private float mDimmableProgress; public DimmableBubbleTextView(Context context) { super(context); @@ -49,48 +48,12 @@ public class DimmableBubbleTextView extends BubbleTextView { mDimmedPaint.setFilterBitmap(true); } - private static float cubic(float r) { - return (float) (Math.pow(r-1, 3) + 1); + public void setDimmableProgress(float progress) { + mDimmableProgress = progress; } - /** - * Returns the interpolated holographic highlight alpha for the effect we want when scrolling - * pages. - */ - public static float highlightAlphaInterpolator(float r) { - final float pivot = 0.3f; - if (r < pivot) { - return Math.max(0.5f, 0.65f*cubic(r/pivot)); - } else { - return Math.min(1.0f, 0.65f*cubic(1 - (r-pivot)/(1-pivot))); - } - } - - /** - * Returns the interpolated view alpha for the effect we want when scrolling pages. - */ - public static float viewAlphaInterpolator(float r) { - final float pivot = 0.6f; - if (r < pivot) { - return r/pivot; - } else { - return 1.0f; - } - } - - @Override - public boolean onSetAlpha(int alpha) { - super.onSetAlpha(alpha); - return true; - } - - @Override - public void setAlpha(float alpha) { - final float viewAlpha = viewAlphaInterpolator(alpha); - final float dimmedAlpha = highlightAlphaInterpolator(alpha); - mAlpha = (int) (viewAlpha * 255); - mDimmedAlpha = (int) (dimmedAlpha * 255); - super.setAlpha(viewAlpha); + public float getDimmableProgress() { + return mDimmableProgress; } @Override @@ -124,13 +87,11 @@ public class DimmableBubbleTextView extends BubbleTextView { super.setAlpha(alpha); canvas.restore(); } else { - if (mAlpha > 0) { - super.onDraw(canvas); - } + super.onDraw(canvas); } - if (mDimmedView != null && mDimmedAlpha > 0) { - mDimmedPaint.setAlpha(mDimmedAlpha); + if (mDimmedView != null && mDimmableProgress > 0) { + mDimmedPaint.setAlpha((int) (mDimmableProgress * 255)); canvas.drawBitmap(mDimmedView, mScrollX, mScrollY, mDimmedPaint); } } diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java index 185f704e26..d54434002f 100644 --- a/src/com/android/launcher2/DragController.java +++ b/src/com/android/launcher2/DragController.java @@ -33,6 +33,8 @@ import android.view.inputmethod.InputMethodManager; import java.util.ArrayList; +import com.android.launcher.R; + /** * Class for initiating a drag within a view or across multiple views. */ @@ -47,7 +49,6 @@ public class DragController { public static int DRAG_ACTION_COPY = 1; private static final int SCROLL_DELAY = 600; - private static final int SCROLL_ZONE = 20; private static final int VIBRATE_DURATION = 35; private static final boolean PROFILE_DRAWING_DURING_DRAG = false; @@ -87,6 +88,11 @@ public class DragController { /** Y offset from the upper-left corner of the cell to where we touched. */ private float mTouchOffsetY; + /** the area at the edge of the screen that makes the workspace go left + * or right while you're dragging. + */ + private int mScrollZone; + /** Where the drag originated */ private DragSource mDragSource; @@ -147,6 +153,7 @@ public class DragController { public DragController(Context context) { mContext = context; mHandler = new Handler(); + mScrollZone = context.getResources().getDimensionPixelSize(R.dimen.scroll_zone); } /** @@ -475,13 +482,13 @@ public class DragController { if (mDeleteRegion != null) { inDeleteRegion = mDeleteRegion.contains(x, y); } - if (!inDeleteRegion && x < SCROLL_ZONE) { + if (!inDeleteRegion && x < mScrollZone) { if (mScrollState == SCROLL_OUTSIDE_ZONE) { mScrollState = SCROLL_WAITING_IN_ZONE; mScrollRunnable.setDirection(SCROLL_LEFT); mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY); } - } else if (!inDeleteRegion && x > mScrollView.getWidth() - SCROLL_ZONE) { + } else if (!inDeleteRegion && x > mScrollView.getWidth() - mScrollZone) { if (mScrollState == SCROLL_OUTSIDE_ZONE) { mScrollState = SCROLL_WAITING_IN_ZONE; mScrollRunnable.setDirection(SCROLL_RIGHT); @@ -514,7 +521,7 @@ public class DragController { mMotionDownX = screenX; mMotionDownY = screenY; - if ((screenX < SCROLL_ZONE) || (screenX > mScrollView.getWidth() - SCROLL_ZONE)) { + if ((screenX < mScrollZone) || (screenX > mScrollView.getWidth() - mScrollZone)) { mScrollState = SCROLL_WAITING_IN_ZONE; mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY); } else { diff --git a/src/com/android/launcher2/InterruptibleInOutAnimator.java b/src/com/android/launcher2/InterruptibleInOutAnimator.java new file mode 100644 index 0000000000..fb0728480d --- /dev/null +++ b/src/com/android/launcher2/InterruptibleInOutAnimator.java @@ -0,0 +1,69 @@ +/* + * 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.animation.ValueAnimator; +import android.util.Log; + +/** + * A convenience class for two-way animations, e.g. a fadeIn/fadeOut animation. + * With a regular ValueAnimator, if you call reverse to show the 'out' animation, you'll get + * a frame-by-frame mirror of the 'in' animation -- i.e., the interpolated values will + * be exactly reversed. Using this class, both the 'in' and the 'out' animation use the + * interpolator in the same direction. + */ +public class InterruptibleInOutAnimator extends ValueAnimator { + private long mOriginalDuration; + private Object mOriginalFromValue; + private Object mOriginalToValue; + + public InterruptibleInOutAnimator(long duration, Object fromValue, Object toValue) { + super(duration, fromValue, toValue); + mOriginalDuration = duration; + mOriginalFromValue = fromValue; + mOriginalToValue = toValue; + } + + private void animate(Object fromValue, Object toValue) { + // This only makes sense when it's running in the opposite direction, or stopped. + setDuration(mOriginalDuration - getCurrentPlayTime()); + + final Object startValue = isRunning() ? getAnimatedValue() : fromValue; + cancel(); + setValues(startValue, toValue); + start(); + } + + /** + * This is the equivalent of calling Animator.start(), except that it can be called when + * the animation is running in the opposite direction, in which case we reverse + * direction and animate for a correspondingly shorter duration. + */ + public void animateIn() { + animate(mOriginalFromValue, mOriginalToValue); + } + + /** + * This is the roughly the equivalent of calling Animator.reverse(), except that it uses the + * same interpolation curve as animateIn(), rather than mirroring it. Also, like animateIn(), + * if the animation is currently running in the opposite direction, we reverse + * direction and animate for a correspondingly shorter duration. + */ + public void animateOut() { + animate(mOriginalToValue, mOriginalFromValue); + } +} diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java index 7c1fa2114d..2800605b42 100644 --- a/src/com/android/launcher2/LauncherModel.java +++ b/src/com/android/launcher2/LauncherModel.java @@ -300,8 +300,10 @@ public class LauncherModel extends BroadcastReceiver { /** * Creates a new unique child id, for a given cell span across all layouts. */ - static int getCellLayoutChildId(int cellId, int screen, int localCellX, int localCellY, int spanX, int spanY) { - return ((cellId & 0xFF) << 16) | (localCellX & 0xFF) << 8 | (localCellY & 0xFF); + static int getCellLayoutChildId( + int cellId, int screen, int localCellX, int localCellY, int spanX, int spanY) { + return ((cellId & 0xFF) << 24) + | (screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF); } static int getCellCountX() { @@ -412,8 +414,9 @@ public class LauncherModel extends BroadcastReceiver { } } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { - String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); - enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packages)); + // When everything comes back, just reload everything. We might not + // have the right icons for apps on external storage. + startLoader(mApp, false); } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java index 5aec48ebec..a1b1e08bad 100644 --- a/src/com/android/launcher2/PagedView.java +++ b/src/com/android/launcher2/PagedView.java @@ -73,11 +73,13 @@ public abstract class PagedView extends ViewGroup { private float mDownMotionX; private float mLastMotionX; private float mLastMotionY; + private int mLastScreenCenter = -1; protected final static int TOUCH_STATE_REST = 0; protected final static int TOUCH_STATE_SCROLLING = 1; protected final static int TOUCH_STATE_PREV_PAGE = 2; protected final static int TOUCH_STATE_NEXT_PAGE = 3; + protected final static float ALPHA_QUANTIZE_LEVEL = 0.01f; protected int mTouchState = TOUCH_STATE_REST; @@ -367,7 +369,6 @@ public abstract class PagedView extends ViewGroup { if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) { int halfScreenSize = getMeasuredWidth() / 2; int screenCenter = mScrollX + halfScreenSize; - final int childCount = getChildCount(); for (int i = 0; i < childCount; ++i) { View layout = (View) getChildAt(i); @@ -391,6 +392,12 @@ public abstract class PagedView extends ViewGroup { dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha))); float alpha = 1.0f - dimAlpha; + if (alpha < ALPHA_QUANTIZE_LEVEL) { + alpha = 0.0f; + } else if (alpha > 1.0f - ALPHA_QUANTIZE_LEVEL) { + alpha = 1.0f; + } + if (Float.compare(alpha, layout.getAlpha()) != 0) { layout.setAlpha(alpha); } @@ -400,9 +407,19 @@ public abstract class PagedView extends ViewGroup { } } + protected void screenScrolled(int screenCenter) { + } + @Override protected void dispatchDraw(Canvas canvas) { - updateAdjacentPagesAlpha(); + int halfScreenSize = getMeasuredWidth() / 2; + int screenCenter = mScrollX + halfScreenSize; + + if (screenCenter != mLastScreenCenter) { + screenScrolled(screenCenter); + updateAdjacentPagesAlpha(); + mLastScreenCenter = screenCenter; + } // Find out which screens are visible; as an optimization we only call draw on them // As an optimization, this code assumes that all pages have the same width as the 0th diff --git a/src/com/android/launcher2/PagedViewIcon.java b/src/com/android/launcher2/PagedViewIcon.java index 0714a933cf..ff5ea496b3 100644 --- a/src/com/android/launcher2/PagedViewIcon.java +++ b/src/com/android/launcher2/PagedViewIcon.java @@ -110,9 +110,9 @@ public class PagedViewIcon extends TextView implements Checkable { mIconCacheKey = info; mHolographicOutline = mIconCache.getOutline(mIconCacheKey); - Drawable image = info.loadIcon(packageManager); - image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight()); - setCompoundDrawablesWithIntrinsicBounds(null, image, null, null); + Bitmap image = Utilities.createIconBitmap(info.loadIcon(packageManager), mContext); + setCompoundDrawablesWithIntrinsicBounds(null, + new FastBitmapDrawable(image), null, null); setText(info.loadLabel(packageManager)); setTag(info); } diff --git a/src/com/android/launcher2/SmoothPagedView.java b/src/com/android/launcher2/SmoothPagedView.java index 5f80f2587f..56037ffb87 100644 --- a/src/com/android/launcher2/SmoothPagedView.java +++ b/src/com/android/launcher2/SmoothPagedView.java @@ -26,11 +26,15 @@ public abstract class SmoothPagedView extends PagedView { private static final float SMOOTHING_SPEED = 0.75f; private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED)); + private float mBaseLineFlingVelocity; + private float mFlingVelocityInfluence; - private static final float BASELINE_FLING_VELOCITY = 2500.f; - private static final float FLING_VELOCITY_INFLUENCE = 0.4f; + static final int OVERSHOOT_MODE = 0; + static final int QUINTIC_MODE = 1; - private WorkspaceOvershootInterpolator mScrollInterpolator; + int mScrollMode; + + private Interpolator mScrollInterpolator; private static class WorkspaceOvershootInterpolator implements Interpolator { private static final float DEFAULT_TENSION = 1.3f; @@ -56,6 +60,16 @@ public abstract class SmoothPagedView extends PagedView { } } + private static class QuinticInterpolator implements Interpolator { + public QuinticInterpolator() { + } + + public float getInterpolation(float t) { + t -= 1.0f; + return t*t*t*t*t + 1; + } + } + /** * Used to inflate the Workspace from XML. * @@ -83,14 +97,27 @@ public abstract class SmoothPagedView extends PagedView { mDeferScrollUpdate = true; } + protected int getScrollMode() { + return OVERSHOOT_MODE; + } + /** * Initializes various states for this workspace. */ @Override protected void init() { super.init(); - mScrollInterpolator = new WorkspaceOvershootInterpolator(); - // overwrite the previous mScroller + + mScrollMode = getScrollMode(); + if (mScrollMode == QUINTIC_MODE) { + mBaseLineFlingVelocity = 700.0f; + mFlingVelocityInfluence = 0.8f; + mScrollInterpolator = new QuinticInterpolator(); + } else { // QUINTIC_MODE + mBaseLineFlingVelocity = 2500.0f; + mFlingVelocityInfluence = 0.4f; + mScrollInterpolator = new WorkspaceOvershootInterpolator(); + } mScroller = new Scroller(getContext(), mScrollInterpolator); } @@ -112,25 +139,32 @@ public abstract class SmoothPagedView extends PagedView { final int screenDelta = Math.max(1, Math.abs(whichPage - mCurrentPage)); final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); final int delta = newX - mScrollX; - int duration = (screenDelta + 1) * 100; + int duration; + if (mScrollMode == OVERSHOOT_MODE) { + duration = (screenDelta + 1) * 100; + } else { // QUINTIC_MODE + duration = Math.round(Math.abs(delta) * 0.6f); + } if (!mScroller.isFinished()) { mScroller.abortAnimation(); } - if (settle) { - mScrollInterpolator.setDistance(screenDelta); - } else { - mScrollInterpolator.disableSettle(); + if (mScrollMode == OVERSHOOT_MODE) { + if (settle) { + ((WorkspaceOvershootInterpolator) mScrollInterpolator).setDistance(screenDelta); + } else { + ((WorkspaceOvershootInterpolator) mScrollInterpolator).disableSettle(); + } } velocity = Math.abs(velocity); if (velocity > 0) { - duration += (duration / (velocity / BASELINE_FLING_VELOCITY)) - * FLING_VELOCITY_INFLUENCE; + duration += (duration / (velocity / mBaseLineFlingVelocity)) * mFlingVelocityInfluence; } else { duration += 100; } + snapToPage(whichPage, delta, duration); } diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java index 4d820cf80f..d80edae8f3 100644 --- a/src/com/android/launcher2/Workspace.java +++ b/src/com/android/launcher2/Workspace.java @@ -33,7 +33,9 @@ import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.res.Resources; import android.content.res.TypedArray; +import android.graphics.Camera; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -63,8 +65,8 @@ public class Workspace extends SmoothPagedView // customization mode private static final float SHRINK_FACTOR = 0.16f; - // The maximum Y rotation to apply to the mini home screens - private static final float MINI_PAGE_MAX_ROTATION = 25.0f; + // Y rotation to apply to the workspace screens + private static final float WORKSPACE_ROTATION = 12.5f; // These are extra scale factors to apply to the mini home screens // so as to achieve the desired transform @@ -72,6 +74,18 @@ public class Workspace extends SmoothPagedView private static final float EXTRA_SCALE_FACTOR_1 = 1.0f; private static final float EXTRA_SCALE_FACTOR_2 = 1.08f; + private static final int BACKGROUND_FADE_OUT_DELAY = 300; + private static final int BACKGROUND_FADE_OUT_DURATION = 300; + private static final int BACKGROUND_FADE_IN_DURATION = 100; + + static final int SCROLL_RIGHT = 0; + static final int SCROLL_LEFT = 1; + + // These animators are used to fade the + private ObjectAnimator mBackgroundFadeIn; + private ObjectAnimator mBackgroundFadeOut; + private float mBackgroundAlpha = 0; + private enum ShrinkPosition { SHRINK_TO_TOP, SHRINK_TO_MIDDLE, SHRINK_TO_BOTTOM }; private final WallpaperManager mWallpaperManager; @@ -179,6 +193,15 @@ public class Workspace extends SmoothPagedView mSnapVelocity = 600; } + @Override + protected int getScrollMode() { + if (LauncherApplication.isScreenXLarge()) { + return SmoothPagedView.QUINTIC_MODE; + } else { + return SmoothPagedView.OVERSHOOT_MODE; + } + } + @Override public void addView(View child, int index, LayoutParams params) { if (!(child instanceof CellLayout)) { @@ -322,7 +345,7 @@ public class Workspace extends SmoothPagedView } // Get the canonical child id to uniquely represent this view in this screen - int childId = LauncherModel.getCellLayoutChildId(child.getId(), screen, x, y, spanX, spanY); + int childId = LauncherModel.getCellLayoutChildId(-1, 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 @@ -382,12 +405,14 @@ public class Workspace extends SmoothPagedView enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1); } } + showOutlines(); } protected void pageEndMoving() { if (!LauncherApplication.isScreenXLarge()) { clearChildrenCache(); } + hideOutlines(); } @Override @@ -424,6 +449,99 @@ public class Workspace extends SmoothPagedView } } + private float getScaleXForRotation(float degrees) { + return (float) (1.0f / Math.cos(Math.PI * degrees / 180.0f)); + } + + public void showOutlines() { + if (mBackgroundFadeOut != null) mBackgroundFadeOut.cancel(); + if (mBackgroundFadeIn != null) mBackgroundFadeIn.cancel(); + mBackgroundFadeIn = new ObjectAnimator(BACKGROUND_FADE_IN_DURATION, this, + new PropertyValuesHolder("backgroundAlpha", 1.0f)); + mBackgroundFadeIn.start(); + } + + public void hideOutlines() { + if (mBackgroundFadeIn != null) mBackgroundFadeIn.cancel(); + if (mBackgroundFadeOut != null) mBackgroundFadeOut.cancel(); + mBackgroundFadeOut = new ObjectAnimator(BACKGROUND_FADE_OUT_DURATION, this, + new PropertyValuesHolder("backgroundAlpha", 0.0f)); + mBackgroundFadeOut.setStartDelay(BACKGROUND_FADE_OUT_DELAY); + mBackgroundFadeOut.start(); + } + + public void setBackgroundAlpha(float alpha) { + mBackgroundAlpha = alpha; + for (int i = 0; i < getChildCount(); i++) { + CellLayout cl = (CellLayout) getChildAt(i); + cl.setBackgroundAlpha(alpha); + } + } + + public float getBackgroundAlpha() { + return mBackgroundAlpha; + } + + @Override + protected void screenScrolled(int screenCenter) { + View cur = getChildAt(mCurrentPage); + View toRight = getChildAt(mCurrentPage + 1); + View toLeft = getChildAt(mCurrentPage - 1); + + for (int i = 0; i < mCurrentPage - 1; i++) { + View v = getChildAt(i); + if (v != null) { + v.setRotationY(WORKSPACE_ROTATION); + v.setScaleX(getScaleXForRotation(WORKSPACE_ROTATION)); + } + } + for (int i = mCurrentPage + 1; i < getChildCount(); i++) { + View v = getChildAt(i); + if (v != null) { + v.setRotationY(-WORKSPACE_ROTATION); + v.setScaleX(getScaleXForRotation(-WORKSPACE_ROTATION)); + } + } + + int pageWidth = cur.getMeasuredWidth(); + int delta = screenCenter - (mCurrentPage * pageWidth + pageWidth / 2 + + getRelativeChildOffset(0)); + + float scrollProgress = Math.abs(delta/(pageWidth*1.0f)); + int scrollDirection = delta > 0 ? SCROLL_LEFT : SCROLL_RIGHT; + + float rotation; + + if (scrollDirection == SCROLL_RIGHT) { + rotation = -scrollProgress * WORKSPACE_ROTATION; + cur.setRotationY(rotation); + cur.setScaleX(getScaleXForRotation(rotation)); + if (toLeft != null) { + rotation = WORKSPACE_ROTATION * (1 - scrollProgress); + toLeft.setRotationY(rotation); + toLeft.setScaleX(getScaleXForRotation(rotation)); + } + if (toRight != null) { + toRight.setRotationY(-WORKSPACE_ROTATION); + toRight.setScaleX(getScaleXForRotation(WORKSPACE_ROTATION)); + } + } else { + rotation = scrollProgress * WORKSPACE_ROTATION; + cur.setRotationY(rotation); + cur.setScaleX(getScaleXForRotation(rotation)); + + if (toRight != null) { + rotation = -WORKSPACE_ROTATION * (1 - scrollProgress); + toRight.setRotationY(rotation); + toRight.setScaleX(getScaleXForRotation(rotation)); + } + if (toLeft != null) { + toLeft.setRotationY(WORKSPACE_ROTATION); + toLeft.setScaleX(getScaleXForRotation(WORKSPACE_ROTATION)); + } + } + } + protected void onAttachedToWindow() { super.onAttachedToWindow(); computeScroll(); @@ -624,7 +742,7 @@ public class Workspace extends SmoothPagedView for (int i = 0; i < screenCount; i++) { CellLayout cl = (CellLayout) getChildAt(i); - float rotation = (-i + 2) * MINI_PAGE_MAX_ROTATION / 2.0f; + float rotation = (-i + 2) * WORKSPACE_ROTATION; float rotationScaleX = (float) (1.0f / Math.cos(Math.PI * rotation / 180.0f)); float rotationScaleY = getYScaleForScreen(i); @@ -636,14 +754,16 @@ public class Workspace extends SmoothPagedView new PropertyValuesHolder("scaleX", SHRINK_FACTOR * rotationScaleX), new PropertyValuesHolder("scaleY", SHRINK_FACTOR * rotationScaleY), new PropertyValuesHolder("backgroundAlpha", 1.0f), + new PropertyValuesHolder("dimmableProgress", 1.0f), new PropertyValuesHolder("alpha", 0.0f), new PropertyValuesHolder("rotationY", rotation)).start(); } else { cl.setX((int)newX); cl.setY((int)newY); - cl.setScaleX(SHRINK_FACTOR); - cl.setScaleY(SHRINK_FACTOR); + cl.setScaleX(SHRINK_FACTOR * rotationScaleX); + cl.setScaleY(SHRINK_FACTOR * rotationScaleY); cl.setBackgroundAlpha(1.0f); + cl.setDimmableProgress(1.0f); cl.setAlpha(0.0f); cl.setRotationY(rotation); } @@ -696,6 +816,14 @@ public class Workspace extends SmoothPagedView for (int i = 0; i < screenCount; i++) { final CellLayout cl = (CellLayout)getChildAt(i); float finalAlphaValue = (i == mCurrentPage) ? 1.0f : 0.0f; + float rotation = 0.0f; + + if (i < mCurrentPage) { + rotation = WORKSPACE_ROTATION; + } else if (i > mCurrentPage) { + rotation = -WORKSPACE_ROTATION; + } + if (animated) { s.playTogether( new ObjectAnimator(duration, cl, "translationX", 0.0f), @@ -704,15 +832,17 @@ public class Workspace extends SmoothPagedView new ObjectAnimator(duration, cl, "scaleY", 1.0f), new ObjectAnimator(duration, cl, "backgroundAlpha", 0.0f), new ObjectAnimator(duration, cl, "alpha", finalAlphaValue), - new ObjectAnimator(duration, cl, "rotationY", 0.0f)); + new ObjectAnimator(duration, cl, "dimmableProgress", 0.0f), + new ObjectAnimator(duration, cl, "rotationY", rotation)); } else { cl.setTranslationX(0.0f); cl.setTranslationY(0.0f); cl.setScaleX(1.0f); cl.setScaleY(1.0f); cl.setBackgroundAlpha(0.0f); - cl.setAlpha(1.0f); - cl.setRotationY(0.0f); + cl.setDimmableProgress(0.0f); + cl.setAlpha(finalAlphaValue); + cl.setRotationY(rotation); } } s.addListener(mUnshrinkAnimationListener); @@ -774,23 +904,25 @@ public class Workspace extends SmoothPagedView } else { cellLayout = getCurrentDropLayout(); } + if (source != this) { onDropExternal(originX, originY, dragInfo, cellLayout); } else { // Move internally if (mDragInfo != null) { final View cell = mDragInfo.cell; - int index = mScroller.isFinished() ? mCurrentPage : mNextPage; - if (index != mDragInfo.screen) { - final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen); - originalCellLayout.removeView(cell); - addInScreen(cell, index, mDragInfo.cellX, mDragInfo.cellY, - mDragInfo.spanX, mDragInfo.spanY); - } mTargetCell = findNearestVacantArea(originX, originY, mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, mTargetCell); + + int screen = indexOfChild(cellLayout); + if (screen != mDragInfo.screen) { + final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen); + originalCellLayout.removeView(cell); + addInScreen(cell, screen, mTargetCell[0], mTargetCell[1], + mDragInfo.spanX, mDragInfo.spanY); + } cellLayout.onDropChild(cell); // update the item's position after drop @@ -799,9 +931,11 @@ public class Workspace extends SmoothPagedView cellLayout.onMove(cell, mTargetCell[0], mTargetCell[1]); lp.cellX = mTargetCell[0]; lp.cellY = mTargetCell[1]; + cell.setId(LauncherModel.getCellLayoutChildId(-1, mDragInfo.screen, + mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY)); LauncherModel.moveItemInDatabase(mLauncher, info, - LauncherSettings.Favorites.CONTAINER_DESKTOP, index, + LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, lp.cellX, lp.cellY); } } @@ -1152,15 +1286,12 @@ public class Workspace extends SmoothPagedView private int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) { - final int[] cellXY = mTempCell; int localPixelX = pixelX - (layout.getLeft() - mScrollX); int localPixelY = pixelY - (layout.getTop() - mScrollY); - layout.estimateDropCell(localPixelX, localPixelY, spanX, spanY, cellXY); - layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate); // Find the best target drop location return layout.findNearestVacantArea( - mTempEstimate[0], mTempEstimate[1], spanX, spanY, ignoreView, recycle); + localPixelX, localPixelY, spanX, spanY, ignoreView, recycle); } /**