am cff2d6a2: (-s ours) Import translations. DO NOT MERGE
* commit 'cff2d6a2d904b7d88096650021aa428cfdc08632':
Import translations. DO NOT MERGE
diff --git a/res/drawable-hdpi/ic_setting.png b/res/drawable-hdpi/ic_setting.png
index c617154..3f5bc43 100644
--- a/res/drawable-hdpi/ic_setting.png
+++ b/res/drawable-hdpi/ic_setting.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_setting_pressed.png b/res/drawable-hdpi/ic_setting_pressed.png
index fb58a4b..9201064 100644
--- a/res/drawable-hdpi/ic_setting_pressed.png
+++ b/res/drawable-hdpi/ic_setting_pressed.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_setting.png b/res/drawable-mdpi/ic_setting.png
index 0c8ae9d..1e76459 100644
--- a/res/drawable-mdpi/ic_setting.png
+++ b/res/drawable-mdpi/ic_setting.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_setting_pressed.png b/res/drawable-mdpi/ic_setting_pressed.png
index 846091f..d7aca18 100644
--- a/res/drawable-mdpi/ic_setting_pressed.png
+++ b/res/drawable-mdpi/ic_setting_pressed.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_setting.png b/res/drawable-xhdpi/ic_setting.png
index 91ba98c..6f06bcf 100644
--- a/res/drawable-xhdpi/ic_setting.png
+++ b/res/drawable-xhdpi/ic_setting.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_setting_pressed.png b/res/drawable-xhdpi/ic_setting_pressed.png
index 08aafc4..bca8ccd 100644
--- a/res/drawable-xhdpi/ic_setting_pressed.png
+++ b/res/drawable-xhdpi/ic_setting_pressed.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_setting.png b/res/drawable-xxhdpi/ic_setting.png
index 6e1e662..b3729d3 100644
--- a/res/drawable-xxhdpi/ic_setting.png
+++ b/res/drawable-xxhdpi/ic_setting.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_setting_pressed.png b/res/drawable-xxhdpi/ic_setting_pressed.png
index a202a40..5c9c1be 100644
--- a/res/drawable-xxhdpi/ic_setting_pressed.png
+++ b/res/drawable-xxhdpi/ic_setting_pressed.png
Binary files differ
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index bf670d4..abb19f4 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -77,19 +77,6 @@
android:layout_height="match_parent"
android:visibility="gone" />
- <!-- TODO: Fix
- <com.android.launcher3.DrawableStateProxyView
- android:id="@+id/voice_button_proxy"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_gravity="top|start"
- android:layout_marginTop="64dp"
- android:clickable="true"
- android:onClick="onClickVoiceButton"
- android:importantForAccessibility="no"
- launcher:sourceViewId="@+id/voice_button" />
- -->
-
<include layout="@layout/apps_customize_pane"
android:id="@+id/apps_customize_pane"
android:layout_width="match_parent"
diff --git a/res/layout/overview_panel.xml b/res/layout/overview_panel.xml
index e36004c..558900c 100644
--- a/res/layout/overview_panel.xml
+++ b/res/layout/overview_panel.xml
@@ -13,18 +13,18 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="horizontal"
android:layout_gravity="center_horizontal|bottom"
- android:paddingBottom="@dimen/overview_panel_bottom_padding">
+ android:orientation="horizontal">
<TextView
android:id="@+id/wallpaper_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="start|top"
android:text="@string/wallpaper_button_text"
android:drawablePadding="4dp"
android:drawableTop="@drawable/wallpaper_button"
@@ -32,13 +32,11 @@
android:fontFamily="sans-serif-condensed"
android:textAllCaps="true"
android:textSize="12sp" />
- <Space
- android:layout_width="@dimen/overview_panel_buttonSpacing"
- android:layout_height="wrap_content"/>
<TextView
android:id="@+id/widget_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|top"
android:text="@string/widget_button_text"
android:drawablePadding="4dp"
android:gravity="center_horizontal"
@@ -46,13 +44,11 @@
android:fontFamily="sans-serif-condensed"
android:textAllCaps="true"
android:textSize="12sp"/>
- <Space
- android:layout_width="@dimen/overview_panel_buttonSpacing"
- android:layout_height="wrap_content"/>
<TextView
android:id="@+id/settings_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="end|top"
android:text="@string/settings_button_text"
android:drawablePadding="4dp"
android:gravity="center_horizontal"
@@ -60,4 +56,4 @@
android:fontFamily="sans-serif-condensed"
android:textAllCaps="true"
android:textSize="12sp" />
-</LinearLayout>
+</FrameLayout>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index c72576e..b95d482 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -98,7 +98,7 @@
<string name="workspace_scroll_format" msgid="8458889198184077399">"Startbildschirm %1$d von %2$d"</string>
<string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"Apps-Seite %1$d von %2$d"</string>
<string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"Widgets-Seite %1$d von %2$d"</string>
- <string name="first_run_cling_title" msgid="7257389003637362144">"Hallo!"</string>
+ <string name="first_run_cling_title" msgid="7257389003637362144">"Willkommen!"</string>
<string name="first_run_cling_description" msgid="6447072552696253358">"Gerät personalisieren"</string>
<string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
<string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
diff --git a/res/values-land/styles.xml b/res/values-land/styles.xml
index ccb5fcb..d18a57b 100644
--- a/res/values-land/styles.xml
+++ b/res/values-land/styles.xml
@@ -27,7 +27,7 @@
</style>
<style name="DropTargetButtonContainer">
<item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">0dp</item>
+ <item name="android:layout_height">wrap_content</item>
</style>
<style name="DropTargetButton">
<item name="android:layout_width">wrap_content</item>
diff --git a/res/values/config.xml b/res/values/config.xml
index 1a83556..1538d9f 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -3,6 +3,10 @@
<integer name="config_dynamic_grid_max_long_edge_cell_count">6</integer>
<integer name="config_dynamic_grid_max_short_edge_cell_count">5</integer>
<integer name="config_dynamic_grid_min_edge_cell_count">3</integer>
+ <!-- Out of 100, the percent of space the overview bar should try and take vertically. -->
+ <integer name="config_dynamic_grid_overview_icon_zone_percentage">20</integer>
+ <!-- Out of 100, the percent to shrink the workspace during overview mode. -->
+ <integer name="config_dynamic_grid_overview_scale_percentage">80</integer>
<!-- Miscellaneous -->
<bool name="config_largeHeap">false</bool>
@@ -24,8 +28,6 @@
<!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
<integer name="config_workspaceSpringLoadShrinkPercentage">80</integer>
- <!-- Out of 100, the percent to shrink the workspace during overview mode. -->
- <integer name="config_workspaceOverviewShrinkPercentage">58</integer>
<!-- Fade/zoom in/out duration & scale in the AllApps transition.
Note: This should be less than the workspaceShrinkTime as they happen together. -->
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 740393e..c86fe31 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -23,6 +23,9 @@
<dimen name="dynamic_grid_icon_drawable_padding">4dp</dimen>
<dimen name="dynamic_grid_all_apps_cell_padding">18dp</dimen>
<dimen name="dynamic_grid_workspace_page_spacing">8dp</dimen>
+ <dimen name="dynamic_grid_overview_min_icon_zone_height">80dp</dimen>
+ <dimen name="dynamic_grid_overview_max_icon_zone_height">120dp</dimen>
+ <dimen name="dynamic_grid_overview_bar_max_width">280dp</dimen>
<!-- Wallpaper picker -->
<dimen name="wallpaperThumbnailWidth">106.5dp</dimen>
@@ -49,9 +52,6 @@
<!-- Workspace -->
<dimen name="workspace_max_gap">16dp</dimen>
<dimen name="workspace_overscroll_drawable_padding">0dp</dimen>
- <dimen name="overview_panel_bottom_padding">50dp</dimen>
- <dimen name="overview_panel_buttonSpacing">60dp</dimen>
- <dimen name="overview_mode_page_offset">130dp</dimen>
<!-- QSB -->
<dimen name="toolbar_button_vertical_padding">4dip</dimen>
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index d9ca157..688ff82 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -916,6 +916,13 @@
}
@Override
+ public float getIntrinsicIconScaleFactor() {
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+ return (float) grid.allAppsIconSizePx / grid.iconSizePx;
+ }
+
+ @Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
cancelAllTasks();
diff --git a/src/com/android/launcher3/CropView.java b/src/com/android/launcher3/CropView.java
index 9224e3b..578b8ea 100644
--- a/src/com/android/launcher3/CropView.java
+++ b/src/com/android/launcher3/CropView.java
@@ -165,7 +165,8 @@
final float imageWidth = imageDims[0];
final float imageHeight = imageDims[1];
mMinScale = Math.max(w / imageWidth, h / imageHeight);
- mRenderer.scale = Math.max(mMinScale, mRenderer.scale);
+ mRenderer.scale =
+ Math.max(mMinScale, resetScale ? Float.MIN_VALUE : mRenderer.scale);
}
}
}
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 4023daf..e3a154b 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -29,6 +29,7 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.TransitionDrawable;
+import android.os.AsyncTask;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewConfiguration;
@@ -334,11 +335,12 @@
if (appWidgetHost != null) {
// Deleting an app widget ID is a void call but writes to disk before returning
// to the caller...
- new Thread("deleteAppWidgetId") {
- public void run() {
+ new AsyncTask<Void, Void, Void>() {
+ public Void doInBackground(Void ... args) {
appWidgetHost.deleteAppWidgetId(launcherAppWidgetInfo.appWidgetId);
+ return null;
}
- }.start();
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
}
}
if (wasWaitingForUninstall && !mWaitingForUninstall) {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
new file mode 100644
index 0000000..9e1f1a7
--- /dev/null
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -0,0 +1,728 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.appwidget.AppWidgetHostView;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Paint;
+import android.graphics.Paint.FontMetrics;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.Surface;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+
+class DeviceProfileQuery {
+ float widthDps;
+ float heightDps;
+ float value;
+ PointF dimens;
+
+ DeviceProfileQuery(float w, float h, float v) {
+ widthDps = w;
+ heightDps = h;
+ value = v;
+ dimens = new PointF(w, h);
+ }
+}
+
+public class DeviceProfile {
+ public static interface DeviceProfileCallbacks {
+ public void onAvailableSizeChanged(DeviceProfile grid);
+ }
+
+ String name;
+ float minWidthDps;
+ float minHeightDps;
+ float numRows;
+ float numColumns;
+ float numHotseatIcons;
+ private float iconSize;
+ private float iconTextSize;
+ private int iconDrawablePaddingOriginalPx;
+ private float hotseatIconSize;
+
+ boolean isLandscape;
+ boolean isTablet;
+ boolean isLargeTablet;
+ boolean transposeLayoutWithOrientation;
+
+ int desiredWorkspaceLeftRightMarginPx;
+ int edgeMarginPx;
+ Rect defaultWidgetPadding;
+
+ int widthPx;
+ int heightPx;
+ int availableWidthPx;
+ int availableHeightPx;
+ int defaultPageSpacingPx;
+
+ int overviewModeMinIconZoneHeightPx;
+ int overviewModeMaxIconZoneHeightPx;
+ int overviewModeMaxBarWidthPx;
+ float overviewModeIconZoneRatio;
+ float overviewModeScaleFactor;
+
+ int iconSizePx;
+ int iconTextSizePx;
+ int iconDrawablePaddingPx;
+ int cellWidthPx;
+ int cellHeightPx;
+ int allAppsIconSizePx;
+ int allAppsIconTextSizePx;
+ int allAppsCellWidthPx;
+ int allAppsCellHeightPx;
+ int allAppsCellPaddingPx;
+ int folderBackgroundOffset;
+ int folderIconSizePx;
+ int folderCellWidthPx;
+ int folderCellHeightPx;
+ int hotseatCellWidthPx;
+ int hotseatCellHeightPx;
+ int hotseatIconSizePx;
+ int hotseatBarHeightPx;
+ int hotseatAllAppsRank;
+ int allAppsNumRows;
+ int allAppsNumCols;
+ int searchBarSpaceWidthPx;
+ int searchBarSpaceMaxWidthPx;
+ int searchBarSpaceHeightPx;
+ int searchBarHeightPx;
+ int pageIndicatorHeightPx;
+
+ private ArrayList<DeviceProfileCallbacks> mCallbacks = new ArrayList<DeviceProfileCallbacks>();
+
+ DeviceProfile(String n, float w, float h, float r, float c,
+ float is, float its, float hs, float his) {
+ // Ensure that we have an odd number of hotseat items (since we need to place all apps)
+ if (!AppsCustomizePagedView.DISABLE_ALL_APPS && hs % 2 == 0) {
+ throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces");
+ }
+
+ name = n;
+ minWidthDps = w;
+ minHeightDps = h;
+ numRows = r;
+ numColumns = c;
+ iconSize = is;
+ iconTextSize = its;
+ numHotseatIcons = hs;
+ hotseatIconSize = his;
+ }
+
+ DeviceProfile(Context context,
+ ArrayList<DeviceProfile> profiles,
+ float minWidth, float minHeight,
+ int wPx, int hPx,
+ int awPx, int ahPx,
+ Resources res) {
+ DisplayMetrics dm = res.getDisplayMetrics();
+ ArrayList<DeviceProfileQuery> points =
+ new ArrayList<DeviceProfileQuery>();
+ transposeLayoutWithOrientation =
+ res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
+ minWidthDps = minWidth;
+ minHeightDps = minHeight;
+
+ ComponentName cn = new ComponentName(context.getPackageName(),
+ this.getClass().getName());
+ defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
+ edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
+ desiredWorkspaceLeftRightMarginPx = 2 * edgeMarginPx;
+ pageIndicatorHeightPx =
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height);
+ defaultPageSpacingPx =
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
+ allAppsCellPaddingPx =
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_all_apps_cell_padding);
+ overviewModeMinIconZoneHeightPx =
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_min_icon_zone_height);
+ overviewModeMaxIconZoneHeightPx =
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_max_icon_zone_height);
+ overviewModeMaxBarWidthPx =
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_max_width);
+ overviewModeIconZoneRatio =
+ res.getInteger(R.integer.config_dynamic_grid_overview_icon_zone_percentage) / 100f;
+ overviewModeScaleFactor =
+ res.getInteger(R.integer.config_dynamic_grid_overview_scale_percentage) / 100f;
+
+ // Interpolate the rows
+ for (DeviceProfile p : profiles) {
+ points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numRows));
+ }
+ numRows = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
+ // Interpolate the columns
+ points.clear();
+ for (DeviceProfile p : profiles) {
+ points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numColumns));
+ }
+ numColumns = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
+ // Interpolate the hotseat length
+ points.clear();
+ for (DeviceProfile p : profiles) {
+ points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numHotseatIcons));
+ }
+ numHotseatIcons = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
+ hotseatAllAppsRank = (int) (numHotseatIcons / 2);
+
+ // Interpolate the icon size
+ points.clear();
+ for (DeviceProfile p : profiles) {
+ points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconSize));
+ }
+ iconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
+ // AllApps uses the original non-scaled icon size
+ allAppsIconSizePx = DynamicGrid.pxFromDp(iconSize, dm);
+
+ // Interpolate the icon text size
+ points.clear();
+ for (DeviceProfile p : profiles) {
+ points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconTextSize));
+ }
+ iconTextSize = invDistWeightedInterpolate(minWidth, minHeight, points);
+ iconDrawablePaddingOriginalPx =
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
+ // AllApps uses the original non-scaled icon text size
+ allAppsIconTextSizePx = DynamicGrid.pxFromDp(iconTextSize, dm);
+
+ // Interpolate the hotseat icon size
+ points.clear();
+ for (DeviceProfile p : profiles) {
+ points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.hotseatIconSize));
+ }
+ // Hotseat
+ hotseatIconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
+
+ // Calculate the remaining vars
+ updateFromConfiguration(context, res, wPx, hPx, awPx, ahPx);
+ updateAvailableDimensions(context);
+ }
+
+ void addCallback(DeviceProfileCallbacks cb) {
+ mCallbacks.add(cb);
+ cb.onAvailableSizeChanged(this);
+ }
+ void removeCallback(DeviceProfileCallbacks cb) {
+ mCallbacks.remove(cb);
+ }
+
+ private int getDeviceOrientation(Context context) {
+ WindowManager windowManager = (WindowManager)
+ context.getSystemService(Context.WINDOW_SERVICE);
+ Resources resources = context.getResources();
+ DisplayMetrics dm = resources.getDisplayMetrics();
+ Configuration config = resources.getConfiguration();
+ int rotation = windowManager.getDefaultDisplay().getRotation();
+
+ boolean isLandscape = (config.orientation == Configuration.ORIENTATION_LANDSCAPE) &&
+ (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180);
+ boolean isRotatedPortrait = (config.orientation == Configuration.ORIENTATION_PORTRAIT) &&
+ (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
+ if (isLandscape || isRotatedPortrait) {
+ return CellLayout.LANDSCAPE;
+ } else {
+ return CellLayout.PORTRAIT;
+ }
+ }
+
+ private void updateAvailableDimensions(Context context) {
+ WindowManager windowManager = (WindowManager)
+ context.getSystemService(Context.WINDOW_SERVICE);
+ Display display = windowManager.getDefaultDisplay();
+ Resources resources = context.getResources();
+ DisplayMetrics dm = resources.getDisplayMetrics();
+ Configuration config = resources.getConfiguration();
+
+ // There are three possible configurations that the dynamic grid accounts for, portrait,
+ // landscape with the nav bar at the bottom, and landscape with the nav bar at the side.
+ // To prevent waiting for fitSystemWindows(), we make the observation that in landscape,
+ // the height is the smallest height (either with the nav bar at the bottom or to the
+ // side) and otherwise, the height is simply the largest possible height for a portrait
+ // device.
+ Point size = new Point();
+ Point smallestSize = new Point();
+ Point largestSize = new Point();
+ display.getSize(size);
+ display.getCurrentSizeRange(smallestSize, largestSize);
+ availableWidthPx = size.x;
+ if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ availableHeightPx = smallestSize.y;
+ } else {
+ availableHeightPx = largestSize.y;
+ }
+
+ // Check to see if the icons fit in the new available height. If not, then we need to
+ // shrink the icon size.
+ Rect workspacePadding = getWorkspacePadding();
+ float scale = 1f;
+ int drawablePadding = iconDrawablePaddingOriginalPx;
+ updateIconSize(1f, drawablePadding, resources, dm);
+ float usedHeight = (cellHeightPx * numRows);
+ int maxHeight = (availableHeightPx - workspacePadding.top - workspacePadding.bottom);
+ if (usedHeight > maxHeight) {
+ scale = maxHeight / usedHeight;
+ drawablePadding = 0;
+ }
+ updateIconSize(scale, drawablePadding, resources, dm);
+
+ // Make the callbacks
+ for (DeviceProfileCallbacks cb : mCallbacks) {
+ cb.onAvailableSizeChanged(this);
+ }
+ }
+
+ private void updateIconSize(float scale, int drawablePadding, Resources resources,
+ DisplayMetrics dm) {
+ iconSizePx = (int) (DynamicGrid.pxFromDp(iconSize, dm) * scale);
+ iconTextSizePx = (int) (DynamicGrid.pxFromSp(iconTextSize, dm) * scale);
+ iconDrawablePaddingPx = drawablePadding;
+ hotseatIconSizePx = (int) (DynamicGrid.pxFromDp(hotseatIconSize, dm) * scale);
+
+ // Search Bar
+ searchBarSpaceMaxWidthPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_max_width);
+ searchBarHeightPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_height);
+ searchBarSpaceWidthPx = Math.min(searchBarSpaceMaxWidthPx, widthPx);
+ searchBarSpaceHeightPx = searchBarHeightPx + getSearchBarTopOffset();
+
+ // Calculate the actual text height
+ Paint textPaint = new Paint();
+ textPaint.setTextSize(iconTextSizePx);
+ FontMetrics fm = textPaint.getFontMetrics();
+ cellWidthPx = iconSizePx;
+ cellHeightPx = iconSizePx + iconDrawablePaddingPx + (int) Math.ceil(fm.bottom - fm.top);
+
+ // Hotseat
+ hotseatBarHeightPx = iconSizePx + 4 * edgeMarginPx;
+ hotseatCellWidthPx = iconSizePx;
+ hotseatCellHeightPx = iconSizePx;
+
+ // Folder
+ folderCellWidthPx = cellWidthPx + 3 * edgeMarginPx;
+ folderCellHeightPx = cellHeightPx + edgeMarginPx;
+ folderBackgroundOffset = -edgeMarginPx;
+ folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
+
+ // All Apps
+ Rect padding = getWorkspacePadding(isLandscape ?
+ CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
+ int pageIndicatorOffset =
+ resources.getDimensionPixelSize(R.dimen.apps_customize_page_indicator_offset);
+ allAppsCellWidthPx = allAppsIconSizePx;
+ allAppsCellHeightPx = allAppsIconSizePx + drawablePadding + iconTextSizePx;
+ int maxLongEdgeCellCount =
+ resources.getInteger(R.integer.config_dynamic_grid_max_long_edge_cell_count);
+ int maxShortEdgeCellCount =
+ resources.getInteger(R.integer.config_dynamic_grid_max_short_edge_cell_count);
+ int minEdgeCellCount =
+ resources.getInteger(R.integer.config_dynamic_grid_min_edge_cell_count);
+ int maxRows = (isLandscape ? maxShortEdgeCellCount : maxLongEdgeCellCount);
+ int maxCols = (isLandscape ? maxLongEdgeCellCount : maxShortEdgeCellCount);
+
+ allAppsNumRows = (availableHeightPx - pageIndicatorHeightPx) /
+ (allAppsCellHeightPx + allAppsCellPaddingPx);
+ allAppsNumRows = Math.max(minEdgeCellCount, Math.min(maxRows, allAppsNumRows));
+ allAppsNumCols = (availableWidthPx) /
+ (allAppsCellWidthPx + allAppsCellPaddingPx);
+ allAppsNumCols = Math.max(minEdgeCellCount, Math.min(maxCols, allAppsNumCols));
+ }
+
+ void updateFromConfiguration(Context context, Resources resources, int wPx, int hPx,
+ int awPx, int ahPx) {
+ isLandscape = (resources.getConfiguration().orientation ==
+ Configuration.ORIENTATION_LANDSCAPE);
+ isTablet = resources.getBoolean(R.bool.is_tablet);
+ isLargeTablet = resources.getBoolean(R.bool.is_large_tablet);
+ widthPx = wPx;
+ heightPx = hPx;
+ availableWidthPx = awPx;
+ availableHeightPx = ahPx;
+
+ updateAvailableDimensions(context);
+ }
+
+ private float dist(PointF p0, PointF p1) {
+ return (float) Math.sqrt((p1.x - p0.x)*(p1.x-p0.x) +
+ (p1.y-p0.y)*(p1.y-p0.y));
+ }
+
+ private float weight(PointF a, PointF b,
+ float pow) {
+ float d = dist(a, b);
+ if (d == 0f) {
+ return Float.POSITIVE_INFINITY;
+ }
+ return (float) (1f / Math.pow(d, pow));
+ }
+
+ private float invDistWeightedInterpolate(float width, float height,
+ ArrayList<DeviceProfileQuery> points) {
+ float sum = 0;
+ float weights = 0;
+ float pow = 5;
+ float kNearestNeighbors = 3;
+ final PointF xy = new PointF(width, height);
+
+ ArrayList<DeviceProfileQuery> pointsByNearness = points;
+ Collections.sort(pointsByNearness, new Comparator<DeviceProfileQuery>() {
+ public int compare(DeviceProfileQuery a, DeviceProfileQuery b) {
+ return (int) (dist(xy, a.dimens) - dist(xy, b.dimens));
+ }
+ });
+
+ for (int i = 0; i < pointsByNearness.size(); ++i) {
+ DeviceProfileQuery p = pointsByNearness.get(i);
+ if (i < kNearestNeighbors) {
+ float w = weight(xy, p.dimens, pow);
+ if (w == Float.POSITIVE_INFINITY) {
+ return p.value;
+ }
+ weights += w;
+ }
+ }
+
+ for (int i = 0; i < pointsByNearness.size(); ++i) {
+ DeviceProfileQuery p = pointsByNearness.get(i);
+ if (i < kNearestNeighbors) {
+ float w = weight(xy, p.dimens, pow);
+ sum += w * p.value / weights;
+ }
+ }
+
+ return sum;
+ }
+
+ /** Returns the search bar top offset */
+ int getSearchBarTopOffset() {
+ if (isTablet() && !isVerticalBarLayout()) {
+ return 4 * edgeMarginPx;
+ } else {
+ return 2 * edgeMarginPx;
+ }
+ }
+
+ /** Returns the search bar bounds in the current orientation */
+ Rect getSearchBarBounds() {
+ return getSearchBarBounds(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
+ }
+ /** Returns the search bar bounds in the specified orientation */
+ Rect getSearchBarBounds(int orientation) {
+ Rect bounds = new Rect();
+ if (orientation == CellLayout.LANDSCAPE &&
+ transposeLayoutWithOrientation) {
+ bounds.set(0, edgeMarginPx, searchBarSpaceHeightPx, availableHeightPx - edgeMarginPx);
+ } else {
+ if (isTablet()) {
+ // Pad the left and right of the workspace to ensure consistent spacing
+ // between all icons
+ int width = (orientation == CellLayout.LANDSCAPE)
+ ? Math.max(widthPx, heightPx)
+ : Math.min(widthPx, heightPx);
+ // XXX: If the icon size changes across orientations, we will have to take
+ // that into account here too.
+ int gap = (int) ((width - 2 * edgeMarginPx -
+ (numColumns * cellWidthPx)) / (2 * (numColumns + 1)));
+ bounds.set(edgeMarginPx + gap, getSearchBarTopOffset(),
+ availableWidthPx - (edgeMarginPx + gap),
+ searchBarSpaceHeightPx);
+ } else {
+ bounds.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left,
+ getSearchBarTopOffset(),
+ availableWidthPx - (desiredWorkspaceLeftRightMarginPx -
+ defaultWidgetPadding.right), searchBarSpaceHeightPx);
+ }
+ }
+ return bounds;
+ }
+
+ /** Returns the workspace padding in the specified orientation */
+ Rect getWorkspacePadding() {
+ return getWorkspacePadding(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
+ }
+ Rect getWorkspacePadding(int orientation) {
+ Rect searchBarBounds = getSearchBarBounds(orientation);
+ Rect padding = new Rect();
+ if (orientation == CellLayout.LANDSCAPE &&
+ transposeLayoutWithOrientation) {
+ // Pad the left and right of the workspace with search/hotseat bar sizes
+ padding.set(searchBarBounds.right, edgeMarginPx,
+ hotseatBarHeightPx, edgeMarginPx);
+ } else {
+ if (isTablet()) {
+ // Pad the left and right of the workspace to ensure consistent spacing
+ // between all icons
+ int width = (orientation == CellLayout.LANDSCAPE)
+ ? Math.max(widthPx, heightPx)
+ : Math.min(widthPx, heightPx);
+ // XXX: If the icon size changes across orientations, we will have to take
+ // that into account here too.
+ int gap = (int) ((width - 2 * edgeMarginPx -
+ (numColumns * cellWidthPx)) / (2 * (numColumns + 1)));
+ padding.set(edgeMarginPx + gap,
+ searchBarBounds.bottom,
+ edgeMarginPx + gap,
+ hotseatBarHeightPx + pageIndicatorHeightPx);
+ } else {
+ // Pad the top and bottom of the workspace with search/hotseat bar sizes
+ padding.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left,
+ searchBarBounds.bottom,
+ desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right,
+ hotseatBarHeightPx + pageIndicatorHeightPx);
+ }
+ }
+ return padding;
+ }
+
+ int getWorkspacePageSpacing(int orientation) {
+ if (orientation == CellLayout.LANDSCAPE &&
+ transposeLayoutWithOrientation) {
+ // In landscape mode the page spacing is set to the default.
+ return defaultPageSpacingPx;
+ } else {
+ // In portrait, we want the pages spaced such that there is no
+ // overhang of the previous / next page into the current page viewport.
+ // We assume symmetrical padding in portrait mode.
+ return 2 * getWorkspacePadding().left;
+ }
+ }
+
+ Rect getOverviewModeButtonBarRect() {
+ int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx);
+ zoneHeight = Math.min(overviewModeMaxIconZoneHeightPx,
+ Math.max(overviewModeMinIconZoneHeightPx, zoneHeight));
+ return new Rect(0, availableHeightPx - zoneHeight, 0, availableHeightPx);
+ }
+
+ float getOverviewModeScale() {
+ Rect workspacePadding = getWorkspacePadding();
+ Rect overviewBar = getOverviewModeButtonBarRect();
+ int pageSpace = availableHeightPx - workspacePadding.top - workspacePadding.bottom;
+ return (overviewModeScaleFactor * (pageSpace - overviewBar.height())) / pageSpace;
+ }
+
+ // The rect returned will be extended to below the system ui that covers the workspace
+ Rect getHotseatRect() {
+ if (isVerticalBarLayout()) {
+ return new Rect(availableWidthPx - hotseatBarHeightPx, 0,
+ Integer.MAX_VALUE, availableHeightPx);
+ } else {
+ return new Rect(0, availableHeightPx - hotseatBarHeightPx,
+ availableWidthPx, Integer.MAX_VALUE);
+ }
+ }
+
+ int calculateCellWidth(int width, int countX) {
+ return width / countX;
+ }
+ int calculateCellHeight(int height, int countY) {
+ return height / countY;
+ }
+
+ boolean isPhone() {
+ return !isTablet && !isLargeTablet;
+ }
+ boolean isTablet() {
+ return isTablet;
+ }
+ boolean isLargeTablet() {
+ return isLargeTablet;
+ }
+
+ boolean isVerticalBarLayout() {
+ return isLandscape && transposeLayoutWithOrientation;
+ }
+
+ boolean shouldFadeAdjacentWorkspaceScreens() {
+ return isVerticalBarLayout() || isLargeTablet();
+ }
+
+ public void layout(Launcher launcher) {
+ FrameLayout.LayoutParams lp;
+ Resources res = launcher.getResources();
+ boolean hasVerticalBarLayout = isVerticalBarLayout();
+
+ // Layout the search bar space
+ View searchBar = launcher.getSearchBar();
+ lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
+ if (hasVerticalBarLayout) {
+ // Vertical search bar space
+ lp.gravity = Gravity.TOP | Gravity.LEFT;
+ lp.width = searchBarSpaceHeightPx;
+ lp.height = LayoutParams.MATCH_PARENT;
+ searchBar.setPadding(
+ 0, 2 * edgeMarginPx, 0,
+ 2 * edgeMarginPx);
+ } else {
+ // Horizontal search bar space
+ lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+ lp.width = searchBarSpaceWidthPx;
+ lp.height = searchBarSpaceHeightPx;
+ searchBar.setPadding(
+ 2 * edgeMarginPx,
+ getSearchBarTopOffset(),
+ 2 * edgeMarginPx, 0);
+ }
+ searchBar.setLayoutParams(lp);
+
+ // Layout the search bar
+ View qsbBar = launcher.getQsbBar();
+ LayoutParams vglp = qsbBar.getLayoutParams();
+ vglp.width = LayoutParams.MATCH_PARENT;
+ vglp.height = LayoutParams.MATCH_PARENT;
+ qsbBar.setLayoutParams(vglp);
+
+ // Layout the voice proxy
+ View voiceButtonProxy = launcher.findViewById(R.id.voice_button_proxy);
+ if (voiceButtonProxy != null) {
+ if (hasVerticalBarLayout) {
+ // TODO: MOVE THIS INTO SEARCH BAR MEASURE
+ } else {
+ lp = (FrameLayout.LayoutParams) voiceButtonProxy.getLayoutParams();
+ lp.gravity = Gravity.TOP | Gravity.END;
+ lp.width = (widthPx - searchBarSpaceWidthPx) / 2 +
+ 2 * iconSizePx;
+ lp.height = searchBarSpaceHeightPx;
+ }
+ }
+
+ // Layout the workspace
+ PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace);
+ lp = (FrameLayout.LayoutParams) workspace.getLayoutParams();
+ lp.gravity = Gravity.CENTER;
+ int orientation = isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT;
+ Rect padding = getWorkspacePadding(orientation);
+ workspace.setLayoutParams(lp);
+ workspace.setPadding(padding.left, padding.top, padding.right, padding.bottom);
+ workspace.setPageSpacing(getWorkspacePageSpacing(orientation));
+
+ // Layout the hotseat
+ View hotseat = launcher.findViewById(R.id.hotseat);
+ lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams();
+ if (hasVerticalBarLayout) {
+ // Vertical hotseat
+ lp.gravity = Gravity.RIGHT;
+ lp.width = hotseatBarHeightPx;
+ lp.height = LayoutParams.MATCH_PARENT;
+ hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx);
+ } else if (isTablet()) {
+ // Pad the hotseat with the grid gap calculated above
+ int gridGap = (int) ((widthPx - 2 * edgeMarginPx -
+ (numColumns * cellWidthPx)) / (2 * (numColumns + 1)));
+ int gridWidth = (int) ((numColumns * cellWidthPx) +
+ ((numColumns - 1) * gridGap));
+ int hotseatGap = (int) Math.max(0,
+ (gridWidth - (numHotseatIcons * hotseatCellWidthPx))
+ / (numHotseatIcons - 1));
+ lp.gravity = Gravity.BOTTOM;
+ lp.width = LayoutParams.MATCH_PARENT;
+ lp.height = hotseatBarHeightPx;
+ hotseat.setPadding(2 * edgeMarginPx + gridGap + hotseatGap, 0,
+ 2 * edgeMarginPx + gridGap + hotseatGap,
+ 2 * edgeMarginPx);
+ } else {
+ // For phones, layout the hotseat without any bottom margin
+ // to ensure that we have space for the folders
+ lp.gravity = Gravity.BOTTOM;
+ lp.width = LayoutParams.MATCH_PARENT;
+ lp.height = hotseatBarHeightPx;
+ hotseat.findViewById(R.id.layout).setPadding(2 * edgeMarginPx, 0,
+ 2 * edgeMarginPx, 0);
+ }
+ hotseat.setLayoutParams(lp);
+
+ // Layout the page indicators
+ View pageIndicator = launcher.findViewById(R.id.page_indicator);
+ if (pageIndicator != null) {
+ if (hasVerticalBarLayout) {
+ // Hide the page indicators when we have vertical search/hotseat
+ pageIndicator.setVisibility(View.GONE);
+ } else {
+ // Put the page indicators above the hotseat
+ lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
+ lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+ lp.width = LayoutParams.WRAP_CONTENT;
+ lp.height = LayoutParams.WRAP_CONTENT;
+ lp.bottomMargin = hotseatBarHeightPx;
+ pageIndicator.setLayoutParams(lp);
+ }
+ }
+
+ // Layout AllApps
+ AppsCustomizeTabHost host = (AppsCustomizeTabHost)
+ launcher.findViewById(R.id.apps_customize_pane);
+ if (host != null) {
+ // Center the all apps page indicator
+ int pageIndicatorHeight = (int) (pageIndicatorHeightPx * Math.min(1f,
+ (allAppsIconSizePx / DynamicGrid.DEFAULT_ICON_SIZE_PX)));
+ pageIndicator = host.findViewById(R.id.apps_customize_page_indicator);
+ if (pageIndicator != null) {
+ lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
+ lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+ lp.width = LayoutParams.WRAP_CONTENT;
+ lp.height = pageIndicatorHeight;
+ pageIndicator.setLayoutParams(lp);
+ }
+
+ AppsCustomizePagedView pagedView = (AppsCustomizePagedView)
+ host.findViewById(R.id.apps_customize_pane_content);
+ padding = new Rect();
+ if (pagedView != null) {
+ // Constrain the dimensions of all apps so that it does not span the full width
+ int paddingLR = (availableWidthPx - (allAppsCellWidthPx * allAppsNumCols)) /
+ (2 * (allAppsNumCols + 1));
+ int paddingTB = (availableHeightPx - (allAppsCellHeightPx * allAppsNumRows)) /
+ (2 * (allAppsNumRows + 1));
+ paddingLR = Math.min(paddingLR, (int)((paddingLR + paddingTB) * 0.75f));
+ paddingTB = Math.min(paddingTB, (int)((paddingLR + paddingTB) * 0.75f));
+ int maxAllAppsWidth = (allAppsNumCols * (allAppsCellWidthPx + 2 * paddingLR));
+ int gridPaddingLR = (availableWidthPx - maxAllAppsWidth) / 2;
+ if (gridPaddingLR > (allAppsCellWidthPx / 4)) {
+ padding.left = padding.right = gridPaddingLR;
+ }
+ // The icons are centered, so we can't just offset by the page indicator height
+ // because the empty space will actually be pageIndicatorHeight + paddingTB
+ padding.bottom = Math.max(0, pageIndicatorHeight - paddingTB);
+ pagedView.setAllAppsPadding(padding);
+ pagedView.setWidgetsPageIndicatorPadding(pageIndicatorHeight);
+ }
+ }
+
+ // Layout the Overview Mode
+ View overviewMode = launcher.getOverviewPanel();
+ if (overviewMode != null) {
+ Rect r = getOverviewModeButtonBarRect();
+ lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams();
+ lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+ lp.width = Math.min(availableWidthPx, overviewModeMaxBarWidthPx);
+ lp.height = r.height();
+ overviewMode.setLayoutParams(lp);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java
index 5e733f0..1bfaa23 100644
--- a/src/com/android/launcher3/DragController.java
+++ b/src/com/android/launcher3/DragController.java
@@ -198,7 +198,7 @@
* @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
* Makes dragging feel more precise, e.g. you can clip out a transparent border
*/
- public void startDrag(Bitmap b, int dragLayerX, int dragLayerY,
+ public DragView startDrag(Bitmap b, int dragLayerX, int dragLayerY,
DragSource source, Object dragInfo, int dragAction, Point dragOffset, Rect dragRegion,
float initialDragViewScale) {
if (PROFILE_DRAWING_DURING_DRAG) {
@@ -245,6 +245,7 @@
mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
dragView.show(mMotionDownX, mMotionDownY);
handleMoveEvent(mMotionDownX, mMotionDownY);
+ return dragView;
}
/**
@@ -327,7 +328,7 @@
for (AppInfo info : appInfos) {
// Added null checks to prevent NPE we've seen in the wild
if (dragInfo != null &&
- dragInfo.intent != null) {
+ dragInfo.intent != null && info != null) {
ComponentName cn = dragInfo.intent.getComponent();
boolean isSameComponent = cn.equals(info.componentName) ||
packageNames.contains(cn.getPackageName());
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 159d7d9..dc0ba90 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -522,14 +522,18 @@
scale *= childScale;
int toX = coord[0];
int toY = coord[1];
+ float toScale = scale;
if (child instanceof TextView) {
TextView tv = (TextView) child;
+ // Account for the source scale of the icon (ie. from AllApps to Workspace, in which
+ // the workspace may have smaller icon bounds).
+ toScale = scale / dragView.getIntrinsicIconScaleFactor();
// The child may be scaled (always about the center of the view) so to account for it,
// we have to offset the position by the scaled size. Once we do that, we can center
// the drag view about the scaled child view.
- toY += Math.round(scale * tv.getPaddingTop());
- toY -= dragView.getMeasuredHeight() * (1 - scale) / 2;
+ toY += Math.round(toScale * tv.getPaddingTop());
+ toY -= dragView.getMeasuredHeight() * (1 - toScale) / 2;
toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
} else if (child instanceof FolderIcon) {
// Account for holographic blur padding on the drag view
@@ -555,7 +559,7 @@
}
}
};
- animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, scale, scale,
+ animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, toScale, toScale,
onCompleteRunnable, ANIMATION_END_DISAPPEAR, duration, anchorView);
}
diff --git a/src/com/android/launcher3/DragSource.java b/src/com/android/launcher3/DragSource.java
index 2ef99ae..cca9ab1 100644
--- a/src/com/android/launcher3/DragSource.java
+++ b/src/com/android/launcher3/DragSource.java
@@ -30,6 +30,11 @@
*/
boolean supportsFlingToDelete();
+ /*
+ * @return the scale of the icons over the workspace icon size
+ */
+ float getIntrinsicIconScaleFactor();
+
/**
* A callback specifically made back to the source after an item from this source has been flung
* to be deleted on a DropTarget. In such a situation, this method will be called after
diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/DragView.java
index 686cf62..b66b55c 100644
--- a/src/com/android/launcher3/DragView.java
+++ b/src/com/android/launcher3/DragView.java
@@ -51,6 +51,9 @@
private float mOffsetX = 0.0f;
private float mOffsetY = 0.0f;
private float mInitialScale = 1f;
+ // The intrinsic icon scale factor is the scale factor for a drag icon over the workspace
+ // size. This is ignored for non-icons.
+ private float mIntrinsicIconScale = 1f;
/**
* Construct the drag view.
@@ -120,6 +123,15 @@
mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
}
+ /** Sets the scale of the view over the normal workspace icon size. */
+ public void setIntrinsicIconScaleFactor(float scale) {
+ mIntrinsicIconScale = scale;
+ }
+
+ public float getIntrinsicIconScaleFactor() {
+ return mIntrinsicIconScale;
+ }
+
public float getOffsetY() {
return mOffsetY;
}
diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java
index 0d759a1..ce78553 100644
--- a/src/com/android/launcher3/DynamicGrid.java
+++ b/src/com/android/launcher3/DynamicGrid.java
@@ -16,632 +16,14 @@
package com.android.launcher3;
-import android.appwidget.AppWidgetHostView;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Paint;
-import android.graphics.Paint.FontMetrics;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.util.TypedValue;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.Surface;
-import android.view.View;
-import android.view.ViewGroup.LayoutParams;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-class DeviceProfileQuery {
- float widthDps;
- float heightDps;
- float value;
- PointF dimens;
-
- DeviceProfileQuery(float w, float h, float v) {
- widthDps = w;
- heightDps = h;
- value = v;
- dimens = new PointF(w, h);
- }
-}
-
-class DeviceProfile {
- public static interface DeviceProfileCallbacks {
- public void onAvailableSizeChanged(DeviceProfile grid);
- }
-
- String name;
- float minWidthDps;
- float minHeightDps;
- float numRows;
- float numColumns;
- float numHotseatIcons;
- private float iconSize;
- private float iconTextSize;
- private int iconDrawablePaddingOriginalPx;
- private float hotseatIconSize;
-
- boolean isLandscape;
- boolean isTablet;
- boolean isLargeTablet;
- boolean transposeLayoutWithOrientation;
-
- int desiredWorkspaceLeftRightMarginPx;
- int edgeMarginPx;
- Rect defaultWidgetPadding;
-
- int widthPx;
- int heightPx;
- int availableWidthPx;
- int availableHeightPx;
- int defaultPageSpacingPx;
-
- int iconSizePx;
- int iconTextSizePx;
- int iconDrawablePaddingPx;
- int cellWidthPx;
- int cellHeightPx;
- int allAppsIconSizePx;
- int allAppsIconTextSizePx;
- int allAppsCellWidthPx;
- int allAppsCellHeightPx;
- int allAppsCellPaddingPx;
- int folderBackgroundOffset;
- int folderIconSizePx;
- int folderCellWidthPx;
- int folderCellHeightPx;
- int hotseatCellWidthPx;
- int hotseatCellHeightPx;
- int hotseatIconSizePx;
- int hotseatBarHeightPx;
- int hotseatAllAppsRank;
- int allAppsNumRows;
- int allAppsNumCols;
- int searchBarSpaceWidthPx;
- int searchBarSpaceMaxWidthPx;
- int searchBarSpaceHeightPx;
- int searchBarHeightPx;
- int pageIndicatorHeightPx;
-
- private ArrayList<DeviceProfileCallbacks> mCallbacks = new ArrayList<DeviceProfileCallbacks>();
-
- DeviceProfile(String n, float w, float h, float r, float c,
- float is, float its, float hs, float his) {
- // Ensure that we have an odd number of hotseat items (since we need to place all apps)
- if (!AppsCustomizePagedView.DISABLE_ALL_APPS && hs % 2 == 0) {
- throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces");
- }
-
- name = n;
- minWidthDps = w;
- minHeightDps = h;
- numRows = r;
- numColumns = c;
- iconSize = is;
- iconTextSize = its;
- numHotseatIcons = hs;
- hotseatIconSize = his;
- }
-
- DeviceProfile(Context context,
- ArrayList<DeviceProfile> profiles,
- float minWidth, float minHeight,
- int wPx, int hPx,
- int awPx, int ahPx,
- Resources res) {
- DisplayMetrics dm = res.getDisplayMetrics();
- ArrayList<DeviceProfileQuery> points =
- new ArrayList<DeviceProfileQuery>();
- transposeLayoutWithOrientation =
- res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
- minWidthDps = minWidth;
- minHeightDps = minHeight;
-
- ComponentName cn = new ComponentName(context.getPackageName(),
- this.getClass().getName());
- defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
- edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
- desiredWorkspaceLeftRightMarginPx = 2 * edgeMarginPx;
- pageIndicatorHeightPx =
- res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height);
- defaultPageSpacingPx =
- res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
- allAppsCellPaddingPx =
- res.getDimensionPixelSize(R.dimen.dynamic_grid_all_apps_cell_padding);
-
- // Interpolate the rows
- for (DeviceProfile p : profiles) {
- points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numRows));
- }
- numRows = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
- // Interpolate the columns
- points.clear();
- for (DeviceProfile p : profiles) {
- points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numColumns));
- }
- numColumns = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
- // Interpolate the hotseat length
- points.clear();
- for (DeviceProfile p : profiles) {
- points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numHotseatIcons));
- }
- numHotseatIcons = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
- hotseatAllAppsRank = (int) (numHotseatIcons / 2);
-
- // Interpolate the icon size
- points.clear();
- for (DeviceProfile p : profiles) {
- points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconSize));
- }
- iconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
- // AllApps uses the original non-scaled icon size
- allAppsIconSizePx = DynamicGrid.pxFromDp(iconSize, dm);
-
- // Interpolate the icon text size
- points.clear();
- for (DeviceProfile p : profiles) {
- points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconTextSize));
- }
- iconTextSize = invDistWeightedInterpolate(minWidth, minHeight, points);
- iconDrawablePaddingOriginalPx =
- res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
- // AllApps uses the original non-scaled icon text size
- allAppsIconTextSizePx = DynamicGrid.pxFromDp(iconTextSize, dm);
-
- // Interpolate the hotseat icon size
- points.clear();
- for (DeviceProfile p : profiles) {
- points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.hotseatIconSize));
- }
- // Hotseat
- hotseatIconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
-
- // Calculate the remaining vars
- updateFromConfiguration(context, res, wPx, hPx, awPx, ahPx);
- updateAvailableDimensions(context);
- }
-
- void addCallback(DeviceProfileCallbacks cb) {
- mCallbacks.add(cb);
- cb.onAvailableSizeChanged(this);
- }
- void removeCallback(DeviceProfileCallbacks cb) {
- mCallbacks.remove(cb);
- }
-
- private int getDeviceOrientation(Context context) {
- WindowManager windowManager = (WindowManager)
- context.getSystemService(Context.WINDOW_SERVICE);
- Resources resources = context.getResources();
- DisplayMetrics dm = resources.getDisplayMetrics();
- Configuration config = resources.getConfiguration();
- int rotation = windowManager.getDefaultDisplay().getRotation();
-
- boolean isLandscape = (config.orientation == Configuration.ORIENTATION_LANDSCAPE) &&
- (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180);
- boolean isRotatedPortrait = (config.orientation == Configuration.ORIENTATION_PORTRAIT) &&
- (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
- if (isLandscape || isRotatedPortrait) {
- return CellLayout.LANDSCAPE;
- } else {
- return CellLayout.PORTRAIT;
- }
- }
-
- private void updateAvailableDimensions(Context context) {
- WindowManager windowManager = (WindowManager)
- context.getSystemService(Context.WINDOW_SERVICE);
- Display display = windowManager.getDefaultDisplay();
- Resources resources = context.getResources();
- DisplayMetrics dm = resources.getDisplayMetrics();
- Configuration config = resources.getConfiguration();
-
- // There are three possible configurations that the dynamic grid accounts for, portrait,
- // landscape with the nav bar at the bottom, and landscape with the nav bar at the side.
- // To prevent waiting for fitSystemWindows(), we make the observation that in landscape,
- // the height is the smallest height (either with the nav bar at the bottom or to the
- // side) and otherwise, the height is simply the largest possible height for a portrait
- // device.
- Point size = new Point();
- Point smallestSize = new Point();
- Point largestSize = new Point();
- display.getSize(size);
- display.getCurrentSizeRange(smallestSize, largestSize);
- availableWidthPx = size.x;
- if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
- availableHeightPx = smallestSize.y;
- } else {
- availableHeightPx = largestSize.y;
- }
-
- // Check to see if the icons fit in the new available height. If not, then we need to
- // shrink the icon size.
- Rect workspacePadding = getWorkspacePadding();
- float scale = 1f;
- int drawablePadding = iconDrawablePaddingOriginalPx;
- updateIconSize(1f, drawablePadding, resources, dm);
- float usedHeight = (cellHeightPx * numRows);
- int maxHeight = (availableHeightPx - workspacePadding.top - workspacePadding.bottom);
- if (usedHeight > maxHeight) {
- scale = maxHeight / usedHeight;
- drawablePadding = 0;
- }
- updateIconSize(scale, drawablePadding, resources, dm);
-
- // Make the callbacks
- for (DeviceProfileCallbacks cb : mCallbacks) {
- cb.onAvailableSizeChanged(this);
- }
- }
-
- private void updateIconSize(float scale, int drawablePadding, Resources resources,
- DisplayMetrics dm) {
- iconSizePx = (int) (DynamicGrid.pxFromDp(iconSize, dm) * scale);
- iconTextSizePx = (int) (DynamicGrid.pxFromSp(iconTextSize, dm) * scale);
- iconDrawablePaddingPx = drawablePadding;
- hotseatIconSizePx = (int) (DynamicGrid.pxFromDp(hotseatIconSize, dm) * scale);
-
- // Search Bar
- searchBarSpaceMaxWidthPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_max_width);
- searchBarHeightPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_height);
- searchBarSpaceWidthPx = Math.min(searchBarSpaceMaxWidthPx, widthPx);
- searchBarSpaceHeightPx = searchBarHeightPx + 2 * edgeMarginPx;
-
- // Calculate the actual text height
- Paint textPaint = new Paint();
- textPaint.setTextSize(iconTextSizePx);
- FontMetrics fm = textPaint.getFontMetrics();
- cellWidthPx = iconSizePx;
- cellHeightPx = iconSizePx + iconDrawablePaddingPx + (int) Math.ceil(fm.bottom - fm.top);
-
- // Hotseat
- hotseatBarHeightPx = iconSizePx + 4 * edgeMarginPx;
- hotseatCellWidthPx = iconSizePx;
- hotseatCellHeightPx = iconSizePx;
-
- // Folder
- folderCellWidthPx = cellWidthPx + 3 * edgeMarginPx;
- folderCellHeightPx = cellHeightPx + edgeMarginPx;
- folderBackgroundOffset = -edgeMarginPx;
- folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
-
- // All Apps
- Rect padding = getWorkspacePadding(isLandscape ?
- CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
- int pageIndicatorOffset =
- resources.getDimensionPixelSize(R.dimen.apps_customize_page_indicator_offset);
- allAppsCellWidthPx = allAppsIconSizePx;
- allAppsCellHeightPx = allAppsIconSizePx + drawablePadding + iconTextSizePx;
- int maxLongEdgeCellCount =
- resources.getInteger(R.integer.config_dynamic_grid_max_long_edge_cell_count);
- int maxShortEdgeCellCount =
- resources.getInteger(R.integer.config_dynamic_grid_max_short_edge_cell_count);
- int minEdgeCellCount =
- resources.getInteger(R.integer.config_dynamic_grid_min_edge_cell_count);
- int maxRows = (isLandscape ? maxShortEdgeCellCount : maxLongEdgeCellCount);
- int maxCols = (isLandscape ? maxLongEdgeCellCount : maxShortEdgeCellCount);
-
- allAppsNumRows = (availableHeightPx - pageIndicatorHeightPx) /
- (allAppsCellHeightPx + allAppsCellPaddingPx);
- allAppsNumRows = Math.max(minEdgeCellCount, Math.min(maxRows, allAppsNumRows));
- allAppsNumCols = (availableWidthPx) /
- (allAppsCellWidthPx + allAppsCellPaddingPx);
- allAppsNumCols = Math.max(minEdgeCellCount, Math.min(maxCols, allAppsNumCols));
- }
-
- void updateFromConfiguration(Context context, Resources resources, int wPx, int hPx,
- int awPx, int ahPx) {
- isLandscape = (resources.getConfiguration().orientation ==
- Configuration.ORIENTATION_LANDSCAPE);
- isTablet = resources.getBoolean(R.bool.is_tablet);
- isLargeTablet = resources.getBoolean(R.bool.is_large_tablet);
- widthPx = wPx;
- heightPx = hPx;
- availableWidthPx = awPx;
- availableHeightPx = ahPx;
-
- updateAvailableDimensions(context);
- }
-
- private float dist(PointF p0, PointF p1) {
- return (float) Math.sqrt((p1.x - p0.x)*(p1.x-p0.x) +
- (p1.y-p0.y)*(p1.y-p0.y));
- }
-
- private float weight(PointF a, PointF b,
- float pow) {
- float d = dist(a, b);
- if (d == 0f) {
- return Float.POSITIVE_INFINITY;
- }
- return (float) (1f / Math.pow(d, pow));
- }
-
- private float invDistWeightedInterpolate(float width, float height,
- ArrayList<DeviceProfileQuery> points) {
- float sum = 0;
- float weights = 0;
- float pow = 5;
- float kNearestNeighbors = 3;
- final PointF xy = new PointF(width, height);
-
- ArrayList<DeviceProfileQuery> pointsByNearness = points;
- Collections.sort(pointsByNearness, new Comparator<DeviceProfileQuery>() {
- public int compare(DeviceProfileQuery a, DeviceProfileQuery b) {
- return (int) (dist(xy, a.dimens) - dist(xy, b.dimens));
- }
- });
-
- for (int i = 0; i < pointsByNearness.size(); ++i) {
- DeviceProfileQuery p = pointsByNearness.get(i);
- if (i < kNearestNeighbors) {
- float w = weight(xy, p.dimens, pow);
- if (w == Float.POSITIVE_INFINITY) {
- return p.value;
- }
- weights += w;
- }
- }
-
- for (int i = 0; i < pointsByNearness.size(); ++i) {
- DeviceProfileQuery p = pointsByNearness.get(i);
- if (i < kNearestNeighbors) {
- float w = weight(xy, p.dimens, pow);
- sum += w * p.value / weights;
- }
- }
-
- return sum;
- }
-
- Rect getWorkspacePadding() {
- return getWorkspacePadding(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
- }
-
- Rect getWorkspacePadding(int orientation) {
- Rect padding = new Rect();
- if (orientation == CellLayout.LANDSCAPE &&
- transposeLayoutWithOrientation) {
- // Pad the left and right of the workspace with search/hotseat bar sizes
- padding.set(searchBarSpaceHeightPx, edgeMarginPx,
- hotseatBarHeightPx, edgeMarginPx);
- } else {
- if (isTablet()) {
- // Pad the left and right of the workspace to ensure consistent spacing
- // between all icons
- int width = (orientation == CellLayout.LANDSCAPE)
- ? Math.max(widthPx, heightPx)
- : Math.min(widthPx, heightPx);
- // XXX: If the icon size changes across orientations, we will have to take
- // that into account here too.
- int gap = (int) ((width - 2 * edgeMarginPx -
- (numColumns * cellWidthPx)) / (2 * (numColumns + 1)));
- padding.set(edgeMarginPx + gap,
- searchBarSpaceHeightPx,
- edgeMarginPx + gap,
- hotseatBarHeightPx + pageIndicatorHeightPx);
- } else {
- // Pad the top and bottom of the workspace with search/hotseat bar sizes
- padding.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left,
- searchBarSpaceHeightPx,
- desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right,
- hotseatBarHeightPx + pageIndicatorHeightPx);
- }
- }
- return padding;
- }
-
- int getWorkspacePageSpacing(int orientation) {
- if (orientation == CellLayout.LANDSCAPE &&
- transposeLayoutWithOrientation) {
- // In landscape mode the page spacing is set to the default.
- return defaultPageSpacingPx;
- } else {
- // In portrait, we want the pages spaced such that there is no
- // overhang of the previous / next page into the current page viewport.
- // We assume symmetrical padding in portrait mode.
- return 2 * getWorkspacePadding().left;
- }
- }
-
- // The rect returned will be extended to below the system ui that covers the workspace
- Rect getHotseatRect() {
- if (isVerticalBarLayout()) {
- return new Rect(availableWidthPx - hotseatBarHeightPx, 0,
- Integer.MAX_VALUE, availableHeightPx);
- } else {
- return new Rect(0, availableHeightPx - hotseatBarHeightPx,
- availableWidthPx, Integer.MAX_VALUE);
- }
- }
-
- int calculateCellWidth(int width, int countX) {
- return width / countX;
- }
- int calculateCellHeight(int height, int countY) {
- return height / countY;
- }
-
- boolean isPhone() {
- return !isTablet && !isLargeTablet;
- }
- boolean isTablet() {
- return isTablet;
- }
- boolean isLargeTablet() {
- return isLargeTablet;
- }
-
- boolean isVerticalBarLayout() {
- return isLandscape && transposeLayoutWithOrientation;
- }
-
- boolean shouldFadeAdjacentWorkspaceScreens() {
- return isVerticalBarLayout() || isLargeTablet();
- }
-
- public void layout(Launcher launcher) {
- FrameLayout.LayoutParams lp;
- Resources res = launcher.getResources();
- boolean hasVerticalBarLayout = isVerticalBarLayout();
-
- // Layout the search bar space
- View searchBar = launcher.getSearchBar();
- lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
- if (hasVerticalBarLayout) {
- // Vertical search bar
- lp.gravity = Gravity.TOP | Gravity.LEFT;
- lp.width = searchBarSpaceHeightPx;
- lp.height = LayoutParams.MATCH_PARENT;
- searchBar.setPadding(
- 0, 2 * edgeMarginPx, 0,
- 2 * edgeMarginPx);
- } else {
- // Horizontal search bar
- lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
- lp.width = searchBarSpaceWidthPx;
- lp.height = searchBarSpaceHeightPx;
- searchBar.setPadding(
- 2 * edgeMarginPx,
- 2 * edgeMarginPx,
- 2 * edgeMarginPx, 0);
- }
- searchBar.setLayoutParams(lp);
-
- // Layout the search bar
- View qsbBar = launcher.getQsbBar();
- LayoutParams vglp = qsbBar.getLayoutParams();
- vglp.width = LayoutParams.MATCH_PARENT;
- vglp.height = LayoutParams.MATCH_PARENT;
- qsbBar.setLayoutParams(vglp);
-
- // Layout the voice proxy
- View voiceButtonProxy = launcher.findViewById(R.id.voice_button_proxy);
- if (voiceButtonProxy != null) {
- if (hasVerticalBarLayout) {
- // TODO: MOVE THIS INTO SEARCH BAR MEASURE
- } else {
- lp = (FrameLayout.LayoutParams) voiceButtonProxy.getLayoutParams();
- lp.gravity = Gravity.TOP | Gravity.END;
- lp.width = (widthPx - searchBarSpaceWidthPx) / 2 +
- 2 * iconSizePx;
- lp.height = searchBarSpaceHeightPx;
- }
- }
-
- // Layout the workspace
- PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace);
- lp = (FrameLayout.LayoutParams) workspace.getLayoutParams();
- lp.gravity = Gravity.CENTER;
- int orientation = isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT;
- Rect padding = getWorkspacePadding(orientation);
- workspace.setLayoutParams(lp);
- workspace.setPadding(padding.left, padding.top, padding.right, padding.bottom);
- workspace.setPageSpacing(getWorkspacePageSpacing(orientation));
-
- // Layout the hotseat
- View hotseat = launcher.findViewById(R.id.hotseat);
- lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams();
- if (hasVerticalBarLayout) {
- // Vertical hotseat
- lp.gravity = Gravity.RIGHT;
- lp.width = hotseatBarHeightPx;
- lp.height = LayoutParams.MATCH_PARENT;
- hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx);
- } else if (isTablet()) {
- // Pad the hotseat with the grid gap calculated above
- int gridGap = (int) ((widthPx - 2 * edgeMarginPx -
- (numColumns * cellWidthPx)) / (2 * (numColumns + 1)));
- int gridWidth = (int) ((numColumns * cellWidthPx) +
- ((numColumns - 1) * gridGap));
- int hotseatGap = (int) Math.max(0,
- (gridWidth - (numHotseatIcons * hotseatCellWidthPx))
- / (numHotseatIcons - 1));
- lp.gravity = Gravity.BOTTOM;
- lp.width = LayoutParams.MATCH_PARENT;
- lp.height = hotseatBarHeightPx;
- hotseat.setPadding(2 * edgeMarginPx + gridGap + hotseatGap, 0,
- 2 * edgeMarginPx + gridGap + hotseatGap,
- 2 * edgeMarginPx);
- } else {
- // For phones, layout the hotseat without any bottom margin
- // to ensure that we have space for the folders
- lp.gravity = Gravity.BOTTOM;
- lp.width = LayoutParams.MATCH_PARENT;
- lp.height = hotseatBarHeightPx;
- hotseat.findViewById(R.id.layout).setPadding(2 * edgeMarginPx, 0,
- 2 * edgeMarginPx, 0);
- }
- hotseat.setLayoutParams(lp);
-
- // Layout the page indicators
- View pageIndicator = launcher.findViewById(R.id.page_indicator);
- if (pageIndicator != null) {
- if (hasVerticalBarLayout) {
- // Hide the page indicators when we have vertical search/hotseat
- pageIndicator.setVisibility(View.GONE);
- } else {
- // Put the page indicators above the hotseat
- lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
- lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
- lp.width = LayoutParams.WRAP_CONTENT;
- lp.height = LayoutParams.WRAP_CONTENT;
- lp.bottomMargin = hotseatBarHeightPx;
- pageIndicator.setLayoutParams(lp);
- }
- }
-
- AppsCustomizeTabHost host = (AppsCustomizeTabHost)
- launcher.findViewById(R.id.apps_customize_pane);
- if (host != null) {
- // Center the all apps page indicator
- int pageIndicatorHeight = (int) (pageIndicatorHeightPx * Math.min(1f,
- (allAppsIconSizePx / DynamicGrid.DEFAULT_ICON_SIZE_PX)));
- pageIndicator = host.findViewById(R.id.apps_customize_page_indicator);
- if (pageIndicator != null) {
- lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
- lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
- lp.width = LayoutParams.WRAP_CONTENT;
- lp.height = pageIndicatorHeight;
- pageIndicator.setLayoutParams(lp);
- }
-
- AppsCustomizePagedView pagedView = (AppsCustomizePagedView)
- host.findViewById(R.id.apps_customize_pane_content);
- padding = new Rect();
- if (pagedView != null) {
- // Constrain the dimensions of all apps so that it does not span the full width
- int paddingLR = (availableWidthPx - (allAppsCellWidthPx * allAppsNumCols)) /
- (2 * (allAppsNumCols + 1));
- int paddingTB = (availableHeightPx - (allAppsCellHeightPx * allAppsNumRows)) /
- (2 * (allAppsNumRows + 1));
- paddingLR = Math.min(paddingLR, (int)((paddingLR + paddingTB) * 0.75f));
- paddingTB = Math.min(paddingTB, (int)((paddingLR + paddingTB) * 0.75f));
- int maxAllAppsWidth = (allAppsNumCols * (allAppsCellWidthPx + 2 * paddingLR));
- int gridPaddingLR = (availableWidthPx - maxAllAppsWidth) / 2;
- if (gridPaddingLR > (allAppsCellWidthPx / 4)) {
- padding.left = padding.right = gridPaddingLR;
- }
- // The icons are centered, so we can't just offset by the page indicator height
- // because the empty space will actually be pageIndicatorHeight + paddingTB
- padding.bottom = Math.max(0, pageIndicatorHeight - paddingTB);
- pagedView.setAllAppsPadding(padding);
- pagedView.setWidgetsPageIndicatorPadding(pageIndicatorHeight);
- }
- }
- }
-}
-
public class DynamicGrid {
@SuppressWarnings("unused")
private static final String TAG = "DynamicGrid";
@@ -692,10 +74,10 @@
// The tablet profile is odd in that the landscape orientation
// also includes the nav bar on the side
deviceProfiles.add(new DeviceProfile("Nexus 7",
- 575, 904, 6, 6, 72, 14.4f, 7, 60));
+ 575, 904, 5, 5, 72, 14.4f, 7, 60));
// Larger tablet profiles always have system bars on the top & bottom
deviceProfiles.add(new DeviceProfile("Nexus 10",
- 727, 1207, 5, 8, 80, 14.4f, 9, 64));
+ 727, 1207, 5, 5, 80, 14.4f, 7, 64));
/*
deviceProfiles.add(new DeviceProfile("Nexus 7",
600, 960, 5, 5, 72, 14.4f, 5, 60));
@@ -713,7 +95,7 @@
resources);
}
- DeviceProfile getDeviceProfile() {
+ public DeviceProfile getDeviceProfile() {
return mProfile;
}
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index bce6707..85e9020 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -27,19 +27,12 @@
class FastBitmapDrawable extends Drawable {
private Bitmap mBitmap;
private int mAlpha;
- private int mWidth;
- private int mHeight;
private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
FastBitmapDrawable(Bitmap b) {
- mAlpha = 255;
+ mAlpha = 255;
mBitmap = b;
- if (b != null) {
- mWidth = mBitmap.getWidth();
- mHeight = mBitmap.getHeight();
- } else {
- mWidth = mHeight = 0;
- }
+ setBounds(0, 0, b.getWidth(), b.getHeight());
}
@Override
@@ -76,32 +69,22 @@
@Override
public int getIntrinsicWidth() {
- return mWidth;
+ return getBounds().width();
}
@Override
public int getIntrinsicHeight() {
- return mHeight;
+ return getBounds().height();
}
@Override
public int getMinimumWidth() {
- return mWidth;
+ return getBounds().width();
}
@Override
public int getMinimumHeight() {
- return mHeight;
- }
-
- public void setBitmap(Bitmap b) {
- mBitmap = b;
- if (b != null) {
- mWidth = mBitmap.getWidth();
- mHeight = mBitmap.getHeight();
- } else {
- mWidth = mHeight = 0;
- }
+ return getBounds().height();
}
public Bitmap getBitmap() {
diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
index 78fdadd..f4c49d7 100644
--- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java
+++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
@@ -91,29 +91,30 @@
mStartTime = currentTime;
}
+ final long currentPlayTime = animation.getCurrentPlayTime();
if (!mHandlingOnAnimationUpdate &&
sVisible &&
// If the current play time exceeds the duration, the animation
// will get finished, even if we call setCurrentPlayTime -- therefore
// don't adjust the animation in that case
- animation.getCurrentPlayTime() < animation.getDuration()) {
+ currentPlayTime < animation.getDuration()) {
mHandlingOnAnimationUpdate = true;
long frameNum = sGlobalFrameCounter - mStartFrame;
// If we haven't drawn our first frame, reset the time to t = 0
// (give up after MAX_DELAY ms of waiting though - might happen, for example, if we
// are no longer in the foreground and no frames are being rendered ever)
- if (frameNum == 0 && currentTime < mStartTime + MAX_DELAY) {
+ if (frameNum == 0 && currentTime < mStartTime + MAX_DELAY && currentPlayTime > 0) {
// The first frame on animations doesn't always trigger an invalidate...
// force an invalidate here to make sure the animation continues to advance
mTarget.getRootView().invalidate();
animation.setCurrentPlayTime(0);
-
// For the second frame, if the first frame took more than 16ms,
// adjust the start time and pretend it took only 16ms anyway. This
// prevents a large jump in the animation due to an expensive first frame
} else if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY &&
!mAdjustedSecondFrameTime &&
- currentTime > mStartTime + IDEAL_FRAME_DURATION) {
+ currentTime > mStartTime + IDEAL_FRAME_DURATION &&
+ currentPlayTime > IDEAL_FRAME_DURATION) {
animation.setCurrentPlayTime(IDEAL_FRAME_DURATION);
mAdjustedSecondFrameTime = true;
} else {
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index bd61010..1d234ff 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -119,6 +119,11 @@
private int DRAG_MODE_REORDER = 1;
private int mDragMode = DRAG_MODE_NONE;
+ // We avoid measuring the scroll view with a 0 width or height, as this
+ // results in CellLayout being measured as UNSPECIFIED, which it does
+ // not support.
+ private static final int MIN_CONTENT_DIMEN = 5;
+
private boolean mDestroyed;
private AutoScrollHelper mAutoScrollHelper;
@@ -758,6 +763,8 @@
}
completeDragExit();
}
+ } else {
+ mLauncher.getWorkspace().removeExtraEmptyScreen(true, null);
}
mDeleteFolderOnDropCompleted = false;
@@ -785,6 +792,11 @@
}
@Override
+ public float getIntrinsicIconScaleFactor() {
+ return 1f;
+ }
+
+ @Override
public boolean supportsFlingToDelete() {
return true;
}
@@ -959,8 +971,13 @@
int maxContentAreaHeight = grid.availableHeightPx -
workspacePadding.top - workspacePadding.bottom -
mFolderNameHeight;
- return Math.min(maxContentAreaHeight,
+ int height = Math.min(maxContentAreaHeight,
mContent.getDesiredHeight());
+ return Math.max(height, MIN_CONTENT_DIMEN);
+ }
+
+ private int getContentAreaWidth() {
+ return Math.max(mContent.getDesiredWidth(), MIN_CONTENT_DIMEN);
}
private int getFolderHeight() {
@@ -972,11 +989,12 @@
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth();
int height = getFolderHeight();
- int contentAreaWidthSpec = MeasureSpec.makeMeasureSpec(mContent.getDesiredWidth(),
+ int contentAreaWidthSpec = MeasureSpec.makeMeasureSpec(getContentAreaWidth(),
MeasureSpec.EXACTLY);
int contentAreaHeightSpec = MeasureSpec.makeMeasureSpec(getContentAreaHeight(),
MeasureSpec.EXACTLY);
- mContent.setFixedSize(mContent.getDesiredWidth(), mContent.getDesiredHeight());
+
+ mContent.setFixedSize(getContentAreaWidth(), getContentAreaHeight());
mScrollView.measure(contentAreaWidthSpec, contentAreaHeightSpec);
mFolderName.measure(contentAreaWidthSpec,
MeasureSpec.makeMeasureSpec(mFolderNameHeight, MeasureSpec.EXACTLY));
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 835c472..fb75161 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -294,7 +294,7 @@
// Add the new apps to the model and bind them
if (!addShortcuts.isEmpty()) {
LauncherAppState app = LauncherAppState.getInstance();
- app.getModel().addAndBindAddedApps(context, addShortcuts, null);
+ app.getModel().addAndBindAddedApps(context, addShortcuts, new ArrayList<AppInfo>());
}
}
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0637b24..4d802bd 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -45,7 +45,6 @@
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -113,6 +112,7 @@
import java.util.Date;
import java.util.HashMap;
import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Default launcher application.
@@ -181,8 +181,10 @@
private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_span_y";
// Type: parcelable
private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_INFO = "launcher.add_widget_info";
- // Type: parcelable
+ // Type: parcelable
private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_ID = "launcher.add_widget_id";
+ // Type: int[]
+ private static final String RUNTIME_STATE_VIEW_IDS = "launcher.view_ids";
private static final String TOOLBAR_ICON_METADATA_NAME = "com.android.launcher.toolbar_icon";
private static final String TOOLBAR_SEARCH_ICON_METADATA_NAME =
@@ -207,6 +209,9 @@
private static final Object sLock = new Object();
private static int sScreen = DEFAULT_SCREEN;
+ private HashMap<Integer, Integer> mItemIdToViewId = new HashMap<Integer, Integer>();
+ private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
+
// How long to wait before the new-shortcut animation automatically pans the workspace
private static int NEW_APPS_PAGE_MOVE_DELAY = 500;
private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
@@ -454,7 +459,7 @@
} else {
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
- mModel.startLoader(true, mWorkspace.getCurrentPage());
+ mModel.startLoader(true, mWorkspace.getRestorePage());
}
}
@@ -579,12 +584,12 @@
mIconCache.flush();
final LocaleConfiguration localeConfiguration = sLocaleConfiguration;
- new Thread("WriteLocaleConfiguration") {
- @Override
- public void run() {
+ new AsyncTask<Void, Void, Void>() {
+ public Void doInBackground(Void ... args) {
writeConfiguration(Launcher.this, localeConfiguration);
+ return null;
}
- }.start();
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
}
}
@@ -671,6 +676,34 @@
}
/**
+ * Copied from View -- the View version of the method isn't called
+ * anywhere else in our process and only exists for API level 17+,
+ * so it's ok to keep our own version with no API requirement.
+ */
+ public static int generateViewId() {
+ for (;;) {
+ final int result = sNextGeneratedId.get();
+ // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
+ int newValue = result + 1;
+ if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
+ if (sNextGeneratedId.compareAndSet(result, newValue)) {
+ return result;
+ }
+ }
+ }
+
+ public int getViewIdForItem(ItemInfo info) {
+ // This cast is safe given the > 2B range for int.
+ int itemId = (int) info.id;
+ if (mItemIdToViewId.containsKey(itemId)) {
+ return mItemIdToViewId.get(itemId);
+ }
+ int viewId = generateViewId();
+ mItemIdToViewId.put(itemId, viewId);
+ return viewId;
+ }
+
+ /**
* Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
* a configuration step, this allows the proper animations to run after other transitions.
*/
@@ -869,7 +902,7 @@
if (mOnResumeState == State.WORKSPACE) {
showWorkspace(false);
} else if (mOnResumeState == State.APPS_CUSTOMIZE) {
- showAllApps(false, AppsCustomizePagedView.ContentType.Applications, false);
+ showAllApps(false, mAppsCustomizeContent.getContentType(), false);
}
mOnResumeState = State.NONE;
@@ -1108,6 +1141,7 @@
*
* @param savedState The previous state.
*/
+ @SuppressWarnings("unchecked")
private void restoreState(Bundle savedState) {
if (savedState == null) {
return;
@@ -1160,6 +1194,8 @@
int currentIndex = savedState.getInt("apps_customize_currentIndex");
mAppsCustomizeContent.restorePageForIndex(currentIndex);
}
+ mItemIdToViewId = (HashMap<Integer, Integer>)
+ savedState.getSerializable(RUNTIME_STATE_VIEW_IDS);
}
/**
@@ -1191,7 +1227,9 @@
widgetButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
- showAllApps(true, AppsCustomizePagedView.ContentType.Widgets, true);
+ if (!mWorkspace.isSwitchingState()) {
+ showAllApps(true, AppsCustomizePagedView.ContentType.Widgets, true);
+ }
}
});
widgetButton.setOnTouchListener(getHapticFeedbackTouchListener());
@@ -1200,7 +1238,9 @@
wallpaperButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
- startWallpaper();
+ if (!mWorkspace.isSwitchingState()) {
+ startWallpaper();
+ }
}
});
wallpaperButton.setOnTouchListener(getHapticFeedbackTouchListener());
@@ -1209,7 +1249,9 @@
settingsButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
- startSettings();
+ if (!mWorkspace.isSwitchingState()) {
+ startSettings();
+ }
}
});
settingsButton.setOnTouchListener(getHapticFeedbackTouchListener());
@@ -1447,11 +1489,12 @@
if (appWidgetId != -1) {
// Deleting an app widget ID is a void call but writes to disk before returning
// to the caller...
- new Thread("deleteAppWidgetId") {
- public void run() {
+ new AsyncTask<Void, Void, Void>() {
+ public Void doInBackground(Void ... args) {
mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+ return null;
}
- }.start();
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
}
showOutOfSpaceMessage(isHotseatLayout(layout));
return;
@@ -1698,7 +1741,7 @@
// In all these cases, only animate if we're already on home
mWorkspace.exitWidgetResizeMode();
if (alreadyOnHome && mState == State.WORKSPACE && !mWorkspace.isTouchActive() &&
- openFolder == null) {
+ openFolder == null && shouldMoveToDefaultScreenOnHomeIntent()) {
mWorkspace.moveToDefaultScreen(true);
}
@@ -1724,6 +1767,8 @@
if (mAppsCustomizeTabHost != null) {
mAppsCustomizeTabHost.reset();
}
+
+ onHomeIntent();
}
if (DEBUG_RESUME_TIME) {
@@ -1731,6 +1776,21 @@
}
}
+ /**
+ * Override point for subclasses to prevent movement to the default screen when the home
+ * button is pressed. Used (for example) in GEL, to prevent movement during a search.
+ */
+ protected boolean shouldMoveToDefaultScreenOnHomeIntent() {
+ return true;
+ }
+
+ /**
+ * Override point for subclasses to provide custom behaviour for when a home intent is fired.
+ */
+ protected void onHomeIntent() {
+ // Do nothing
+ }
+
@Override
public void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
@@ -1742,7 +1802,8 @@
@Override
protected void onSaveInstanceState(Bundle outState) {
if (mWorkspace.getChildCount() > 0) {
- outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getRestorePage());
+ outState.putInt(RUNTIME_STATE_CURRENT_SCREEN,
+ mWorkspace.getCurrentPageOffsetFromCustomContent());
}
super.onSaveInstanceState(outState);
@@ -1770,13 +1831,15 @@
// Save the current AppsCustomize tab
if (mAppsCustomizeTabHost != null) {
- String currentTabTag = mAppsCustomizeTabHost.getCurrentTabTag();
+ AppsCustomizePagedView.ContentType type = mAppsCustomizeContent.getContentType();
+ String currentTabTag = mAppsCustomizeTabHost.getTabTagForContentType(type);
if (currentTabTag != null) {
outState.putString("apps_customize_currentTab", currentTabTag);
}
int currentIndex = mAppsCustomizeContent.getSaveInstanceStateIndex();
outState.putInt("apps_customize_currentIndex", currentIndex);
}
+ outState.putSerializable(RUNTIME_STATE_VIEW_IDS, mItemIdToViewId);
}
@Override
@@ -1815,7 +1878,7 @@
mDragLayer.clearAllResizeFrames();
((ViewGroup) mWorkspace.getParent()).removeAllViews();
- mWorkspace.removeAllViews();
+ mWorkspace.removeAllWorkspaceScreens();
mWorkspace = null;
mDragController = null;
@@ -2601,7 +2664,7 @@
}
public void closeFolder() {
- Folder folder = mWorkspace.getOpenFolder();
+ Folder folder = mWorkspace != null ? mWorkspace.getOpenFolder() : null;
if (folder != null) {
if (folder.isEditingName()) {
folder.dismissEditingName();
@@ -3650,6 +3713,10 @@
@Override
public void bindAddScreens(ArrayList<Long> orderedScreenIds) {
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - bindAddScreens()", true);
+ Launcher.addDumpLog(TAG, "11683562 - orderedScreenIds: " +
+ TextUtils.join(", ", orderedScreenIds), true);
int count = orderedScreenIds.size();
for (int i = 0; i < count; i++) {
mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(orderedScreenIds.get(i));
@@ -3952,6 +4019,16 @@
return bounceAnim;
}
+ public boolean useVerticalBarLayout() {
+ return LauncherAppState.getInstance().getDynamicGrid().
+ getDeviceProfile().isVerticalBarLayout();
+ }
+
+ protected Rect getSearchBarBounds() {
+ return LauncherAppState.getInstance().getDynamicGrid().
+ getDeviceProfile().getSearchBarBounds();
+ }
+
@Override
public void bindSearchablesChanged() {
boolean searchVisible = updateGlobalSearchIcon();
@@ -4195,13 +4272,14 @@
public void run() {
cling.cleanup();
// We should update the shared preferences on a background thread
- new Thread("dismissClingThread") {
- public void run() {
+ new AsyncTask<Void, Void, Void>() {
+ public Void doInBackground(Void ... args) {
SharedPreferences.Editor editor = mSharedPrefs.edit();
editor.putBoolean(flag, true);
editor.commit();
+ return null;
}
- }.start();
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
if (postAnimationCb != null) {
postAnimationCb.run();
}
@@ -4491,9 +4569,8 @@
public void dumpLogsToLocalData() {
if (DEBUG_DUMP_LOG) {
- new Thread("DumpLogsToLocalData") {
- @Override
- public void run() {
+ new AsyncTask<Void, Void, Void>() {
+ public Void doInBackground(Void ... args) {
boolean success = false;
sDateStamp.setTime(sRunStart);
String FILENAME = sDateStamp.getMonth() + "-"
@@ -4531,8 +4608,9 @@
} catch (IOException e) {
e.printStackTrace();
}
+ return null;
}
- }.start();
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
}
}
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index fe2b43f..9a47eaa 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -191,7 +191,7 @@
availableWidth, availableHeight);
return grid;
}
- DynamicGrid getDynamicGrid() {
+ public DynamicGrid getDynamicGrid() {
return mDynamicGrid;
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index c746b4d..74f28b3 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -40,6 +40,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.provider.BaseColumns;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import com.android.launcher3.InstallWidgetReceiver.WidgetMimeTypeHandlerData;
@@ -282,7 +283,10 @@
addAndBindAddedApps(context, workspaceApps, cb, allAppsApps);
}
public void addAndBindAddedApps(final Context context, final ArrayList<ItemInfo> workspaceApps,
- final Callbacks callbacks, final ArrayList<AppInfo> allAppsApps) {
+ final Callbacks callbacks, final ArrayList<AppInfo> allAppsApps) {
+ if (workspaceApps == null || allAppsApps == null) {
+ throw new RuntimeException("workspaceApps and allAppsApps must not be null");
+ }
if (workspaceApps.isEmpty() && allAppsApps.isEmpty()) {
return;
}
@@ -496,8 +500,7 @@
if (stackTrace != null) {
e.setStackTrace(stackTrace);
}
- // TODO: something breaks this in the upgrade path
- //throw e;
+ throw e;
}
}
@@ -956,6 +959,10 @@
* a list of screen ids in the order that they should appear.
*/
void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - updateWorkspaceScreenOrder()", true);
+ Launcher.addDumpLog(TAG, "11683562 - screens: " + TextUtils.join(", ", screens), true);
+
final ArrayList<Long> screensCopy = new ArrayList<Long>(screens);
final ContentResolver cr = context.getContentResolver();
final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
@@ -1236,6 +1243,15 @@
} finally {
sc.close();
}
+
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - loadWorkspaceScreensDb()", true);
+ ArrayList<String> orderedScreensPairs= new ArrayList<String>();
+ for (Integer i : orderedScreens.keySet()) {
+ orderedScreensPairs.add("{ " + i + ": " + orderedScreens.get(i) + " }");
+ }
+ Launcher.addDumpLog(TAG, "11683562 - screens: " +
+ TextUtils.join(", ", orderedScreensPairs), true);
return orderedScreens;
}
@@ -1509,7 +1525,7 @@
}
if (!added.isEmpty()) {
Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
- addAndBindAddedApps(context, added, cb, null);
+ addAndBindAddedApps(context, added, cb, new ArrayList<AppInfo>());
}
}
@@ -1600,6 +1616,9 @@
/** Returns whether this is an upgradge path */
private boolean loadWorkspace() {
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - loadWorkspace()", true);
+
final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
final Context context = mContext;
@@ -1617,8 +1636,12 @@
LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary(0);
// Check if we need to do any upgrade-path logic
+ // (Includes having just imported default favorites)
boolean loadedOldDb = LauncherAppState.getLauncherProvider().justLoadedOldDb();
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - loadedOldDb: " + loadedOldDb, true);
+
synchronized (sBgLock) {
clearSBgDataStructures();
@@ -1934,6 +1957,10 @@
}
}
Collections.sort(sBgWorkspaceScreens);
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - maxScreenId: " + maxScreenId, true);
+ Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " +
+ TextUtils.join(", ", sBgWorkspaceScreens), true);
LauncherAppState.getLauncherProvider().updateMaxScreenId(maxScreenId);
updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
@@ -1950,6 +1977,9 @@
for (Integer i : orderedScreens.keySet()) {
sBgWorkspaceScreens.add(orderedScreens.get(i));
}
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - sBgWorkspaceScreens: " +
+ TextUtils.join(", ", sBgWorkspaceScreens), true);
// Remove any empty screens
ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens);
@@ -1963,6 +1993,10 @@
// If there are any empty screens remove them, and update.
if (unusedScreens.size() != 0) {
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - unusedScreens (to be removed): " +
+ TextUtils.join(", ", unusedScreens), true);
+
sBgWorkspaceScreens.removeAll(unusedScreens);
updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
}
@@ -1994,7 +2028,7 @@
/** Filters the set of items who are directly or indirectly (via another container) on the
* specified screen. */
- private void filterCurrentWorkspaceItems(int currentScreen,
+ private void filterCurrentWorkspaceItems(long currentScreenId,
ArrayList<ItemInfo> allWorkspaceItems,
ArrayList<ItemInfo> currentScreenItems,
ArrayList<ItemInfo> otherScreenItems) {
@@ -2007,12 +2041,6 @@
}
}
- // If we aren't filtering on a screen, then the set of items to load is the full set of
- // items given.
- if (currentScreen < 0) {
- currentScreenItems.addAll(allWorkspaceItems);
- }
-
// Order the set of items by their containers first, this allows use to walk through the
// list sequentially, build up a list of containers that are in the specified screen,
// as well as all items in those containers.
@@ -2025,7 +2053,7 @@
});
for (ItemInfo info : allWorkspaceItems) {
if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- if (info.screenId == currentScreen) {
+ if (info.screenId == currentScreenId) {
currentScreenItems.add(info);
itemsOnScreen.add(info.id);
} else {
@@ -2046,20 +2074,15 @@
}
/** Filters the set of widgets which are on the specified screen. */
- private void filterCurrentAppWidgets(int currentScreen,
+ private void filterCurrentAppWidgets(long currentScreenId,
ArrayList<LauncherAppWidgetInfo> appWidgets,
ArrayList<LauncherAppWidgetInfo> currentScreenWidgets,
ArrayList<LauncherAppWidgetInfo> otherScreenWidgets) {
- // If we aren't filtering on a screen, then the set of items to load is the full set of
- // widgets given.
- if (currentScreen < 0) {
- currentScreenWidgets.addAll(appWidgets);
- }
for (LauncherAppWidgetInfo widget : appWidgets) {
if (widget == null) continue;
if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
- widget.screenId == currentScreen) {
+ widget.screenId == currentScreenId) {
currentScreenWidgets.add(widget);
} else {
otherScreenWidgets.add(widget);
@@ -2068,23 +2091,18 @@
}
/** Filters the set of folders which are on the specified screen. */
- private void filterCurrentFolders(int currentScreen,
+ private void filterCurrentFolders(long currentScreenId,
HashMap<Long, ItemInfo> itemsIdMap,
HashMap<Long, FolderInfo> folders,
HashMap<Long, FolderInfo> currentScreenFolders,
HashMap<Long, FolderInfo> otherScreenFolders) {
- // If we aren't filtering on a screen, then the set of items to load is the full set of
- // widgets given.
- if (currentScreen < 0) {
- currentScreenFolders.putAll(folders);
- }
for (long id : folders.keySet()) {
ItemInfo info = itemsIdMap.get(id);
FolderInfo folder = folders.get(id);
if (info == null || folder == null) continue;
if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
- info.screenId == currentScreen) {
+ info.screenId == currentScreenId) {
currentScreenFolders.put(id, folder);
} else {
otherScreenFolders.put(id, folder);
@@ -2211,13 +2229,7 @@
return;
}
- final boolean isLoadingSynchronously = (synchronizeBindPage > -1);
- final int currentScreen = isLoadingSynchronously ? synchronizeBindPage :
- oldCallbacks.getCurrentWorkspaceScreen();
-
- // Load all the items that are on the current page first (and in the process, unbind
- // all the existing workspace items before we call startBinding() below.
- unbindWorkspaceItemsOnMainThread();
+ // Save a copy of all the bg-thread collections
ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
ArrayList<LauncherAppWidgetInfo> appWidgets =
new ArrayList<LauncherAppWidgetInfo>();
@@ -2232,6 +2244,22 @@
orderedScreenIds.addAll(sBgWorkspaceScreens);
}
+ final boolean isLoadingSynchronously = (synchronizeBindPage > -1);
+ int currScreen = isLoadingSynchronously ? synchronizeBindPage :
+ oldCallbacks.getCurrentWorkspaceScreen();
+ if (currScreen >= orderedScreenIds.size()) {
+ // There may be no workspace screens (just hotseat items and an empty page).
+ currScreen = -1;
+ }
+ final int currentScreen = currScreen;
+ final long currentScreenId =
+ currentScreen < 0 ? -1 : orderedScreenIds.get(currentScreen);
+
+ // Load all the items that are on the current page first (and in the process, unbind
+ // all the existing workspace items before we call startBinding() below.
+ unbindWorkspaceItemsOnMainThread();
+
+ // Separate the items that are on the current screen, and all the other remaining items
ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>();
ArrayList<LauncherAppWidgetInfo> currentAppWidgets =
@@ -2241,12 +2269,11 @@
HashMap<Long, FolderInfo> currentFolders = new HashMap<Long, FolderInfo>();
HashMap<Long, FolderInfo> otherFolders = new HashMap<Long, FolderInfo>();
- // Separate the items that are on the current screen, and all the other remaining items
- filterCurrentWorkspaceItems(currentScreen, workspaceItems, currentWorkspaceItems,
+ filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
otherWorkspaceItems);
- filterCurrentAppWidgets(currentScreen, appWidgets, currentAppWidgets,
+ filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
otherAppWidgets);
- filterCurrentFolders(currentScreen, itemsIdMap, folders, currentFolders,
+ filterCurrentFolders(currentScreenId, itemsIdMap, folders, currentFolders,
otherFolders);
sortWorkspaceItemsSpatially(currentWorkspaceItems);
sortWorkspaceItemsSpatially(otherWorkspaceItems);
@@ -2271,7 +2298,7 @@
r = new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
+ if (callbacks != null && currentScreen >= 0) {
callbacks.onPageBoundSynchronously(currentScreen);
}
}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index e992706..27b7dae 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -71,6 +71,9 @@
static final String OLD_AUTHORITY = "com.android.launcher2.settings";
static final String AUTHORITY = ProviderConfig.AUTHORITY;
+ // Should we attempt to load anything from the com.android.launcher2 provider?
+ static final boolean IMPORT_LAUNCHER2_DATABASE = true;
+
static final String TABLE_FAVORITES = "favorites";
static final String TABLE_WORKSPACE_SCREENS = "workspaceScreens";
static final String PARAMETER_NOTIFY = "notify";
@@ -287,6 +290,13 @@
public void onRow(ContentValues values);
}
+ private static boolean shouldImportLauncher2Database(Context context) {
+ boolean isTablet = context.getResources().getBoolean(R.bool.is_tablet);
+
+ // We don't import the old databse for tablets, as the grid size has changed.
+ return !isTablet && IMPORT_LAUNCHER2_DATABASE;
+ }
+
private static class DatabaseHelper extends SQLiteOpenHelper {
private static final String TAG_FAVORITES = "favorites";
private static final String TAG_FAVORITE = "favorite";
@@ -366,32 +376,38 @@
sendAppWidgetResetNotify();
}
- // Try converting the old database
- ContentValuesCallback permuteScreensCb = new ContentValuesCallback() {
- public void onRow(ContentValues values) {
- int container = values.getAsInteger(LauncherSettings.Favorites.CONTAINER);
- if (container == Favorites.CONTAINER_DESKTOP) {
- int screen = values.getAsInteger(LauncherSettings.Favorites.SCREEN);
- screen = (int) upgradeLauncherDb_permuteScreens(screen);
- values.put(LauncherSettings.Favorites.SCREEN, screen);
+ if (shouldImportLauncher2Database(mContext)) {
+ // Try converting the old database
+ ContentValuesCallback permuteScreensCb = new ContentValuesCallback() {
+ public void onRow(ContentValues values) {
+ int container = values.getAsInteger(LauncherSettings.Favorites.CONTAINER);
+ if (container == Favorites.CONTAINER_DESKTOP) {
+ int screen = values.getAsInteger(LauncherSettings.Favorites.SCREEN);
+ screen = (int) upgradeLauncherDb_permuteScreens(screen);
+ values.put(LauncherSettings.Favorites.SCREEN, screen);
+ }
+ }
+ };
+ Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
+ "/old_favorites?notify=true");
+ if (!convertDatabase(db, uri, permuteScreensCb, true)) {
+ // Try and upgrade from the Launcher2 db
+ uri = LauncherSettings.Favorites.OLD_CONTENT_URI;
+ if (!convertDatabase(db, uri, permuteScreensCb, false)) {
+ // If we fail, then set a flag to load the default workspace
+ setFlagEmptyDbCreated();
+ return;
}
}
- };
- Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
- "/old_favorites?notify=true");
- if (!convertDatabase(db, uri, permuteScreensCb, true)) {
- // Try and upgrade from the Launcher2 db
- uri = LauncherSettings.Favorites.OLD_CONTENT_URI;
- if (!convertDatabase(db, uri, permuteScreensCb, false)) {
- // If we fail, then set a flag to load the default workspace
- setFlagEmptyDbCreated();
- return;
- }
+ // Right now, in non-default workspace cases, we want to run the final
+ // upgrade code (ie. to fix workspace screen indices -> ids, etc.), so
+ // set that flag too.
+ setFlagJustLoadedOldDb();
+ } else {
+ // Fresh and clean launcher DB.
+ mMaxItemId = initializeMaxItemId(db);
+ setFlagEmptyDbCreated();
}
- // Right now, in non-default workspace cases, we want to run the final
- // upgrade code (ie. to fix workspace screen indices -> ids, etc.), so
- // set that flag too.
- setFlagJustLoadedOldDb();
}
private void addWorkspacesTable(SQLiteDatabase db) {
@@ -868,10 +884,14 @@
throw new RuntimeException("Error: max screen id was not initialized");
}
mMaxScreenId += 1;
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - generateNewScreenId(): " + mMaxScreenId, true);
return mMaxScreenId;
}
public void updateMaxScreenId(long maxScreenId) {
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - updateMaxScreenId(): " + maxScreenId, true);
mMaxScreenId = maxScreenId;
}
@@ -892,6 +912,8 @@
throw new RuntimeException("Error: could not query max screen id");
}
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - initializeMaxScreenId(): " + id, true);
return id;
}
diff --git a/src/com/android/launcher3/LauncherScroller.java b/src/com/android/launcher3/LauncherScroller.java
new file mode 100644
index 0000000..3bd0a78
--- /dev/null
+++ b/src/com/android/launcher3/LauncherScroller.java
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2006 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.launcher3;
+
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.hardware.SensorManager;
+import android.os.Build;
+import android.util.FloatMath;
+import android.view.ViewConfiguration;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+/**
+ * This class differs from the framework {@link android.widget.Scroller} in that
+ * you can modify the Interpolator post-construction.
+ */
+public class LauncherScroller {
+ private int mMode;
+
+ private int mStartX;
+ private int mStartY;
+ private int mFinalX;
+ private int mFinalY;
+
+ private int mMinX;
+ private int mMaxX;
+ private int mMinY;
+ private int mMaxY;
+
+ private int mCurrX;
+ private int mCurrY;
+ private long mStartTime;
+ private int mDuration;
+ private float mDurationReciprocal;
+ private float mDeltaX;
+ private float mDeltaY;
+ private boolean mFinished;
+ private TimeInterpolator mInterpolator;
+ private boolean mFlywheel;
+
+ private float mVelocity;
+ private float mCurrVelocity;
+ private int mDistance;
+
+ private float mFlingFriction = ViewConfiguration.getScrollFriction();
+
+ private static final int DEFAULT_DURATION = 250;
+ private static final int SCROLL_MODE = 0;
+ private static final int FLING_MODE = 1;
+
+ private static float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9));
+ private static final float INFLEXION = 0.35f; // Tension lines cross at (INFLEXION, 1)
+ private static final float START_TENSION = 0.5f;
+ private static final float END_TENSION = 1.0f;
+ private static final float P1 = START_TENSION * INFLEXION;
+ private static final float P2 = 1.0f - END_TENSION * (1.0f - INFLEXION);
+
+ private static final int NB_SAMPLES = 100;
+ private static final float[] SPLINE_POSITION = new float[NB_SAMPLES + 1];
+ private static final float[] SPLINE_TIME = new float[NB_SAMPLES + 1];
+
+ private float mDeceleration;
+ private final float mPpi;
+
+ // A context-specific coefficient adjusted to physical values.
+ private float mPhysicalCoeff;
+
+ static {
+ float x_min = 0.0f;
+ float y_min = 0.0f;
+ for (int i = 0; i < NB_SAMPLES; i++) {
+ final float alpha = (float) i / NB_SAMPLES;
+
+ float x_max = 1.0f;
+ float x, tx, coef;
+ while (true) {
+ x = x_min + (x_max - x_min) / 2.0f;
+ coef = 3.0f * x * (1.0f - x);
+ tx = coef * ((1.0f - x) * P1 + x * P2) + x * x * x;
+ if (Math.abs(tx - alpha) < 1E-5) break;
+ if (tx > alpha) x_max = x;
+ else x_min = x;
+ }
+ SPLINE_POSITION[i] = coef * ((1.0f - x) * START_TENSION + x) + x * x * x;
+
+ float y_max = 1.0f;
+ float y, dy;
+ while (true) {
+ y = y_min + (y_max - y_min) / 2.0f;
+ coef = 3.0f * y * (1.0f - y);
+ dy = coef * ((1.0f - y) * START_TENSION + y) + y * y * y;
+ if (Math.abs(dy - alpha) < 1E-5) break;
+ if (dy > alpha) y_max = y;
+ else y_min = y;
+ }
+ SPLINE_TIME[i] = coef * ((1.0f - y) * P1 + y * P2) + y * y * y;
+ }
+ SPLINE_POSITION[NB_SAMPLES] = SPLINE_TIME[NB_SAMPLES] = 1.0f;
+
+ // This controls the viscous fluid effect (how much of it)
+ sViscousFluidScale = 8.0f;
+ // must be set to 1.0 (used in viscousFluid())
+ sViscousFluidNormalize = 1.0f;
+ sViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
+
+ }
+
+ private static float sViscousFluidScale;
+ private static float sViscousFluidNormalize;
+
+ public void setInterpolator(TimeInterpolator interpolator) {
+ mInterpolator = interpolator;
+ }
+
+ /**
+ * Create a Scroller with the default duration and interpolator.
+ */
+ public LauncherScroller(Context context) {
+ this(context, null);
+ }
+
+ /**
+ * Create a Scroller with the specified interpolator. If the interpolator is
+ * null, the default (viscous) interpolator will be used. "Flywheel" behavior will
+ * be in effect for apps targeting Honeycomb or newer.
+ */
+ public LauncherScroller(Context context, Interpolator interpolator) {
+ this(context, interpolator,
+ context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
+ }
+
+ /**
+ * Create a Scroller with the specified interpolator. If the interpolator is
+ * null, the default (viscous) interpolator will be used. Specify whether or
+ * not to support progressive "flywheel" behavior in flinging.
+ */
+ public LauncherScroller(Context context, Interpolator interpolator, boolean flywheel) {
+ mFinished = true;
+ mInterpolator = interpolator;
+ mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
+ mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
+ mFlywheel = flywheel;
+
+ mPhysicalCoeff = computeDeceleration(0.84f); // look and feel tuning
+ }
+
+ /**
+ * The amount of friction applied to flings. The default value
+ * is {@link ViewConfiguration#getScrollFriction}.
+ *
+ * @param friction A scalar dimension-less value representing the coefficient of
+ * friction.
+ */
+ public final void setFriction(float friction) {
+ mDeceleration = computeDeceleration(friction);
+ mFlingFriction = friction;
+ }
+
+ private float computeDeceleration(float friction) {
+ return SensorManager.GRAVITY_EARTH // g (m/s^2)
+ * 39.37f // inch/meter
+ * mPpi // pixels per inch
+ * friction;
+ }
+
+ /**
+ *
+ * Returns whether the scroller has finished scrolling.
+ *
+ * @return True if the scroller has finished scrolling, false otherwise.
+ */
+ public final boolean isFinished() {
+ return mFinished;
+ }
+
+ /**
+ * Force the finished field to a particular value.
+ *
+ * @param finished The new finished value.
+ */
+ public final void forceFinished(boolean finished) {
+ mFinished = finished;
+ }
+
+ /**
+ * Returns how long the scroll event will take, in milliseconds.
+ *
+ * @return The duration of the scroll in milliseconds.
+ */
+ public final int getDuration() {
+ return mDuration;
+ }
+
+ /**
+ * Returns the current X offset in the scroll.
+ *
+ * @return The new X offset as an absolute distance from the origin.
+ */
+ public final int getCurrX() {
+ return mCurrX;
+ }
+
+ /**
+ * Returns the current Y offset in the scroll.
+ *
+ * @return The new Y offset as an absolute distance from the origin.
+ */
+ public final int getCurrY() {
+ return mCurrY;
+ }
+
+ /**
+ * Returns the current velocity.
+ *
+ * @return The original velocity less the deceleration. Result may be
+ * negative.
+ */
+ public float getCurrVelocity() {
+ return mMode == FLING_MODE ?
+ mCurrVelocity : mVelocity - mDeceleration * timePassed() / 2000.0f;
+ }
+
+ /**
+ * Returns the start X offset in the scroll.
+ *
+ * @return The start X offset as an absolute distance from the origin.
+ */
+ public final int getStartX() {
+ return mStartX;
+ }
+
+ /**
+ * Returns the start Y offset in the scroll.
+ *
+ * @return The start Y offset as an absolute distance from the origin.
+ */
+ public final int getStartY() {
+ return mStartY;
+ }
+
+ /**
+ * Returns where the scroll will end. Valid only for "fling" scrolls.
+ *
+ * @return The final X offset as an absolute distance from the origin.
+ */
+ public final int getFinalX() {
+ return mFinalX;
+ }
+
+ /**
+ * Returns where the scroll will end. Valid only for "fling" scrolls.
+ *
+ * @return The final Y offset as an absolute distance from the origin.
+ */
+ public final int getFinalY() {
+ return mFinalY;
+ }
+
+ /**
+ * Call this when you want to know the new location. If it returns true,
+ * the animation is not yet finished.
+ */
+ public boolean computeScrollOffset() {
+ if (mFinished) {
+ return false;
+ }
+
+ int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
+
+ if (timePassed < mDuration) {
+ switch (mMode) {
+ case SCROLL_MODE:
+ float x = timePassed * mDurationReciprocal;
+
+ if (mInterpolator == null)
+ x = viscousFluid(x);
+ else
+ x = mInterpolator.getInterpolation(x);
+
+ mCurrX = mStartX + Math.round(x * mDeltaX);
+ mCurrY = mStartY + Math.round(x * mDeltaY);
+ break;
+ case FLING_MODE:
+ final float t = (float) timePassed / mDuration;
+ final int index = (int) (NB_SAMPLES * t);
+ float distanceCoef = 1.f;
+ float velocityCoef = 0.f;
+ if (index < NB_SAMPLES) {
+ final float t_inf = (float) index / NB_SAMPLES;
+ final float t_sup = (float) (index + 1) / NB_SAMPLES;
+ final float d_inf = SPLINE_POSITION[index];
+ final float d_sup = SPLINE_POSITION[index + 1];
+ velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
+ distanceCoef = d_inf + (t - t_inf) * velocityCoef;
+ }
+
+ mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;
+
+ mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
+ // Pin to mMinX <= mCurrX <= mMaxX
+ mCurrX = Math.min(mCurrX, mMaxX);
+ mCurrX = Math.max(mCurrX, mMinX);
+
+ mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
+ // Pin to mMinY <= mCurrY <= mMaxY
+ mCurrY = Math.min(mCurrY, mMaxY);
+ mCurrY = Math.max(mCurrY, mMinY);
+
+ if (mCurrX == mFinalX && mCurrY == mFinalY) {
+ mFinished = true;
+ }
+
+ break;
+ }
+ }
+ else {
+ mCurrX = mFinalX;
+ mCurrY = mFinalY;
+ mFinished = true;
+ }
+ return true;
+ }
+
+ /**
+ * Start scrolling by providing a starting point and the distance to travel.
+ * The scroll will use the default value of 250 milliseconds for the
+ * duration.
+ *
+ * @param startX Starting horizontal scroll offset in pixels. Positive
+ * numbers will scroll the content to the left.
+ * @param startY Starting vertical scroll offset in pixels. Positive numbers
+ * will scroll the content up.
+ * @param dx Horizontal distance to travel. Positive numbers will scroll the
+ * content to the left.
+ * @param dy Vertical distance to travel. Positive numbers will scroll the
+ * content up.
+ */
+ public void startScroll(int startX, int startY, int dx, int dy) {
+ startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
+ }
+
+ /**
+ * Start scrolling by providing a starting point, the distance to travel,
+ * and the duration of the scroll.
+ *
+ * @param startX Starting horizontal scroll offset in pixels. Positive
+ * numbers will scroll the content to the left.
+ * @param startY Starting vertical scroll offset in pixels. Positive numbers
+ * will scroll the content up.
+ * @param dx Horizontal distance to travel. Positive numbers will scroll the
+ * content to the left.
+ * @param dy Vertical distance to travel. Positive numbers will scroll the
+ * content up.
+ * @param duration Duration of the scroll in milliseconds.
+ */
+ public void startScroll(int startX, int startY, int dx, int dy, int duration) {
+ mMode = SCROLL_MODE;
+ mFinished = false;
+ mDuration = duration;
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mStartX = startX;
+ mStartY = startY;
+ mFinalX = startX + dx;
+ mFinalY = startY + dy;
+ mDeltaX = dx;
+ mDeltaY = dy;
+ mDurationReciprocal = 1.0f / (float) mDuration;
+ }
+
+ /**
+ * Start scrolling based on a fling gesture. The distance travelled will
+ * depend on the initial velocity of the fling.
+ *
+ * @param startX Starting point of the scroll (X)
+ * @param startY Starting point of the scroll (Y)
+ * @param velocityX Initial velocity of the fling (X) measured in pixels per
+ * second.
+ * @param velocityY Initial velocity of the fling (Y) measured in pixels per
+ * second
+ * @param minX Minimum X value. The scroller will not scroll past this
+ * point.
+ * @param maxX Maximum X value. The scroller will not scroll past this
+ * point.
+ * @param minY Minimum Y value. The scroller will not scroll past this
+ * point.
+ * @param maxY Maximum Y value. The scroller will not scroll past this
+ * point.
+ */
+ public void fling(int startX, int startY, int velocityX, int velocityY,
+ int minX, int maxX, int minY, int maxY) {
+ // Continue a scroll or fling in progress
+ if (mFlywheel && !mFinished) {
+ float oldVel = getCurrVelocity();
+
+ float dx = (float) (mFinalX - mStartX);
+ float dy = (float) (mFinalY - mStartY);
+ float hyp = FloatMath.sqrt(dx * dx + dy * dy);
+
+ float ndx = dx / hyp;
+ float ndy = dy / hyp;
+
+ float oldVelocityX = ndx * oldVel;
+ float oldVelocityY = ndy * oldVel;
+ if (Math.signum(velocityX) == Math.signum(oldVelocityX) &&
+ Math.signum(velocityY) == Math.signum(oldVelocityY)) {
+ velocityX += oldVelocityX;
+ velocityY += oldVelocityY;
+ }
+ }
+
+ mMode = FLING_MODE;
+ mFinished = false;
+
+ float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY);
+
+ mVelocity = velocity;
+ mDuration = getSplineFlingDuration(velocity);
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mStartX = startX;
+ mStartY = startY;
+
+ float coeffX = velocity == 0 ? 1.0f : velocityX / velocity;
+ float coeffY = velocity == 0 ? 1.0f : velocityY / velocity;
+
+ double totalDistance = getSplineFlingDistance(velocity);
+ mDistance = (int) (totalDistance * Math.signum(velocity));
+
+ mMinX = minX;
+ mMaxX = maxX;
+ mMinY = minY;
+ mMaxY = maxY;
+
+ mFinalX = startX + (int) Math.round(totalDistance * coeffX);
+ // Pin to mMinX <= mFinalX <= mMaxX
+ mFinalX = Math.min(mFinalX, mMaxX);
+ mFinalX = Math.max(mFinalX, mMinX);
+
+ mFinalY = startY + (int) Math.round(totalDistance * coeffY);
+ // Pin to mMinY <= mFinalY <= mMaxY
+ mFinalY = Math.min(mFinalY, mMaxY);
+ mFinalY = Math.max(mFinalY, mMinY);
+ }
+
+ private double getSplineDeceleration(float velocity) {
+ return Math.log(INFLEXION * Math.abs(velocity) / (mFlingFriction * mPhysicalCoeff));
+ }
+
+ private int getSplineFlingDuration(float velocity) {
+ final double l = getSplineDeceleration(velocity);
+ final double decelMinusOne = DECELERATION_RATE - 1.0;
+ return (int) (1000.0 * Math.exp(l / decelMinusOne));
+ }
+
+ private double getSplineFlingDistance(float velocity) {
+ final double l = getSplineDeceleration(velocity);
+ final double decelMinusOne = DECELERATION_RATE - 1.0;
+ return mFlingFriction * mPhysicalCoeff * Math.exp(DECELERATION_RATE / decelMinusOne * l);
+ }
+
+ static float viscousFluid(float x)
+ {
+ x *= sViscousFluidScale;
+ if (x < 1.0f) {
+ x -= (1.0f - (float)Math.exp(-x));
+ } else {
+ float start = 0.36787944117f; // 1/e == exp(-1)
+ x = 1.0f - (float)Math.exp(1.0f - x);
+ x = start + x * (1.0f - start);
+ }
+ x *= sViscousFluidNormalize;
+ return x;
+ }
+
+ /**
+ * Stops the animation. Contrary to {@link #forceFinished(boolean)},
+ * aborting the animating cause the scroller to move to the final x and y
+ * position
+ *
+ * @see #forceFinished(boolean)
+ */
+ public void abortAnimation() {
+ mCurrX = mFinalX;
+ mCurrY = mFinalY;
+ mFinished = true;
+ }
+
+ /**
+ * Extend the scroll animation. This allows a running animation to scroll
+ * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
+ *
+ * @param extend Additional time to scroll in milliseconds.
+ * @see #setFinalX(int)
+ * @see #setFinalY(int)
+ */
+ public void extendDuration(int extend) {
+ int passed = timePassed();
+ mDuration = passed + extend;
+ mDurationReciprocal = 1.0f / mDuration;
+ mFinished = false;
+ }
+
+ /**
+ * Returns the time elapsed since the beginning of the scrolling.
+ *
+ * @return The elapsed time in milliseconds.
+ */
+ public int timePassed() {
+ return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
+ }
+
+ /**
+ * Sets the final position (X) for this scroller.
+ *
+ * @param newX The new X offset as an absolute distance from the origin.
+ * @see #extendDuration(int)
+ * @see #setFinalY(int)
+ */
+ public void setFinalX(int newX) {
+ mFinalX = newX;
+ mDeltaX = mFinalX - mStartX;
+ mFinished = false;
+ }
+
+ /**
+ * Sets the final position (Y) for this scroller.
+ *
+ * @param newY The new Y offset as an absolute distance from the origin.
+ * @see #extendDuration(int)
+ * @see #setFinalX(int)
+ */
+ public void setFinalY(int newY) {
+ mFinalY = newY;
+ mDeltaY = mFinalY - mStartY;
+ mFinished = false;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isScrollingInDirection(float xvel, float yvel) {
+ return !mFinished && Math.signum(xvel) == Math.signum(mFinalX - mStartX) &&
+ Math.signum(yvel) == Math.signum(mFinalY - mStartY);
+ }
+}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 3e2ec58..c216f92 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -122,7 +122,8 @@
protected int mNextPage = INVALID_PAGE;
protected int mMaxScrollX;
- protected Scroller mScroller;
+ protected LauncherScroller mScroller;
+ private Interpolator mDefaultInterpolator;
private VelocityTracker mVelocityTracker;
private int mPageSpacing = 0;
@@ -152,7 +153,6 @@
protected int mTouchState = TOUCH_STATE_REST;
protected boolean mForceScreenScrolled = false;
- private boolean mScrollAbortedFromIntercept = false;
protected OnLongClickListener mLongClickListener;
@@ -310,7 +310,8 @@
protected void init() {
mDirtyPageContent = new ArrayList<Boolean>();
mDirtyPageContent.ensureCapacity(32);
- mScroller = new Scroller(getContext(), new ScrollInterpolator());
+ mScroller = new LauncherScroller(getContext());
+ setDefaultInterpolator(new ScrollInterpolator());
mCurrentPage = 0;
mCenterPagesVertically = true;
@@ -330,6 +331,11 @@
setOnHierarchyChangeListener(this);
}
+ protected void setDefaultInterpolator(Interpolator interpolator) {
+ mDefaultInterpolator = interpolator;
+ mScroller.setInterpolator(mDefaultInterpolator);
+ }
+
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -564,6 +570,9 @@
void setRestorePage(int restorePage) {
mRestorePage = restorePage;
}
+ int getRestorePage() {
+ return mRestorePage;
+ }
protected void notifyPageSwitchListener() {
if (mPageSwitchListener != null) {
@@ -786,14 +795,14 @@
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
- // NOTE: We multiply by 1.5f to account for the fact that depending on the offset of the
+ // NOTE: We multiply by 2f to account for the fact that depending on the offset of the
// viewport, we can be at most one and a half screens offset once we scale down
DisplayMetrics dm = getResources().getDisplayMetrics();
int maxSize = Math.max(dm.widthPixels + mInsets.left + mInsets.right,
dm.heightPixels + mInsets.top + mInsets.bottom);
- int parentWidthSize = (int) (1.5f * maxSize);
- int parentHeightSize = maxSize;
+ int parentWidthSize = (int) (2f * maxSize);
+ int parentHeightSize = (int) (2f * maxSize);
int scaledWidthSize, scaledHeightSize;
if (mUseMinScale) {
scaledWidthSize = (int) (parentWidthSize / mMinScale);
@@ -902,6 +911,7 @@
int verticalPadding = getPaddingTop() + getPaddingBottom();
LayoutParams lp = (LayoutParams) getChildAt(startIndex).getLayoutParams();
+ LayoutParams nextLp;
int childLeft = offsetX + (lp.isFullScreenPage ? 0 : getPaddingLeft());
if (mPageScrolls == null || getChildCount() != mChildCountOnLastLayout) {
@@ -931,7 +941,24 @@
int scrollOffsetLeft = lp.isFullScreenPage ? 0 : getPaddingLeft();
mPageScrolls[i] = childLeft - scrollOffsetLeft - offsetX;
- childLeft += childWidth + mPageSpacing;
+
+ int pageGap = mPageSpacing;
+ int next = i + delta;
+ if (next != endIndex) {
+ nextLp = (LayoutParams) getPageAt(next).getLayoutParams();
+ } else {
+ nextLp = null;
+ }
+
+ // Prevent full screen pages from showing in the viewport
+ // when they are not the current page.
+ if (lp.isFullScreenPage) {
+ pageGap = getPaddingLeft();
+ } else if (nextLp != null && nextLp.isFullScreenPage) {
+ pageGap = getPaddingRight();
+ }
+
+ childLeft += childWidth + pageGap;
}
}
@@ -1367,13 +1394,13 @@
* being flinged.
*/
final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
- final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop);
+ final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop / 3);
if (finishedScrolling) {
mTouchState = TOUCH_STATE_REST;
- if (!mScroller.isFinished()) {
- mScrollAbortedFromIntercept = true;
- abortScrollerAnimation(false);
+ if (!mScroller.isFinished() && !mFreeScroll) {
+ setCurrentPage(getNextPage());
+ pageEndMoving();
}
} else {
if (isTouchPointInViewportWithBuffer((int) mDownMotionX, (int) mDownMotionY)) {
@@ -1401,9 +1428,6 @@
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- if (mScrollAbortedFromIntercept) {
- snapToDestination();
- }
resetTouchState();
break;
@@ -1607,11 +1631,11 @@
}
protected void enableFreeScroll() {
- setEnableFreeScroll(true, -1);
+ setEnableFreeScroll(true);
}
- protected void disableFreeScroll(int snapPage) {
- setEnableFreeScroll(false, snapPage);
+ protected void disableFreeScroll() {
+ setEnableFreeScroll(false);
}
void updateFreescrollBounds() {
@@ -1625,16 +1649,10 @@
}
}
- private void setEnableFreeScroll(boolean freeScroll, int snapPage) {
+ private void setEnableFreeScroll(boolean freeScroll) {
mFreeScroll = freeScroll;
- if (snapPage == -1) {
- snapPage = getPageNearestToCenterOfScreen();
- }
-
- if (!mFreeScroll) {
- snapToPage(snapPage);
- } else {
+ if (mFreeScroll) {
updateFreescrollBounds();
getOverviewModePages(mTempVisiblePagesRange);
if (getCurrentPage() < mTempVisiblePagesRange[0]) {
@@ -1820,7 +1838,9 @@
addView(mDragView, pageUnderPointIndex);
onAddView(mDragView, pageUnderPointIndex);
mSidePageHoverIndex = -1;
- mPageIndicator.setActiveMarker(getNextPage());
+ if (mPageIndicator != null) {
+ mPageIndicator.setActiveMarker(getNextPage());
+ }
}
};
postDelayed(mSidePageHoverRunnable, REORDERING_SIDE_PAGE_HOVER_TIMEOUT);
@@ -1890,6 +1910,7 @@
int vX = (int) (-velocityX * scaleX);
int initialScrollX = (int) (getScrollX() * scaleX);
+ mScroller.setInterpolator(mDefaultInterpolator);
mScroller.fling(initialScrollX,
getScrollY(), vX, 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0);
invalidate();
@@ -1975,7 +1996,6 @@
releaseVelocityTracker();
endReordering();
mCancelTap = false;
- mScrollAbortedFromIntercept = false;
mTouchState = TOUCH_STATE_REST;
mActivePointerId = INVALID_POINTER;
}
@@ -2156,27 +2176,33 @@
}
protected void snapToPageImmediately(int whichPage) {
- snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true);
+ snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true, null);
}
protected void snapToPage(int whichPage, int duration) {
- snapToPage(whichPage, duration, false);
+ snapToPage(whichPage, duration, false, null);
}
- protected void snapToPage(int whichPage, int duration, boolean immediate) {
+ protected void snapToPage(int whichPage, int duration, TimeInterpolator interpolator) {
+ snapToPage(whichPage, duration, false, interpolator);
+ }
+
+ protected void snapToPage(int whichPage, int duration, boolean immediate,
+ TimeInterpolator interpolator) {
whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
int newX = getScrollForPage(whichPage);
final int sX = mUnboundedScrollX;
final int delta = newX - sX;
- snapToPage(whichPage, delta, duration, immediate);
+ snapToPage(whichPage, delta, duration, immediate, interpolator);
}
protected void snapToPage(int whichPage, int delta, int duration) {
- snapToPage(whichPage, delta, duration, false);
+ snapToPage(whichPage, delta, duration, false, null);
}
- protected void snapToPage(int whichPage, int delta, int duration, boolean immediate) {
+ protected void snapToPage(int whichPage, int delta, int duration, boolean immediate,
+ TimeInterpolator interpolator) {
mNextPage = whichPage;
View focusedChild = getFocusedChild();
if (focusedChild != null && whichPage != mCurrentPage &&
@@ -2195,8 +2221,15 @@
}
if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
+ abortScrollerAnimation(false);
}
+
+ if (interpolator != null) {
+ mScroller.setInterpolator(interpolator);
+ } else {
+ mScroller.setInterpolator(mDefaultInterpolator);
+ }
+
mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration);
notifyPageSwitchListener();
@@ -2452,7 +2485,8 @@
mDragView = getChildAt(dragViewIndex);
mDragView.animate().scaleX(1.15f).scaleY(1.15f).setDuration(100).start();
mDragViewBaselineLeft = mDragView.getLeft();
- disableFreeScroll(-1);
+ snapToPage(getPageNearestToCenterOfScreen());
+ disableFreeScroll();
onStartReordering();
return true;
}
diff --git a/src/com/android/launcher3/PreloadReceiver.java b/src/com/android/launcher3/PreloadReceiver.java
index 75e5c98..ca25746 100644
--- a/src/com/android/launcher3/PreloadReceiver.java
+++ b/src/com/android/launcher3/PreloadReceiver.java
@@ -19,6 +19,7 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.Log;
@@ -39,12 +40,12 @@
if (LOGD) {
Log.d(TAG, "workspace name: " + name + " id: " + workspaceResId);
}
- new Thread(new Runnable() {
- @Override
- public void run() {
+ new AsyncTask<Void, Void, Void>() {
+ public Void doInBackground(Void ... args) {
provider.loadDefaultFavoritesIfNecessary(workspaceResId);
+ return null;
}
- }).start();
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
}
}
}
diff --git a/src/com/android/launcher3/RampUpScroller.java b/src/com/android/launcher3/RampUpScroller.java
deleted file mode 100644
index 89eb579..0000000
--- a/src/com/android/launcher3/RampUpScroller.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2013 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.launcher3;
-
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-/**
- * Scroller that gradually reaches a target velocity.
- */
-class RampUpScroller {
- private final Interpolator mInterpolator;
- private final long mRampUpTime;
-
- private long mStartTime;
- private long mDeltaTime;
- private float mTargetVelocityX;
- private float mTargetVelocityY;
- private int mDeltaX;
- private int mDeltaY;
-
- /**
- * Creates a new ramp-up scroller that reaches full velocity after a
- * specified duration.
- *
- * @param rampUpTime Duration before the scroller reaches target velocity.
- */
- public RampUpScroller(long rampUpTime) {
- mInterpolator = new AccelerateInterpolator();
- mRampUpTime = rampUpTime;
- }
-
- /**
- * Starts the scroller at the current animation time.
- */
- public void start() {
- mStartTime = AnimationUtils.currentAnimationTimeMillis();
- mDeltaTime = mStartTime;
- }
-
- /**
- * Computes the current scroll deltas. This usually only be called after
- * starting the scroller with {@link #start()}.
- *
- * @see #getDeltaX()
- * @see #getDeltaY()
- */
- public void computeScrollDelta() {
- final long currentTime = AnimationUtils.currentAnimationTimeMillis();
- final long elapsedSinceStart = currentTime - mStartTime;
- final float scale;
- if (elapsedSinceStart < mRampUpTime) {
- scale = mInterpolator.getInterpolation((float) elapsedSinceStart / mRampUpTime);
- } else {
- scale = 1f;
- }
-
- final long elapsedSinceDelta = currentTime - mDeltaTime;
- mDeltaTime = currentTime;
-
- mDeltaX = (int) (elapsedSinceDelta * scale * mTargetVelocityX);
- mDeltaY = (int) (elapsedSinceDelta * scale * mTargetVelocityY);
- }
-
- /**
- * Sets the target velocity for this scroller.
- *
- * @param x The target X velocity in pixels per millisecond.
- * @param y The target Y velocity in pixels per millisecond.
- */
- public void setTargetVelocity(float x, float y) {
- mTargetVelocityX = x;
- mTargetVelocityY = y;
- }
-
- /**
- * @return The target X velocity for this scroller.
- */
- public float getTargetVelocityX() {
- return mTargetVelocityX;
- }
-
- /**
- * @return The target Y velocity for this scroller.
- */
- public float getTargetVelocityY() {
- return mTargetVelocityY;
- }
-
- /**
- * The distance traveled in the X-coordinate computed by the last call to
- * {@link #computeScrollDelta()}.
- */
- public int getDeltaX() {
- return mDeltaX;
- }
-
- /**
- * The distance traveled in the Y-coordinate computed by the last call to
- * {@link #computeScrollDelta()}.
- */
- public int getDeltaY() {
- return mDeltaY;
- }
-}
diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java
index 18e7c86..435dbda 100644
--- a/src/com/android/launcher3/SearchDropTargetBar.java
+++ b/src/com/android/launcher3/SearchDropTargetBar.java
@@ -71,10 +71,10 @@
mDeleteDropTarget.setLauncher(launcher);
mQSBSearchBar = launcher.getQsbBar();
if (mEnableDropDownDropTargets) {
- mQSBSearchBarAnim = ObjectAnimator.ofFloat(mQSBSearchBar, "translationY", 0f,
+ mQSBSearchBarAnim = LauncherAnimUtils.ofFloat(mQSBSearchBar, "translationY", 0,
-mBarHeight);
} else {
- mQSBSearchBarAnim = ObjectAnimator.ofFloat(mQSBSearchBar, "alpha", 1f, 0f);
+ mQSBSearchBarAnim = LauncherAnimUtils.ofFloat(mQSBSearchBar, "alpha", 1f, 0f);
}
setupAnimation(mQSBSearchBarAnim, mQSBSearchBar);
}
@@ -117,12 +117,12 @@
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
mBarHeight = grid.searchBarSpaceHeightPx;
mDropTargetBar.setTranslationY(-mBarHeight);
- mDropTargetBarAnim = ObjectAnimator.ofFloat(mDropTargetBar, "translationY",
+ mDropTargetBarAnim = LauncherAnimUtils.ofFloat(mDropTargetBar, "translationY",
-mBarHeight, 0f);
} else {
mDropTargetBar.setAlpha(0f);
- mDropTargetBarAnim = ObjectAnimator.ofFloat(mDropTargetBar, "alpha", 0f, 1f);
+ mDropTargetBarAnim = LauncherAnimUtils.ofFloat(mDropTargetBar, "alpha", 0f, 1f);
}
setupAnimation(mDropTargetBarAnim, mDropTargetBar);
}
diff --git a/src/com/android/launcher3/SmoothPagedView.java b/src/com/android/launcher3/SmoothPagedView.java
index 64dcb34..4e331aa 100644
--- a/src/com/android/launcher3/SmoothPagedView.java
+++ b/src/com/android/launcher3/SmoothPagedView.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.util.AttributeSet;
import android.view.animation.Interpolator;
-import android.widget.Scroller;
public abstract class SmoothPagedView extends PagedView {
private static final float SMOOTHING_SPEED = 0.75f;
@@ -100,7 +99,7 @@
mBaseLineFlingVelocity = 2500.0f;
mFlingVelocityInfluence = 0.4f;
mScrollInterpolator = new OvershootInterpolator();
- mScroller = new Scroller(getContext(), mScrollInterpolator);
+ setDefaultInterpolator(mScrollInterpolator);
}
}
diff --git a/src/com/android/launcher3/WallpaperCropActivity.java b/src/com/android/launcher3/WallpaperCropActivity.java
index 276aba3..2102599 100644
--- a/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/src/com/android/launcher3/WallpaperCropActivity.java
@@ -550,6 +550,8 @@
Rect roundedTrueCrop = new Rect();
Matrix rotateMatrix = new Matrix();
Matrix inverseRotateMatrix = new Matrix();
+
+ Point bounds = getImageBounds();
if (mRotation > 0) {
rotateMatrix.setRotate(mRotation);
inverseRotateMatrix.setRotate(-mRotation);
@@ -557,7 +559,6 @@
mCropBounds.roundOut(roundedTrueCrop);
mCropBounds = new RectF(roundedTrueCrop);
- Point bounds = getImageBounds();
if (bounds == null) {
Log.w(LOGTAG, "cannot get bounds for image");
failure = true;
@@ -629,12 +630,38 @@
Utils.closeSilently(is);
}
if (fullSize != null) {
+ // Find out the true sample size that was used by the decoder
+ scaleDownSampleSize = bounds.x / fullSize.getWidth();
mCropBounds.left /= scaleDownSampleSize;
mCropBounds.top /= scaleDownSampleSize;
mCropBounds.bottom /= scaleDownSampleSize;
mCropBounds.right /= scaleDownSampleSize;
mCropBounds.roundOut(roundedTrueCrop);
+ // Adjust values to account for issues related to rounding
+ if (roundedTrueCrop.width() > fullSize.getWidth()) {
+ // Adjust the width
+ roundedTrueCrop.right = roundedTrueCrop.left + fullSize.getWidth();
+ }
+ if (roundedTrueCrop.right > fullSize.getWidth()) {
+ // Adjust the left value
+ int adjustment = roundedTrueCrop.left -
+ Math.max(0, roundedTrueCrop.right - roundedTrueCrop.width());
+ roundedTrueCrop.left -= adjustment;
+ roundedTrueCrop.right -= adjustment;
+ }
+ if (roundedTrueCrop.height() > fullSize.getHeight()) {
+ // Adjust the height
+ roundedTrueCrop.bottom = roundedTrueCrop.top + fullSize.getHeight();
+ }
+ if (roundedTrueCrop.bottom > fullSize.getHeight()) {
+ // Adjust the top value
+ int adjustment = roundedTrueCrop.top -
+ Math.max(0, roundedTrueCrop.bottom - roundedTrueCrop.height());
+ roundedTrueCrop.top -= adjustment;
+ roundedTrueCrop.bottom -= adjustment;
+ }
+
crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left,
roundedTrueCrop.top, roundedTrueCrop.width(),
roundedTrueCrop.height());
@@ -759,14 +786,15 @@
final WallpaperManager wallpaperManager) {
final Point defaultWallpaperSize = getDefaultWallpaperSize(res, windowManager);
- new Thread("suggestWallpaperDimension") {
- public void run() {
+ new AsyncTask<Void, Void, Void>() {
+ public Void doInBackground(Void ... args) {
// If we have saved a wallpaper width/height, use that instead
int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, defaultWallpaperSize.x);
int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, defaultWallpaperSize.y);
wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight);
+ return null;
}
- }.start();
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
}
protected static RectF getMaxCropRect(
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 8c2c89d..c6c6e13 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -31,7 +31,6 @@
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -55,10 +54,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.View.OnClickListener;
-import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.TextView;
@@ -184,7 +180,6 @@
private SpringLoadedDragController mSpringLoadedDragController;
private float mSpringLoadedShrinkFactor;
private float mOverviewModeShrinkFactor;
- private int mOverviewModePageOffset;
// State variable that indicates whether the pages are small (ie when you're
// in all apps or customize mode)
@@ -315,13 +310,13 @@
mFadeInAdjacentScreens = false;
mWallpaperManager = WallpaperManager.getInstance(context);
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.Workspace, defStyle, 0);
mSpringLoadedShrinkFactor =
res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
- mOverviewModeShrinkFactor =
- res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100.0f;
- mOverviewModePageOffset = res.getDimensionPixelSize(R.dimen.overview_mode_page_offset);
+ mOverviewModeShrinkFactor = grid.getOverviewModeScale();
mCameraDistance = res.getInteger(R.integer.config_cameraDistance);
mOriginalDefaultPage = mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
a.recycle();
@@ -339,6 +334,14 @@
@Override
public void setInsets(Rect insets) {
mInsets.set(insets);
+
+ CellLayout customScreen = getScreenWithId(CUSTOM_CONTENT_SCREEN_ID);
+ if (customScreen != null) {
+ View customContent = customScreen.getShortcutsAndWidgets().getChildAt(0);
+ if (customContent instanceof Insettable) {
+ ((Insettable) customContent).setInsets(mInsets);
+ }
+ }
}
// estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each
@@ -417,10 +420,8 @@
setClipChildren(false);
setClipToPadding(false);
setChildrenDrawnWithCacheEnabled(true);
-
- // This is a bit of a hack to account for the fact that we translate the workspace
- // up a bit, and still need to draw the background covering the whole screen.
- setMinScale(mOverviewModeShrinkFactor - 0.2f);
+
+ setMinScale(mOverviewModeShrinkFactor);
setupLayoutTransition();
final Resources res = getResources();
@@ -537,6 +538,10 @@
}
public long insertNewWorkspaceScreen(long screenId, int insertIndex) {
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - insertNewWorkspaceScreen(): " + screenId +
+ " at index: " + insertIndex, true);
+
if (mWorkspaceScreens.containsKey(screenId)) {
throw new RuntimeException("Screen id " + screenId + " already exists!");
}
@@ -622,6 +627,12 @@
if (customContent instanceof Insettable) {
((Insettable)customContent).setInsets(mInsets);
}
+
+ // Verify that the child is removed from any existing parent.
+ if (customContent.getParent() instanceof ViewGroup) {
+ ViewGroup parent = (ViewGroup) customContent.getParent();
+ parent.removeView(customContent);
+ }
customScreen.removeAllViews();
customScreen.addViewToCellLayout(customContent, 0, 0, lp, true);
mCustomContentDescription = description;
@@ -630,6 +641,9 @@
}
public void addExtraEmptyScreenOnDrag() {
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - addExtraEmptyScreenOnDrag()", true);
+
boolean lastChildOnScreen = false;
boolean childOnFinalScreen = false;
@@ -656,6 +670,9 @@
}
public boolean addExtraEmptyScreen() {
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - addExtraEmptyScreen()", true);
+
if (!mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID)) {
insertNewWorkspaceScreen(EXTRA_EMPTY_SCREEN_ID);
return true;
@@ -664,6 +681,9 @@
}
private void convertFinalScreenToEmptyScreenIfNecessary() {
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - convertFinalScreenToEmptyScreenIfNecessary()", true);
+
if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return;
long finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1);
@@ -679,6 +699,10 @@
// if this is the last non-custom content screen, convert it to the empty screen
mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen);
mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
+
+ // Update the model if we have changed any screens
+ mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
+ Launcher.addDumpLog(TAG, "11683562 - extra empty screen: " + finalScreenId, true);
}
}
@@ -688,6 +712,8 @@
public void removeExtraEmptyScreen(final boolean animate, final Runnable onComplete,
final int delay, final boolean stripEmptyScreens) {
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - removeExtraEmptyScreen()", true);
if (delay > 0) {
postDelayed(new Runnable() {
@Override
@@ -711,7 +737,12 @@
onComplete, stripEmptyScreens);
}
return;
+ } else if (stripEmptyScreens) {
+ // If we're not going to strip the empty screens after removing
+ // the extra empty screen, do it right away.
+ stripEmptyScreens();
}
+
if (onComplete != null) {
onComplete.run();
}
@@ -719,6 +750,9 @@
private void fadeAndRemoveEmptyScreen(int delay, int duration, final Runnable onComplete,
final boolean stripEmptyScreens) {
+ // Log to disk
+ // XXX: Do we need to update LM workspace screens below?
+ Launcher.addDumpLog(TAG, "11683562 - fadeAndRemoveEmptyScreen()", true);
PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f);
PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", 0f);
@@ -762,6 +796,9 @@
}
public long commitExtraEmptyScreen() {
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - commitExtraEmptyScreen()", true);
+
int index = getPageIndexForScreenId(EXTRA_EMPTY_SCREEN_ID);
CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
@@ -814,6 +851,9 @@
}
public void stripEmptyScreens() {
+ // Log to disk
+ Launcher.addDumpLog(TAG, "11683562 - stripEmptyScreens()", true);
+
if (isPageMoving()) {
mStripScreensOnPageStopMoving = true;
return;
@@ -834,6 +874,7 @@
int pageShift = 0;
for (Long id: removeScreens) {
+ Launcher.addDumpLog(TAG, "11683562 - removing id: " + id, true);
CellLayout cl = mWorkspaceScreens.get(id);
mWorkspaceScreens.remove(id);
mScreenOrder.remove(id);
@@ -952,7 +993,9 @@
}
// Get the canonical child id to uniquely represent this view in this screen
- int childId = LauncherModel.getCellLayoutChildId(container, screenId, x, y, spanX, spanY);
+ ItemInfo info = (ItemInfo) child.getTag();
+ int childId = mLauncher.getViewIdForItem(info);
+
boolean markCellsAsOccupied = !(child instanceof Folder);
if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) {
// TODO: This branch occurs when the workspace is adding views
@@ -1484,7 +1527,7 @@
mState == State.NORMAL &&
!mIsSwitchingState &&
!isInOverscroll) {
- for (int i = 0; i < getChildCount(); i++) {
+ for (int i = numCustomPages(); i < getChildCount(); i++) {
CellLayout child = (CellLayout) getChildAt(i);
if (child != null) {
float scrollProgress = getScrollProgress(screenCenter, child, i);
@@ -1998,14 +2041,17 @@
}
int getOverviewModeTranslationY() {
- int childHeight = getNormalChildHeight();
- int viewPortHeight = getViewportHeight();
- int scaledChildHeight = (int) (mOverviewModeShrinkFactor * childHeight);
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+ Rect overviewBar = grid.getOverviewModeButtonBarRect();
- int offset = (viewPortHeight - scaledChildHeight) / 2;
- int offsetDelta = mOverviewModePageOffset - offset + mInsets.top;
+ int availableHeight = getViewportHeight();
+ int scaledHeight = (int) (mOverviewModeShrinkFactor * getNormalChildHeight());
+ int offsetFromTopEdge = (availableHeight - scaledHeight) / 2;
+ int offsetToCenterInOverview = (availableHeight - mInsets.top - overviewBar.height()
+ - scaledHeight) / 2;
- return offsetDelta;
+ return -offsetFromTopEdge + mInsets.top + offsetToCenterInOverview;
}
boolean shouldVoiceButtonProxyBeVisible() {
@@ -2073,7 +2119,7 @@
mNewScale = 1.0f;
if (oldStateIsOverview) {
- disableFreeScroll(snapPage);
+ disableFreeScroll();
} else if (stateIsOverview) {
enableFreeScroll();
}
@@ -2100,15 +2146,20 @@
duration = getResources().getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime);
}
+ if (snapPage == -1) {
+ snapPage = getPageNearestToCenterOfScreen();
+ }
+ snapToPage(snapPage, duration, mZoomInInterpolator);
+
for (int i = 0; i < getChildCount(); i++) {
final CellLayout cl = (CellLayout) getChildAt(i);
- boolean isCurrentPage = (i == getNextPage());
+ boolean isCurrentPage = (i == snapPage);
float initialAlpha = cl.getShortcutsAndWidgets().getAlpha();
float finalAlpha;
if (stateIsSmall) {
finalAlpha = 0f;
} else if (stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
- finalAlpha = i == getNextPage() ? 1f : 0f;
+ finalAlpha = (i == snapPage || i < numCustomPages()) ? 1f : 0f;
} else {
finalAlpha = 1f;
}
@@ -2140,6 +2191,7 @@
final View searchBar = mLauncher.getQsbBar();
final View overviewPanel = mLauncher.getOverviewPanel();
final View hotseat = mLauncher.getHotseat();
+ final View pageIndicator = getPageIndicator();
if (animated) {
anim.setDuration(duration);
LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(this);
@@ -2179,31 +2231,38 @@
}
}
}
- ObjectAnimator pageIndicatorAlpha = null;
- if (getPageIndicator() != null) {
- pageIndicatorAlpha = ObjectAnimator.ofFloat(getPageIndicator(), "alpha",
- finalHotseatAndPageIndicatorAlpha);
+ Animator pageIndicatorAlpha = null;
+ if (pageIndicator != null) {
+ pageIndicatorAlpha = new LauncherViewPropertyAnimator(pageIndicator)
+ .alpha(finalHotseatAndPageIndicatorAlpha).withLayer();
+ pageIndicatorAlpha.addListener(new AlphaUpdateListener(pageIndicator));
+ } else {
+ // create a dummy animation so we don't need to do null checks later
+ pageIndicatorAlpha = ValueAnimator.ofFloat(0, 0);
}
- ObjectAnimator hotseatAlpha = ObjectAnimator.ofFloat(hotseat, "alpha",
- finalHotseatAndPageIndicatorAlpha);
- ObjectAnimator searchBarAlpha = ObjectAnimator.ofFloat(searchBar,
- "alpha", finalSearchBarAlpha);
- ObjectAnimator overviewPanelAlpha = ObjectAnimator.ofFloat(overviewPanel,
- "alpha", finalOverviewPanelAlpha);
- overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel));
+ Animator hotseatAlpha = new LauncherViewPropertyAnimator(hotseat)
+ .alpha(finalHotseatAndPageIndicatorAlpha).withLayer();
hotseatAlpha.addListener(new AlphaUpdateListener(hotseat));
+
+ Animator searchBarAlpha = new LauncherViewPropertyAnimator(searchBar)
+ .alpha(finalSearchBarAlpha).withLayer();
searchBarAlpha.addListener(new AlphaUpdateListener(searchBar));
+ Animator overviewPanelAlpha = new LauncherViewPropertyAnimator(overviewPanel)
+ .alpha(finalOverviewPanelAlpha).withLayer();
+ overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel));
+
if (workspaceToOverview) {
+ pageIndicatorAlpha.setInterpolator(new DecelerateInterpolator(2));
hotseatAlpha.setInterpolator(new DecelerateInterpolator(2));
+ overviewPanelAlpha.setInterpolator(null);
} else if (overviewToWorkspace) {
+ pageIndicatorAlpha.setInterpolator(null);
+ hotseatAlpha.setInterpolator(null);
overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2));
}
-
- if (getPageIndicator() != null) {
- pageIndicatorAlpha.addListener(new AlphaUpdateListener(getPageIndicator()));
- }
+ searchBarAlpha.setInterpolator(null);
anim.play(overviewPanelAlpha);
anim.play(hotseatAlpha);
@@ -2215,9 +2274,9 @@
AlphaUpdateListener.updateVisibility(overviewPanel);
hotseat.setAlpha(finalHotseatAndPageIndicatorAlpha);
AlphaUpdateListener.updateVisibility(hotseat);
- if (getPageIndicator() != null) {
- getPageIndicator().setAlpha(finalHotseatAndPageIndicatorAlpha);
- AlphaUpdateListener.updateVisibility(getPageIndicator());
+ if (pageIndicator != null) {
+ pageIndicator.setAlpha(finalHotseatAndPageIndicatorAlpha);
+ AlphaUpdateListener.updateVisibility(pageIndicator);
}
searchBar.setAlpha(finalSearchBarAlpha);
AlphaUpdateListener.updateVisibility(searchBar);
@@ -2333,7 +2392,9 @@
void hideCustomContentIfNecessary() {
boolean hide = mState != Workspace.State.NORMAL;
if (hide && hasCustomContent()) {
+ disableLayoutTransitions();
mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(INVISIBLE);
+ enableLayoutTransitions();
}
}
@@ -2349,6 +2410,11 @@
final CellLayout cl = (CellLayout) getChildAt(i);
cl.setShortcutAndWidgetAlpha(1f);
}
+ } else {
+ for (int i = 0; i < numCustomPages(); i++) {
+ final CellLayout cl = (CellLayout) getChildAt(i);
+ cl.setShortcutAndWidgetAlpha(1f);
+ }
}
showCustomContentIfNecessary();
}
@@ -2536,8 +2602,16 @@
icon.clearPressedOrFocusedBackground();
}
- mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
+ if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
+ String msg = "Drag started with a view that has no tag set. This "
+ + "will cause a crash (issue 11627249) down the line. "
+ + "View: " + child + " tag: " + child.getTag();
+ throw new IllegalStateException(msg);
+ }
+
+ DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale);
+ dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
if (child.getParent() instanceof ShortcutAndWidgetContainer) {
mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
@@ -2883,8 +2957,6 @@
lp.cellHSpan = item.spanX;
lp.cellVSpan = item.spanY;
lp.isLockedToGrid = true;
- cell.setId(LauncherModel.getCellLayoutChildId(container, mDragInfo.screenId,
- mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY));
if (container != LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
cell instanceof LauncherAppWidgetHostView) {
@@ -3907,7 +3979,7 @@
return mDragInfo;
}
- public int getRestorePage() {
+ public int getCurrentPageOffsetFromCustomContent() {
return getNextPage() - numCustomPages();
}
@@ -4141,6 +4213,11 @@
}
@Override
+ public float getIntrinsicIconScaleFactor() {
+ return 1f;
+ }
+
+ @Override
public boolean supportsFlingToDelete() {
return true;
}
@@ -4177,7 +4254,9 @@
if (mSavedStates != null) {
mRestoredPages.add(child);
CellLayout cl = (CellLayout) getChildAt(child);
- cl.restoreInstanceState(mSavedStates);
+ if (cl != null) {
+ cl.restoreInstanceState(mSavedStates);
+ }
}
}
@@ -4561,6 +4640,7 @@
child.requestFocus();
}
}
+ exitWidgetResizeMode();
}
@Override
diff --git a/src/com/android/photos/BitmapRegionTileSource.java b/src/com/android/photos/BitmapRegionTileSource.java
index 2f203af..8511de2 100644
--- a/src/com/android/photos/BitmapRegionTileSource.java
+++ b/src/com/android/photos/BitmapRegionTileSource.java
@@ -94,7 +94,6 @@
}
class DumbBitmapRegionDecoder implements SimpleBitmapRegionDecoder {
- //byte[] mStreamCopy;
Bitmap mBuffer;
Canvas mTempCanvas;
Paint mTempPaint;