Merge branch 'master' into honeycomb-release
diff --git a/Android.mk b/Android.mk
index dfe6788..89e626b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -30,7 +30,7 @@
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 0000000..ebcba6c
--- /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("""<span class="intent" title="%s">shortcut</span>""" % (
+ cgi.escape(cell, True)
+ ))
+
+
+def print_icon(out, id, i, cell):
+ if cell:
+ icon_fn = "icon_%d.png" % id
+ out.write("""<img src="%s">""" % ( 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("""<html>
+<head>
+<style type="text/css">
+.intent {
+ font-style: italic;
+}
+</style>
+</head>
+<body>
+""")
+
+ # Data table
+ out.write("<b>Favorites table</b><br/>\n")
+ out.write("""<html>
+<table border=1 cellspacing=0 cellpadding=4>
+<tr>
+""")
+ 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(""" <th>%s</th>
+""" % ( col ))
+ out.write("""
+</tr>
+""")
+ for row in rows:
+ out.write("""<tr>
+""")
+ for i in range(0,len(row)):
+ cell = row[i]
+ # row[0] is always _id
+ out.write(""" <td>""")
+ print_functions[i](out, row[0], row, cell)
+ out.write("""</td>
+""")
+ out.write("""</tr>
+""")
+ out.write("""</table>
+""")
+
+ # 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("<br/><b>Screen %d</b><br/>\n" % i)
+ out.write("<table class=layout border=1 cellspacing=0 cellpadding=4>\n")
+ for m in screen:
+ out.write(" <tr>\n")
+ for cell in m:
+ if cell is None:
+ out.write(" <td width=%d height=%d></td>\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((" <td colspan=%d rowspan=%d width=%d height=%d"
+ + " bgcolor=#dddddd align=center valign=middle %s>") % (
+ spanX, spanY,
+ (CELL_SIZE*spanX), (CELL_SIZE*spanY),
+ title))
+ itemType = cell["itemType"]
+ if itemType == 0:
+ out.write("""<img src="icon_%d.png">\n""" % ( cell["_id"] ))
+ out.write("<br/>\n")
+ out.write(cgi.escape(cell["title"]) + " <br/><i>(app)</i>")
+ elif itemType == 1:
+ out.write("""<img src="icon_%d.png">\n""" % ( cell["_id"] ))
+ out.write("<br/>\n")
+ out.write(cgi.escape(cell["title"]) + " <br/><i>(shortcut)</i>")
+ elif itemType == 2:
+ out.write("""<i>folder</i>""")
+ elif itemType == 3:
+ out.write("""<i>live folder</i>""")
+ elif itemType == 4:
+ out.write("<i>widget %d</i><br/>\n" % cell["appWidgetId"])
+ elif itemType == 1000:
+ out.write("""<i>clock</i>""")
+ elif itemType == 1001:
+ out.write("""<i>search</i>""")
+ elif itemType == 1002:
+ out.write("""<i>photo frame</i>""")
+ else:
+ out.write("<b>unknown type: %d</b>" % itemType)
+ out.write("</td>\n")
+ out.write("</tr>\n")
+ out.write("</table>\n")
+ i=i+1
+
+ out.write("""
+</body>
+</html>
+""")
+
+ 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 55574ed..ace493b 100644
--- a/res/drawable-hdpi/all_apps_button_focused.png
+++ b/res/drawable-hdpi/all_apps_button_focused.png
Binary files differ
diff --git a/res/drawable-hdpi/all_apps_button_normal.png b/res/drawable-hdpi/all_apps_button_normal.png
index 7e7ec86..cde455d 100644
--- a/res/drawable-hdpi/all_apps_button_normal.png
+++ b/res/drawable-hdpi/all_apps_button_normal.png
Binary files differ
diff --git a/res/drawable-hdpi/all_apps_button_pressed.png b/res/drawable-hdpi/all_apps_button_pressed.png
index 3ccb5af..d5f9f54 100644
--- a/res/drawable-hdpi/all_apps_button_pressed.png
+++ b/res/drawable-hdpi/all_apps_button_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_bg_center.9.png b/res/drawable-hdpi/hotseat_bg_center.9.png
index 468e766..c69bc87 100644
--- a/res/drawable-hdpi/hotseat_bg_center.9.png
+++ b/res/drawable-hdpi/hotseat_bg_center.9.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_bg_left.9.png b/res/drawable-hdpi/hotseat_bg_left.9.png
index 433a10e..ca901da 100644
--- a/res/drawable-hdpi/hotseat_bg_left.9.png
+++ b/res/drawable-hdpi/hotseat_bg_left.9.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_bg_right.9.png b/res/drawable-hdpi/hotseat_bg_right.9.png
index 4ea2a73..810c4d4 100644
--- a/res/drawable-hdpi/hotseat_bg_right.9.png
+++ b/res/drawable-hdpi/hotseat_bg_right.9.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_browser_focused.png b/res/drawable-hdpi/hotseat_browser_focused.png
index 6717ad2..4020a89 100644
--- a/res/drawable-hdpi/hotseat_browser_focused.png
+++ b/res/drawable-hdpi/hotseat_browser_focused.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_browser_normal.png b/res/drawable-hdpi/hotseat_browser_normal.png
index d02fdd9..ebdab62 100644
--- a/res/drawable-hdpi/hotseat_browser_normal.png
+++ b/res/drawable-hdpi/hotseat_browser_normal.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_browser_pressed.png b/res/drawable-hdpi/hotseat_browser_pressed.png
index 71df2d1..bfa23b3 100644
--- a/res/drawable-hdpi/hotseat_browser_pressed.png
+++ b/res/drawable-hdpi/hotseat_browser_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_left.png b/res/drawable-hdpi/hotseat_left.png
deleted file mode 100644
index 5dabf57..0000000
--- a/res/drawable-hdpi/hotseat_left.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_phone_focused.png b/res/drawable-hdpi/hotseat_phone_focused.png
index 3e84a58..f81f0a8 100644
--- a/res/drawable-hdpi/hotseat_phone_focused.png
+++ b/res/drawable-hdpi/hotseat_phone_focused.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_phone_normal.png b/res/drawable-hdpi/hotseat_phone_normal.png
index e8a869c..2a16f9c 100644
--- a/res/drawable-hdpi/hotseat_phone_normal.png
+++ b/res/drawable-hdpi/hotseat_phone_normal.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_phone_pressed.png b/res/drawable-hdpi/hotseat_phone_pressed.png
index dc4ad6e..a6c2baf 100644
--- a/res/drawable-hdpi/hotseat_phone_pressed.png
+++ b/res/drawable-hdpi/hotseat_phone_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi/hotseat_right.png b/res/drawable-hdpi/hotseat_right.png
deleted file mode 100644
index 114bcb5..0000000
--- a/res/drawable-hdpi/hotseat_right.png
+++ /dev/null
Binary files 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 0000000..d939d5c
--- /dev/null
+++ b/res/drawable-xlarge/home_screen_bg.9.png
Binary files differ
diff --git a/res/values-xlarge/config.xml b/res/values-xlarge/config.xml
index f6f6646..c7c68e2 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. -->
<integer name="config_dragViewOffsetX">0</integer>
<integer name="config_dragViewOffsetY">12</integer>
+
+ <!-- The duration (in ms) of the fade animation on the object outlines, used when
+ we are dragging objects around on the home screen. -->
+ <integer name="config_dragOutlineFadeTime">900</integer>
+
+ <!-- The alpha value at which to show the most recent drop visualization outline. -->
+ <integer name="config_dragOutlineMaxAlpha">180</integer>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 790f835..c83986b 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -37,4 +37,8 @@
<!-- delete_zone_size_full - button_bar_height_portrait -->
<dimen name="delete_zone_padding">14dip</dimen>
+
+ <!-- the area at the edge of the screen that makes the workspace go left
+ or right while you're dragging. -->
+ <dimen name="scroll_zone">20dp</dimen>
</resources>
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 84b26f2..0f1d469 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -31,18 +31,19 @@
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 @@
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 @@
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 @@
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 @@
@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 @@
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 @@
}
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 @@
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 @@
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 @@
*/
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 @@
}
}
}
- 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 @@
*/
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 @@
}
}
- if (ignoreView != null) {
- markCellsAsOccupiedForView(ignoreView);
- }
+ // re-mark space taken by ignoreView as occupied
+ markCellsAsOccupiedForView(ignoreView);
return foundCell;
}
@@ -948,6 +1020,10 @@
if (mCrosshairsAnimator != null) {
animateCrosshairsTo(0.0f);
}
+
+ mDragRectAnims[mDragRectCurrent].animateOut();
+ mDragRectCurrent = (mDragRectCurrent + 1) % mDragRects.length;
+ mDragRects[mDragRectCurrent].setEmpty();
}
/**
@@ -990,7 +1066,6 @@
* 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 @@
}
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 0000000..df43b3c
--- /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 b5ec8cd..1f512a3 100644
--- a/src/com/android/launcher2/DimmableAppWidgetHostView.java
+++ b/src/com/android/launcher2/DimmableAppWidgetHostView.java
@@ -21,52 +21,21 @@
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 @@
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 @@
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 @@
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 66cc97a..cb3b8ef 100644
--- a/src/com/android/launcher2/DimmableBubbleTextView.java
+++ b/src/com/android/launcher2/DimmableBubbleTextView.java
@@ -21,18 +21,17 @@
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 @@
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 @@
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 185f704..d544340 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -33,6 +33,8 @@
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 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 @@
/** 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 DragController(Context context) {
mContext = context;
mHandler = new Handler();
+ mScrollZone = context.getResources().getDimensionPixelSize(R.dimen.scroll_zone);
}
/**
@@ -475,13 +482,13 @@
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 @@
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 0000000..fb07284
--- /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 7c1fa21..2800605 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -300,8 +300,10 @@
/**
* 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 @@
}
} 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 5aec48e..a1b1e08 100644
--- a/src/com/android/launcher2/PagedView.java
+++ b/src/com/android/launcher2/PagedView.java
@@ -73,11 +73,13 @@
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 @@
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 @@
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 @@
}
}
+ 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 0714a93..ff5ea49 100644
--- a/src/com/android/launcher2/PagedViewIcon.java
+++ b/src/com/android/launcher2/PagedViewIcon.java
@@ -110,9 +110,9 @@
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 5f80f25..56037ff 100644
--- a/src/com/android/launcher2/SmoothPagedView.java
+++ b/src/com/android/launcher2/SmoothPagedView.java
@@ -26,11 +26,15 @@
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 @@
}
}
+ 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 @@
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 @@
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 4d820cf..d80edae 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -33,7 +33,9 @@
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 @@
// 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 @@
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<Float> mBackgroundFadeIn;
+ private ObjectAnimator<Float> mBackgroundFadeOut;
+ private float mBackgroundAlpha = 0;
+
private enum ShrinkPosition { SHRINK_TO_TOP, SHRINK_TO_MIDDLE, SHRINK_TO_BOTTOM };
private final WallpaperManager mWallpaperManager;
@@ -180,6 +194,15 @@
}
@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)) {
throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
@@ -322,7 +345,7 @@
}
// 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 @@
enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
}
}
+ showOutlines();
}
protected void pageEndMoving() {
if (!LauncherApplication.isScreenXLarge()) {
clearChildrenCache();
}
+ hideOutlines();
}
@Override
@@ -424,6 +449,99 @@
}
}
+ 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<Float>(BACKGROUND_FADE_IN_DURATION, this,
+ new PropertyValuesHolder<Float>("backgroundAlpha", 1.0f));
+ mBackgroundFadeIn.start();
+ }
+
+ public void hideOutlines() {
+ if (mBackgroundFadeIn != null) mBackgroundFadeIn.cancel();
+ if (mBackgroundFadeOut != null) mBackgroundFadeOut.cancel();
+ mBackgroundFadeOut = new ObjectAnimator<Float>(BACKGROUND_FADE_OUT_DURATION, this,
+ new PropertyValuesHolder<Float>("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 @@
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 @@
new PropertyValuesHolder<Float>("scaleX", SHRINK_FACTOR * rotationScaleX),
new PropertyValuesHolder<Float>("scaleY", SHRINK_FACTOR * rotationScaleY),
new PropertyValuesHolder<Float>("backgroundAlpha", 1.0f),
+ new PropertyValuesHolder<Float>("dimmableProgress", 1.0f),
new PropertyValuesHolder<Float>("alpha", 0.0f),
new PropertyValuesHolder<Float>("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 @@
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<Float>(duration, cl, "translationX", 0.0f),
@@ -704,15 +832,17 @@
new ObjectAnimator<Float>(duration, cl, "scaleY", 1.0f),
new ObjectAnimator<Float>(duration, cl, "backgroundAlpha", 0.0f),
new ObjectAnimator<Float>(duration, cl, "alpha", finalAlphaValue),
- new ObjectAnimator<Float>(duration, cl, "rotationY", 0.0f));
+ new ObjectAnimator<Float>(duration, cl, "dimmableProgress", 0.0f),
+ new ObjectAnimator<Float>(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 @@
} 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 @@
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 @@
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);
}
/**