diff options
71 files changed, 1138 insertions, 381 deletions
diff --git a/res/drawable-sw600dp-hdpi/homescreen_blue_normal_holo.9.png b/res/drawable-sw600dp-hdpi/homescreen_blue_normal_holo.9.png Binary files differdeleted file mode 100644 index 98f7ac8a7..000000000 --- a/res/drawable-sw600dp-hdpi/homescreen_blue_normal_holo.9.png +++ /dev/null diff --git a/res/drawable-sw600dp-hdpi/homescreen_blue_strong_holo.9.png b/res/drawable-sw600dp-hdpi/homescreen_blue_strong_holo.9.png Binary files differdeleted file mode 100644 index 0a511f9df..000000000 --- a/res/drawable-sw600dp-hdpi/homescreen_blue_strong_holo.9.png +++ /dev/null diff --git a/res/drawable-sw600dp-hdpi/ic_allapps.png b/res/drawable-sw600dp-hdpi/ic_allapps.png Binary files differdeleted file mode 100644 index 8bda43569..000000000 --- a/res/drawable-sw600dp-hdpi/ic_allapps.png +++ /dev/null diff --git a/res/drawable-sw600dp-hdpi/ic_allapps_pressed.png b/res/drawable-sw600dp-hdpi/ic_allapps_pressed.png Binary files differdeleted file mode 100644 index 07ff33183..000000000 --- a/res/drawable-sw600dp-hdpi/ic_allapps_pressed.png +++ /dev/null diff --git a/res/drawable-sw600dp-hdpi/overscroll_glow_left.9.png b/res/drawable-sw600dp-hdpi/overscroll_glow_left.9.png Binary files differdeleted file mode 100644 index 3928e2c59..000000000 --- a/res/drawable-sw600dp-hdpi/overscroll_glow_left.9.png +++ /dev/null diff --git a/res/drawable-sw600dp-hdpi/overscroll_glow_right.9.png b/res/drawable-sw600dp-hdpi/overscroll_glow_right.9.png Binary files differdeleted file mode 100644 index e34de34ac..000000000 --- a/res/drawable-sw600dp-hdpi/overscroll_glow_right.9.png +++ /dev/null diff --git a/res/drawable-sw600dp-hdpi/portal_ring_inner_holo.png b/res/drawable-sw600dp-hdpi/portal_ring_inner_holo.png Binary files differdeleted file mode 100644 index b3be4720a..000000000 --- a/res/drawable-sw600dp-hdpi/portal_ring_inner_holo.png +++ /dev/null diff --git a/res/drawable-sw600dp-hdpi/portal_ring_inner_nolip_holo.png b/res/drawable-sw600dp-hdpi/portal_ring_inner_nolip_holo.png Binary files differdeleted file mode 100644 index 96b981c50..000000000 --- a/res/drawable-sw600dp-hdpi/portal_ring_inner_nolip_holo.png +++ /dev/null diff --git a/res/drawable-sw600dp-hdpi/portal_ring_outer_holo.png b/res/drawable-sw600dp-hdpi/portal_ring_outer_holo.png Binary files differdeleted file mode 100644 index bc13a26cd..000000000 --- a/res/drawable-sw600dp-hdpi/portal_ring_outer_holo.png +++ /dev/null diff --git a/res/drawable-sw600dp-mdpi/homescreen_blue_normal_holo.9.png b/res/drawable-sw600dp-mdpi/homescreen_blue_normal_holo.9.png Binary files differdeleted file mode 100644 index 3ae4affb0..000000000 --- a/res/drawable-sw600dp-mdpi/homescreen_blue_normal_holo.9.png +++ /dev/null diff --git a/res/drawable-sw600dp-mdpi/homescreen_blue_strong_holo.9.png b/res/drawable-sw600dp-mdpi/homescreen_blue_strong_holo.9.png Binary files differdeleted file mode 100644 index 31148dd52..000000000 --- a/res/drawable-sw600dp-mdpi/homescreen_blue_strong_holo.9.png +++ /dev/null diff --git a/res/drawable-sw600dp-mdpi/ic_allapps.png b/res/drawable-sw600dp-mdpi/ic_allapps.png Binary files differdeleted file mode 100644 index e2afea5f3..000000000 --- a/res/drawable-sw600dp-mdpi/ic_allapps.png +++ /dev/null diff --git a/res/drawable-sw600dp-mdpi/ic_allapps_pressed.png b/res/drawable-sw600dp-mdpi/ic_allapps_pressed.png Binary files differdeleted file mode 100644 index d409c7ed3..000000000 --- a/res/drawable-sw600dp-mdpi/ic_allapps_pressed.png +++ /dev/null diff --git a/res/drawable-sw600dp-mdpi/overscroll_glow_left.9.png b/res/drawable-sw600dp-mdpi/overscroll_glow_left.9.png Binary files differdeleted file mode 100644 index 58ec10c43..000000000 --- a/res/drawable-sw600dp-mdpi/overscroll_glow_left.9.png +++ /dev/null diff --git a/res/drawable-sw600dp-mdpi/overscroll_glow_right.9.png b/res/drawable-sw600dp-mdpi/overscroll_glow_right.9.png Binary files differdeleted file mode 100644 index a986fd301..000000000 --- a/res/drawable-sw600dp-mdpi/overscroll_glow_right.9.png +++ /dev/null diff --git a/res/drawable-sw600dp-mdpi/portal_ring_inner_holo.png b/res/drawable-sw600dp-mdpi/portal_ring_inner_holo.png Binary files differdeleted file mode 100644 index 319c07427..000000000 --- a/res/drawable-sw600dp-mdpi/portal_ring_inner_holo.png +++ /dev/null diff --git a/res/drawable-sw600dp-mdpi/portal_ring_inner_nolip_holo.png b/res/drawable-sw600dp-mdpi/portal_ring_inner_nolip_holo.png Binary files differdeleted file mode 100644 index 8537714aa..000000000 --- a/res/drawable-sw600dp-mdpi/portal_ring_inner_nolip_holo.png +++ /dev/null diff --git a/res/drawable-sw600dp-mdpi/portal_ring_outer_holo.png b/res/drawable-sw600dp-mdpi/portal_ring_outer_holo.png Binary files differdeleted file mode 100644 index 365dcfce9..000000000 --- a/res/drawable-sw600dp-mdpi/portal_ring_outer_holo.png +++ /dev/null diff --git a/res/drawable-sw600dp-xhdpi/homescreen_blue_normal_holo.9.png b/res/drawable-sw600dp-xhdpi/homescreen_blue_normal_holo.9.png Binary files differdeleted file mode 100644 index c25b590a9..000000000 --- a/res/drawable-sw600dp-xhdpi/homescreen_blue_normal_holo.9.png +++ /dev/null diff --git a/res/drawable-sw600dp-xhdpi/homescreen_blue_strong_holo.9.png b/res/drawable-sw600dp-xhdpi/homescreen_blue_strong_holo.9.png Binary files differdeleted file mode 100644 index 24ac88013..000000000 --- a/res/drawable-sw600dp-xhdpi/homescreen_blue_strong_holo.9.png +++ /dev/null diff --git a/res/drawable-sw600dp-xhdpi/ic_allapps.png b/res/drawable-sw600dp-xhdpi/ic_allapps.png Binary files differdeleted file mode 100644 index 8fed2903a..000000000 --- a/res/drawable-sw600dp-xhdpi/ic_allapps.png +++ /dev/null diff --git a/res/drawable-sw600dp-xhdpi/ic_allapps_pressed.png b/res/drawable-sw600dp-xhdpi/ic_allapps_pressed.png Binary files differdeleted file mode 100644 index 786b676ff..000000000 --- a/res/drawable-sw600dp-xhdpi/ic_allapps_pressed.png +++ /dev/null diff --git a/res/drawable-sw600dp-xhdpi/overscroll_glow_left.9.png b/res/drawable-sw600dp-xhdpi/overscroll_glow_left.9.png Binary files differdeleted file mode 100644 index b66dd2fe6..000000000 --- a/res/drawable-sw600dp-xhdpi/overscroll_glow_left.9.png +++ /dev/null diff --git a/res/drawable-sw600dp-xhdpi/overscroll_glow_right.9.png b/res/drawable-sw600dp-xhdpi/overscroll_glow_right.9.png Binary files differdeleted file mode 100644 index 3ccce3302..000000000 --- a/res/drawable-sw600dp-xhdpi/overscroll_glow_right.9.png +++ /dev/null diff --git a/res/drawable-sw600dp-xhdpi/portal_ring_inner_holo.png b/res/drawable-sw600dp-xhdpi/portal_ring_inner_holo.png Binary files differdeleted file mode 100644 index d4ce45f12..000000000 --- a/res/drawable-sw600dp-xhdpi/portal_ring_inner_holo.png +++ /dev/null diff --git a/res/drawable-sw600dp-xhdpi/portal_ring_inner_nolip_holo.png b/res/drawable-sw600dp-xhdpi/portal_ring_inner_nolip_holo.png Binary files differdeleted file mode 100644 index 9aa13c987..000000000 --- a/res/drawable-sw600dp-xhdpi/portal_ring_inner_nolip_holo.png +++ /dev/null diff --git a/res/drawable-sw600dp-xhdpi/portal_ring_outer_holo.png b/res/drawable-sw600dp-xhdpi/portal_ring_outer_holo.png Binary files differdeleted file mode 100644 index 0106cd6e0..000000000 --- a/res/drawable-sw600dp-xhdpi/portal_ring_outer_holo.png +++ /dev/null diff --git a/res/drawable-sw600dp-xxhdpi/homescreen_blue_normal_holo.9.png b/res/drawable-sw600dp-xxhdpi/homescreen_blue_normal_holo.9.png Binary files differdeleted file mode 100644 index c661f68fb..000000000 --- a/res/drawable-sw600dp-xxhdpi/homescreen_blue_normal_holo.9.png +++ /dev/null diff --git a/res/drawable-sw600dp-xxhdpi/homescreen_blue_strong_holo.9.png b/res/drawable-sw600dp-xxhdpi/homescreen_blue_strong_holo.9.png Binary files differdeleted file mode 100644 index bf6ab97c8..000000000 --- a/res/drawable-sw600dp-xxhdpi/homescreen_blue_strong_holo.9.png +++ /dev/null diff --git a/res/drawable-sw600dp-xxhdpi/ic_allapps.png b/res/drawable-sw600dp-xxhdpi/ic_allapps.png Binary files differdeleted file mode 100644 index 242965605..000000000 --- a/res/drawable-sw600dp-xxhdpi/ic_allapps.png +++ /dev/null diff --git a/res/drawable-sw600dp-xxhdpi/ic_allapps_pressed.png b/res/drawable-sw600dp-xxhdpi/ic_allapps_pressed.png Binary files differdeleted file mode 100644 index b93a51bea..000000000 --- a/res/drawable-sw600dp-xxhdpi/ic_allapps_pressed.png +++ /dev/null diff --git a/res/drawable-sw600dp-xxhdpi/overscroll_glow_left.9.png b/res/drawable-sw600dp-xxhdpi/overscroll_glow_left.9.png Binary files differdeleted file mode 100644 index 472c3f921..000000000 --- a/res/drawable-sw600dp-xxhdpi/overscroll_glow_left.9.png +++ /dev/null diff --git a/res/drawable-sw600dp-xxhdpi/overscroll_glow_right.9.png b/res/drawable-sw600dp-xxhdpi/overscroll_glow_right.9.png Binary files differdeleted file mode 100644 index e30cc9705..000000000 --- a/res/drawable-sw600dp-xxhdpi/overscroll_glow_right.9.png +++ /dev/null diff --git a/res/drawable-sw600dp-xxhdpi/portal_ring_inner_holo.png b/res/drawable-sw600dp-xxhdpi/portal_ring_inner_holo.png Binary files differdeleted file mode 100644 index 65b254197..000000000 --- a/res/drawable-sw600dp-xxhdpi/portal_ring_inner_holo.png +++ /dev/null diff --git a/res/drawable-sw600dp-xxhdpi/portal_ring_inner_nolip_holo.png b/res/drawable-sw600dp-xxhdpi/portal_ring_inner_nolip_holo.png Binary files differdeleted file mode 100644 index 506864643..000000000 --- a/res/drawable-sw600dp-xxhdpi/portal_ring_inner_nolip_holo.png +++ /dev/null diff --git a/res/drawable-sw600dp-xxhdpi/portal_ring_outer_holo.png b/res/drawable-sw600dp-xxhdpi/portal_ring_outer_holo.png Binary files differdeleted file mode 100644 index 662842560..000000000 --- a/res/drawable-sw600dp-xxhdpi/portal_ring_outer_holo.png +++ /dev/null diff --git a/res/drawable-sw720dp-hdpi/workspace_bg.9.png b/res/drawable-sw720dp-hdpi/workspace_bg.9.png Binary files differdeleted file mode 100644 index 5bbfa4fff..000000000 --- a/res/drawable-sw720dp-hdpi/workspace_bg.9.png +++ /dev/null diff --git a/res/drawable-sw720dp-mdpi/workspace_bg.9.png b/res/drawable-sw720dp-mdpi/workspace_bg.9.png Binary files differdeleted file mode 100644 index 2856e0985..000000000 --- a/res/drawable-sw720dp-mdpi/workspace_bg.9.png +++ /dev/null diff --git a/res/drawable-sw720dp-xhdpi/workspace_bg.9.png b/res/drawable-sw720dp-xhdpi/workspace_bg.9.png Binary files differdeleted file mode 100644 index 72269f207..000000000 --- a/res/drawable-sw720dp-xhdpi/workspace_bg.9.png +++ /dev/null diff --git a/res/layout/folder_icon.xml b/res/layout/folder_icon.xml index 44056828c..5147f9960 100644 --- a/res/layout/folder_icon.xml +++ b/res/layout/folder_icon.xml @@ -30,5 +30,8 @@ android:src="@drawable/portal_ring_inner_holo"/> <com.android.launcher3.BubbleTextView style="@style/WorkspaceIcon" - android:id="@+id/folder_icon_name" /> + android:id="@+id/folder_icon_name" + android:layout_gravity="top" + android:layout_width="match_parent" + android:layout_height="match_parent" /> </com.android.launcher3.FolderIcon> diff --git a/res/layout/wallpaper_cropper.xml b/res/layout/wallpaper_cropper.xml index 3a3d98a69..abb860898 100644 --- a/res/layout/wallpaper_cropper.xml +++ b/res/layout/wallpaper_cropper.xml @@ -32,7 +32,7 @@ android:visibility="invisible" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center" + android:layout_centerInParent="true" android:indeterminate="true" android:indeterminateOnly="true" android:background="@android:color/transparent" /> diff --git a/res/layout/wallpaper_picker.xml b/res/layout/wallpaper_picker.xml index 0492b7bd9..c36493d2f 100644 --- a/res/layout/wallpaper_picker.xml +++ b/res/layout/wallpaper_picker.xml @@ -27,18 +27,13 @@ android:id="@+id/cropView" android:layout_width="match_parent" android:layout_height="match_parent" /> - <ImageView - android:id="@+id/defaultWallpaperView" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:visibility="invisible" /> <ProgressBar android:id="@+id/loading" style="@android:style/Widget.Holo.ProgressBar.Large" android:visibility="invisible" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center" + android:layout_centerInParent="true" android:indeterminate="true" android:indeterminateOnly="true" android:background="@android:color/transparent" /> diff --git a/res/values/colors.xml b/res/values/colors.xml index dc35a3f68..c07390380 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -34,6 +34,7 @@ <color name="apps_customize_icon_text_color">#FFF</color> <color name="wallpaper_picker_translucent_gray">#66000000</color> <color name="folder_items_text_color">#FF333333</color> + <color name="folder_items_glow_color">#FFCCCCCC</color> <color name="outline_color">#FFFFFFFF</color> <color name="first_run_cling_circle_background_color">#64b1ea</color> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index b4b2367c7..dcddc09ad 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -20,6 +20,7 @@ <dimen name="dynamic_grid_search_bar_max_width">500dp</dimen> <dimen name="dynamic_grid_search_bar_height">48dp</dimen> <dimen name="dynamic_grid_page_indicator_height">24dp</dimen> + <dimen name="dynamic_grid_icon_drawable_padding">4dp</dimen> <!-- Wallpaper picker --> <dimen name="wallpaperThumbnailWidth">106.5dp</dimen> diff --git a/res/values/strings.xml b/res/values/strings.xml index f916ad8a5..30f4d8fd3 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -30,6 +30,14 @@ <string name="folder_name"></string> <!-- Button label on Wallpaper picker screen; user selects this button to set a specific wallpaper --> <string name="wallpaper_instructions">Set wallpaper</string> + <!-- Error message when an image is selected as a wallpaper, + but the wallpaper picker cannot load it --> + <string name="image_load_fail">Coudn\'t load image</string> + <!-- Error message when an image is selected as a wallpaper, + but the wallpaper cropper cannot load it. The user will + usually see this when using another app and trying to set + an image as the wallpaper --> + <string name="wallpaper_load_fail">Couldn\'t load image as wallpaper</string> <!-- Shown when wallpapers are selected in Wallpaper picker --> <!-- String indicating how many media item(s) is(are) selected eg. 1 selected [CHAR LIMIT=30] --> diff --git a/res/values/styles.xml b/res/values/styles.xml index a1d2c5ccf..39e55d894 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -98,7 +98,7 @@ <style name="WorkspaceIcon.AppsCustomize"> <item name="android:background">@null</item> <item name="android:textColor">@color/apps_customize_icon_text_color</item> - <item name="android:drawablePadding">4dp</item> + <item name="android:drawablePadding">@dimen/dynamic_grid_icon_drawable_padding</item> <item name="android:shadowRadius">4.0</item> <item name="android:shadowColor">#FF000000</item> </style> diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java index 9b35bb5ea..25cd9f2f1 100644 --- a/src/com/android/launcher3/AppsCustomizePagedView.java +++ b/src/com/android/launcher3/AppsCustomizePagedView.java @@ -811,13 +811,23 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen !(target instanceof DeleteDropTarget))) { // Exit spring loaded mode if we have not successfully dropped or have not handled the // drop in Workspace - mLauncher.exitSpringLoadedDragMode(); + mLauncher.getWorkspace().removeExtraEmptyScreen(true, new Runnable() { + @Override + public void run() { + mLauncher.exitSpringLoadedDragMode(); + mLauncher.unlockScreenOrientation(false); + } + }); + } else { + mLauncher.unlockScreenOrientation(false); } - mLauncher.unlockScreenOrientation(false); } @Override public View getContent() { + if (getChildCount() > 0) { + return getChildAt(0); + } return null; } @@ -841,7 +851,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { mInTransition = false; for (AsyncTaskPageData d : mDeferredSyncWidgetPageItems) { - onSyncWidgetPageItems(d); + onSyncWidgetPageItems(d, false); } mDeferredSyncWidgetPageItems.clear(); for (Runnable r : mDeferredPrepareLoadWidgetPreviewsTasks) { @@ -935,12 +945,12 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } public void setContentType(ContentType type) { - int page = getCurrentPage(); - if (mContentType != type) { - page = 0; + // Widgets appear to be cleared every time you leave, always force invalidate for them + if (mContentType != type || type == ContentType.Widgets) { + int page = (mContentType != type) ? 0 : getCurrentPage(); + mContentType = type; + invalidatePageData(page, true); } - mContentType = type; - invalidatePageData(page, true); } public ContentType getContentType() { @@ -1110,7 +1120,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen mRunningTasks.remove(task); if (task.isCancelled()) return; // do cleanup inside onSyncWidgetPageItems - onSyncWidgetPageItems(data); + onSyncWidgetPageItems(data, false); } }, mWidgetPreviewLoader); @@ -1226,7 +1236,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen AsyncTaskPageData data = new AsyncTaskPageData(page, items, maxPreviewWidth, maxPreviewHeight, null, null, mWidgetPreviewLoader); loadWidgetPreviewsInBackground(null, data); - onSyncWidgetPageItems(data); + onSyncWidgetPageItems(data, immediate); } else { if (mInTransition) { mDeferredPrepareLoadWidgetPreviewsTasks.add(this); @@ -1265,8 +1275,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen } } - private void onSyncWidgetPageItems(AsyncTaskPageData data) { - if (mInTransition) { + private void onSyncWidgetPageItems(AsyncTaskPageData data, boolean immediatelySyncItems) { + if (!immediatelySyncItems && mInTransition) { mDeferredSyncWidgetPageItems.add(data); return; } diff --git a/src/com/android/launcher3/AppsCustomizeTabHost.java b/src/com/android/launcher3/AppsCustomizeTabHost.java index bfcf92ac2..697bd7ecf 100644 --- a/src/com/android/launcher3/AppsCustomizeTabHost.java +++ b/src/com/android/launcher3/AppsCustomizeTabHost.java @@ -367,6 +367,10 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona @Override public View getContent() { + View appsCustomizeContent = mAppsCustomizePane.getContent(); + if (appsCustomizeContent != null) { + return appsCustomizeContent; + } return mContent; } @@ -397,6 +401,7 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona @Override public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) { + mAppsCustomizePane.onLauncherTransitionStart(l, animated, toWorkspace); if (animated) { enableAndBuildHardwareLayer(); } @@ -407,7 +412,7 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona @Override public void onLauncherTransitionStep(Launcher l, float t) { - // Do nothing + mAppsCustomizePane.onLauncherTransitionStep(l, t); } @Override diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 8dab9432a..30016e5b3 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -87,7 +87,7 @@ public class BubbleTextView extends TextView { // Ensure we are using the right text size LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - setTextSize(TypedValue.COMPLEX_UNIT_SP, grid.iconTextSize); + setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); setTextColor(getResources().getColor(R.color.workspace_icon_text_color)); } @@ -111,7 +111,7 @@ public class BubbleTextView extends TextView { setCompoundDrawables(null, Utilities.createIconDrawable(b), null, null); - setCompoundDrawablePadding((int) ((grid.folderIconSizePx - grid.iconSizePx) / 2f)); + setCompoundDrawablePadding(grid.iconDrawablePaddingPx); setText(info.title); setTag(info); } @@ -203,6 +203,10 @@ public class BubbleTextView extends TextView { destCanvas.restore(); } + public void setGlowColor(int color) { + mFocusedOutlineColor = mFocusedGlowColor = mPressedOutlineColor = mPressedGlowColor = color; + } + /** * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location. * Responsibility for the bitmap is transferred to the caller. diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index dafb79ffc..bf1e9c931 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -214,7 +214,7 @@ public class CellLayout extends ViewGroup { setAlwaysDrawnWithCacheEnabled(false); final Resources res = getResources(); - mHotseatScale = (float) grid.hotseatIconSize / grid.iconSize; + mHotseatScale = (float) grid.hotseatIconSizePx / grid.iconSizePx; mNormalBackground = res.getDrawable(R.drawable.screenpanel); mActiveGlowBackground = res.getDrawable(R.drawable.screenpanel_hover); diff --git a/src/com/android/launcher3/Cling.java b/src/com/android/launcher3/Cling.java index 338b722ab..9974acde6 100644 --- a/src/com/android/launcher3/Cling.java +++ b/src/com/android/launcher3/Cling.java @@ -147,7 +147,7 @@ public class Cling extends FrameLayout implements Insettable, View.OnClickListen pos.left + Utilities.sIconTextureWidth, pos.top + Utilities.sIconTextureHeight); Utilities.scaleRectAboutCenter(mFocusedHotseatAppBounds, - (grid.hotseatIconSize / grid.iconSize)); + ((float) grid.hotseatIconSizePx / grid.iconSizePx)); // Set the title TextView v = (TextView) findViewById(R.id.focused_hotseat_app_title); diff --git a/src/com/android/launcher3/DrawableTileSource.java b/src/com/android/launcher3/DrawableTileSource.java new file mode 100644 index 000000000..c1f2eff0f --- /dev/null +++ b/src/com/android/launcher3/DrawableTileSource.java @@ -0,0 +1,102 @@ +/* + * 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.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; + +import com.android.gallery3d.glrenderer.BasicTexture; +import com.android.gallery3d.glrenderer.BitmapTexture; +import com.android.photos.views.TiledImageRenderer; + +public class DrawableTileSource implements TiledImageRenderer.TileSource { + private static final int GL_SIZE_LIMIT = 2048; + // This must be no larger than half the size of the GL_SIZE_LIMIT + // due to decodePreview being allowed to be up to 2x the size of the target + public static final int MAX_PREVIEW_SIZE = GL_SIZE_LIMIT / 2; + + private int mTileSize; + private int mPreviewSize; + private Drawable mDrawable; + private BitmapTexture mPreview; + + public DrawableTileSource(Context context, Drawable d, int previewSize) { + mTileSize = TiledImageRenderer.suggestedTileSize(context); + mDrawable = d; + mPreviewSize = Math.min(previewSize, MAX_PREVIEW_SIZE); + } + + @Override + public int getTileSize() { + return mTileSize; + } + + @Override + public int getImageWidth() { + return mDrawable.getIntrinsicWidth(); + } + + @Override + public int getImageHeight() { + return mDrawable.getIntrinsicHeight(); + } + + @Override + public int getRotation() { + return 0; + } + + @Override + public BasicTexture getPreview() { + if (mPreviewSize == 0) { + return null; + } + if (mPreview == null){ + float width = getImageWidth(); + float height = getImageHeight(); + while (width > MAX_PREVIEW_SIZE || height > MAX_PREVIEW_SIZE) { + width /= 2; + height /= 2; + } + Bitmap b = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(b); + mDrawable.setBounds(new Rect(0, 0, (int) width, (int) height)); + mDrawable.draw(c); + c.setBitmap(null); + mPreview = new BitmapTexture(b); + } + return mPreview; + } + + @Override + public Bitmap getTile(int level, int x, int y, Bitmap bitmap) { + int tileSize = getTileSize(); + if (bitmap == null) { + bitmap = Bitmap.createBitmap(tileSize, tileSize, Bitmap.Config.ARGB_8888); + } + Canvas c = new Canvas(bitmap); + Rect bounds = new Rect(0, 0, getImageWidth(), getImageHeight()); + bounds.offset(-x, -y); + mDrawable.setBounds(bounds); + mDrawable.draw(c); + c.setBitmap(null); + return bitmap; + } +} diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java index 4776c8668..d90deca4f 100644 --- a/src/com/android/launcher3/DynamicGrid.java +++ b/src/com/android/launcher3/DynamicGrid.java @@ -23,13 +23,17 @@ 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; @@ -52,15 +56,20 @@ class DeviceProfileQuery { } class DeviceProfile { + public static interface DeviceProfileCallbacks { + public void onAvailableSizeChanged(DeviceProfile grid); + } + String name; float minWidthDps; float minHeightDps; float numRows; float numColumns; - float iconSize; - float iconTextSize; float numHotseatIcons; - float hotseatIconSize; + private float iconSize; + private float iconTextSize; + private int iconDrawablePaddingOriginalPx; + private float hotseatIconSize; boolean isLandscape; boolean isTablet; @@ -75,8 +84,10 @@ class DeviceProfile { int heightPx; int availableWidthPx; int availableHeightPx; + int iconSizePx; int iconTextSizePx; + int iconDrawablePaddingPx; int cellWidthPx; int cellHeightPx; int folderBackgroundOffset; @@ -96,6 +107,8 @@ class DeviceProfile { 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) @@ -146,13 +159,20 @@ class DeviceProfile { 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); - iconSizePx = DynamicGrid.pxFromDp(iconSize, dm); // Interpolate the icon text size points.clear(); @@ -160,14 +180,8 @@ class DeviceProfile { points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconTextSize)); } iconTextSize = invDistWeightedInterpolate(minWidth, minHeight, points); - iconTextSizePx = DynamicGrid.pxFromSp(iconTextSize, dm); + iconDrawablePaddingOriginalPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding); - // Interpolate the hotseat size - points.clear(); - for (DeviceProfile p : profiles) { - points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numHotseatIcons)); - } - numHotseatIcons = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points)); // Interpolate the hotseat icon size points.clear(); for (DeviceProfile p : profiles) { @@ -175,11 +189,91 @@ class DeviceProfile { } // Hotseat hotseatIconSize = invDistWeightedInterpolate(minWidth, minHeight, points); - hotseatIconSizePx = DynamicGrid.pxFromDp(hotseatIconSize, dm); - hotseatAllAppsRank = (int) (numColumns / 2); - // Calculate other vars based on Configuration - updateFromConfiguration(resources, wPx, hPx, awPx, ahPx); + // Calculate the remaining vars + updateFromConfiguration(context, resources, 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); @@ -192,23 +286,7 @@ class DeviceProfile { textPaint.setTextSize(iconTextSizePx); FontMetrics fm = textPaint.getFontMetrics(); cellWidthPx = iconSizePx; - cellHeightPx = iconSizePx + (int) Math.ceil(fm.bottom - fm.top); - - // At this point, if the cells do not fit into the available height, then we need - // to shrink the icon size - /* - Rect padding = getWorkspacePadding(isLandscape ? - CellLayout.LANDSCAPE : CellLayout.PORTRAIT); - int h = (int) (numRows * cellHeightPx) + padding.top + padding.bottom; - if (h > availableHeightPx) { - float delta = h - availableHeightPx; - int deltaPx = (int) Math.ceil(delta / numRows); - iconSizePx -= deltaPx; - iconSize = DynamicGrid.dpiFromPx(iconSizePx, dm); - cellWidthPx = iconSizePx; - cellHeightPx = iconSizePx + (int) Math.ceil(fm.bottom - fm.top); - } - */ + cellHeightPx = iconSizePx + iconDrawablePaddingPx + (int) Math.ceil(fm.bottom - fm.top); // Hotseat hotseatBarHeightPx = iconSizePx + 4 * edgeMarginPx; @@ -217,26 +295,15 @@ class DeviceProfile { // Folder folderCellWidthPx = cellWidthPx + 3 * edgeMarginPx; - folderCellHeightPx = cellHeightPx + (int) ((3f/2f) * edgeMarginPx); + folderCellHeightPx = cellHeightPx + edgeMarginPx; folderBackgroundOffset = -edgeMarginPx; folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset; - } - - void updateFromConfiguration(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; + // All Apps Rect padding = getWorkspacePadding(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT); int pageIndicatorOffset = - resources.getDimensionPixelSize(R.dimen.apps_customize_page_indicator_offset); + resources.getDimensionPixelSize(R.dimen.apps_customize_page_indicator_offset); if (isLandscape) { allAppsNumRows = (availableHeightPx - pageIndicatorOffset - 4 * edgeMarginPx) / (iconSizePx + iconTextSizePx + 2 * edgeMarginPx); @@ -247,6 +314,20 @@ class DeviceProfile { (iconSizePx + 2 * edgeMarginPx); } + 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)); @@ -298,6 +379,9 @@ class DeviceProfile { return sum; } + Rect getWorkspacePadding() { + return getWorkspacePadding(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT); + } Rect getWorkspacePadding(int orientation) { Rect padding = new Rect(); if (orientation == CellLayout.LANDSCAPE && @@ -431,8 +515,7 @@ class DeviceProfile { lp.gravity = Gravity.RIGHT; lp.width = hotseatBarHeightPx; lp.height = LayoutParams.MATCH_PARENT; - hotseat.setPadding(0, 2 * edgeMarginPx, - 2 * edgeMarginPx, 2 * edgeMarginPx); + 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 - @@ -553,7 +636,7 @@ public class DynamicGrid { "Wd: " + mProfile.minWidthDps + ", Hd: " + mProfile.minHeightDps + ", W: " + mProfile.widthPx + ", H: " + mProfile.heightPx + " [r: " + mProfile.numRows + ", c: " + mProfile.numColumns + - ", is: " + mProfile.iconSizePx + ", its: " + mProfile.iconTextSize + + ", is: " + mProfile.iconSizePx + ", its: " + mProfile.iconTextSizePx + ", cw: " + mProfile.cellWidthPx + ", ch: " + mProfile.cellHeightPx + ", hc: " + mProfile.numHotseatIcons + ", his: " + mProfile.hotseatIconSizePx + "]"; } diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java index 14760c7b6..bce6707da 100644 --- a/src/com/android/launcher3/FastBitmapDrawable.java +++ b/src/com/android/launcher3/FastBitmapDrawable.java @@ -67,6 +67,7 @@ class FastBitmapDrawable extends Drawable { public void setFilterBitmap(boolean filterBitmap) { mPaint.setFilterBitmap(filterBitmap); + mPaint.setAntiAlias(filterBitmap); } public int getAlpha() { diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 69d9a3d4b..bd6101080 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -538,6 +538,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList textView.setTag(item); textView.setTextColor(getResources().getColor(R.color.folder_items_text_color)); textView.setShadowsEnabled(false); + textView.setGlowColor(getResources().getColor(R.color.folder_items_glow_color)); textView.setOnClickListener(this); textView.setOnLongClickListener(this); @@ -956,9 +957,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList Rect workspacePadding = grid.getWorkspacePadding(grid.isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT); int maxContentAreaHeight = grid.availableHeightPx - - 4 * grid.edgeMarginPx - workspacePadding.top - workspacePadding.bottom - - getPaddingTop() - getPaddingBottom() - mFolderNameHeight; return Math.min(maxContentAreaHeight, mContent.getDesiredHeight()); diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java index cd1ff2c37..5e461a7b5 100644 --- a/src/com/android/launcher3/FolderIcon.java +++ b/src/com/android/launcher3/FolderIcon.java @@ -37,7 +37,7 @@ import android.view.ViewGroup; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.ImageView; -import android.widget.LinearLayout; +import android.widget.FrameLayout; import android.widget.TextView; import com.android.launcher3.R; @@ -49,7 +49,7 @@ import java.util.ArrayList; /** * An icon that can appear on in the workspace representing an {@link UserFolder}. */ -public class FolderIcon extends LinearLayout implements FolderListener { +public class FolderIcon extends FrameLayout implements FolderListener { private Launcher mLauncher; private Folder mFolder; private FolderInfo mInfo; @@ -134,17 +134,20 @@ public class FolderIcon extends LinearLayout implements FolderListener { "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " + "is dependent on this"); } + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false); icon.setClipToPadding(false); icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name); icon.mFolderName.setText(folderInfo.title); - icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background); - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + icon.mFolderName.setCompoundDrawablePadding(0); + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) icon.mFolderName.getLayoutParams(); + lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx; + // Offset the preview background to center this view accordingly - LinearLayout.LayoutParams lp = - (LinearLayout.LayoutParams) icon.mPreviewBackground.getLayoutParams(); + icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background); + lp = (FrameLayout.LayoutParams) icon.mPreviewBackground.getLayoutParams(); lp.topMargin = grid.folderBackgroundOffset; lp.width = grid.folderIconSizePx; lp.height = grid.folderIconSizePx; @@ -537,12 +540,10 @@ public class FolderIcon extends LinearLayout implements FolderListener { if (d != null) { mOldBounds.set(d.getBounds()); d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize); - d.setFilterBitmap(true); d.setColorFilter(Color.argb(params.overlayAlpha, 255, 255, 255), PorterDuff.Mode.SRC_ATOP); d.draw(canvas); d.clearColorFilter(); - d.setFilterBitmap(false); d.setBounds(mOldBounds); } canvas.restore(); diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 543b8ee2d..a55fce01c 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -156,7 +156,7 @@ public class IconCache { Iterator<Entry<ComponentName, CacheEntry>> it = mCache.entrySet().iterator(); while (it.hasNext()) { final CacheEntry e = it.next().getValue(); - if (e.icon.getWidth() != grid.iconSizePx || e.icon.getHeight() != grid.iconSizePx) { + if (e.icon.getWidth() < grid.iconSizePx || e.icon.getHeight() < grid.iconSizePx) { it.remove(); } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index af58f79df..f721571c2 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -197,8 +197,8 @@ public class Launcher extends Activity private AnimatorSet mStateAnimation; static final int APPWIDGET_HOST_ID = 1024; - private static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300; - private static final int EXIT_SPRINGLOADED_MODE_LONG_TIMEOUT = 600; + public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300; + private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500; private static final int SHOW_CLING_DURATION = 250; private static final int DISMISS_CLING_DURATION = 200; @@ -305,6 +305,7 @@ public class Launcher extends Activity private Drawable mWorkspaceBackgroundDrawable; private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>(); + private static final boolean DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE = false; static final ArrayList<String> sDumpLogs = new ArrayList<String>(); static Date sDateStamp = new Date(); @@ -390,6 +391,7 @@ public class Launcher extends Activity display.getRealSize(realSize); DisplayMetrics dm = new DisplayMetrics(); display.getMetrics(dm); + // Lazy-initialize the dynamic grid DeviceProfile grid = app.initDynamicGrid(this, Math.min(smallestSize.x, smallestSize.y), @@ -437,18 +439,12 @@ public class Launcher extends Activity mSavedState = savedInstanceState; restoreState(mSavedState); - // Update customization drawer _after_ restoring the states - if (mAppsCustomizeContent != null) { - mAppsCustomizeContent.onPackagesUpdated( - LauncherModel.getSortedWidgetsAndShortcuts(this)); - } - if (PROFILE_STARTUP) { android.os.Debug.stopMethodTracing(); } if (!mRestoring) { - if (sPausedFromUserAction) { + if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE || sPausedFromUserAction) { // If the user leaves launcher, then we should just load items asynchronously when // they return. mModel.startLoader(true, -1); @@ -709,13 +705,24 @@ public class Launcher extends Activity // Reset the startActivity waiting flag mWaitingForResult = false; + Runnable exitSpringLoaded = new Runnable() { + @Override + public void run() { + exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), + EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); + } + }; + if (requestCode == REQUEST_BIND_APPWIDGET) { - int appWidgetId = data != null ? + final int appWidgetId = data != null ? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1; if (resultCode == RESULT_CANCELED) { completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId); + mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded, + ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); } else if (resultCode == RESULT_OK) { - addAppWidgetImpl(appWidgetId, mPendingAddInfo, null, mPendingAddWidgetInfo); + addAppWidgetImpl(appWidgetId, mPendingAddInfo, null, + mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY); } return; } else if (requestCode == REQUEST_PICK_WALLPAPER) { @@ -725,22 +732,37 @@ public class Launcher extends Activity return; } - boolean delayExitSpringLoadedMode = false; boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET || requestCode == REQUEST_CREATE_APPWIDGET); // We have special handling for widgets if (isWidgetDrop) { - int appWidgetId = data != null ? + final int appWidgetId = data != null ? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1; + final int result; + final Runnable onComplete; if (appWidgetId < 0) { Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not returned from the \\" + "widget configuration activity."); - completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId); - mWorkspace.stripEmptyScreens(); + result = RESULT_CANCELED; + completeTwoStageWidgetDrop(result, appWidgetId); + onComplete = new Runnable() { + @Override + public void run() { + exitSpringLoadedDragModeDelayed(false, 0, null); + } + }; } else { - completeTwoStageWidgetDrop(resultCode, appWidgetId); + result = resultCode; + onComplete = new Runnable() { + @Override + public void run() { + completeTwoStageWidgetDrop(result, appWidgetId); + } + }; } + mWorkspace.removeExtraEmptyScreen(true, onComplete, ON_ACTIVITY_RESULT_ANIMATION_DELAY, + false); return; } @@ -760,15 +782,15 @@ public class Launcher extends Activity if (isWorkspaceLocked()) { sPendingAddList.add(args); } else { - delayExitSpringLoadedMode = completeAdd(args); + completeAdd(args); } + mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded, + ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); } else if (resultCode == RESULT_CANCELED) { - mWorkspace.stripEmptyScreens(); + mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded, + ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); } mDragLayer.clearAnimatedView(); - // Exit spring loaded mode if necessary after cancelling the configuration of a widget - exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), delayExitSpringLoadedMode, - null); } private void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) { @@ -788,25 +810,18 @@ public class Launcher extends Activity public void run() { completeAddAppWidget(appWidgetId, mPendingAddInfo.container, mPendingAddInfo.screenId, layout, null); - exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false, - null); + exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), + EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); } }; } else if (resultCode == RESULT_CANCELED) { animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION; - onCompleteRunnable = new Runnable() { - @Override - public void run() { - exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false, - null); - } - }; } if (mDragLayer.getAnimatedView() != null) { mWorkspace.animateWidgetDrop(mPendingAddInfo, cellLayout, (DragView) mDragLayer.getAnimatedView(), onCompleteRunnable, animationType, boundWidget, true); - } else { + } else if (onCompleteRunnable != null) { // The animated view may be null in the case of a rotation during widget configuration onCompleteRunnable.run(); } @@ -1900,8 +1915,14 @@ public class Launcher extends Activity mPendingAddInfo.dropPos = null; } - void addAppWidgetImpl(final int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget, - AppWidgetProviderInfo appWidgetInfo) { + void addAppWidgetImpl(final int appWidgetId, final ItemInfo info, + final AppWidgetHostView boundWidget, final AppWidgetProviderInfo appWidgetInfo) { + addAppWidgetImpl(appWidgetId, info, boundWidget, appWidgetInfo, 0); + } + + void addAppWidgetImpl(final int appWidgetId, final ItemInfo info, + final AppWidgetHostView boundWidget, final AppWidgetProviderInfo appWidgetInfo, int + delay) { if (appWidgetInfo.configure != null) { mPendingAddWidgetInfo = appWidgetInfo; @@ -1912,10 +1933,17 @@ public class Launcher extends Activity Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_APPWIDGET); } else { // Otherwise just add it + Runnable onComplete = new Runnable() { + @Override + public void run() { + // Exit spring loaded mode if necessary after adding the widget + exitSpringLoadedDragModeDelayed(true, EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, + null); + } + }; completeAddAppWidget(appWidgetId, info.container, info.screenId, boundWidget, appWidgetInfo); - // Exit spring loaded mode if necessary after adding the widget - exitSpringLoadedDragModeDelayed(true, false, null); + mWorkspace.removeExtraEmptyScreen(true, onComplete, delay, false); } } @@ -2979,7 +3007,7 @@ public class Launcher extends Activity dispatchOnLauncherTransitionPrepare(fromView, animated, true); dispatchOnLauncherTransitionPrepare(toView, animated, true); - mAppsCustomizeContent.pauseScrolling(); + mAppsCustomizeContent.stopScrolling(); mStateAnimation.addListener(new AnimatorListenerAdapter() { @Override @@ -2991,7 +3019,6 @@ public class Launcher extends Activity onCompleteRunnable.run(); } mAppsCustomizeContent.updateCurrentPageScroll(); - mAppsCustomizeContent.resumeScrolling(); } }); @@ -3104,7 +3131,7 @@ public class Launcher extends Activity } } - void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, boolean extendedDelay, + void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay, final Runnable onCompleteRunnable) { if (mState != State.APPS_CUSTOMIZE_SPRING_LOADED) return; @@ -3121,9 +3148,8 @@ public class Launcher extends Activity exitSpringLoadedDragMode(); } } - }, (extendedDelay ? - EXIT_SPRINGLOADED_MODE_LONG_TIMEOUT : - EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT)); + }, delay); + } void exitSpringLoadedDragMode() { @@ -3662,7 +3688,7 @@ public class Launcher extends Activity } // Remove the extra empty screen - mWorkspace.removeExtraEmptyScreen(); + mWorkspace.removeExtraEmptyScreen(false, null); if (!AppsCustomizePagedView.DISABLE_ALL_APPS && addedApps != null && mAppsCustomizeContent != null) { @@ -3932,6 +3958,8 @@ public class Launcher extends Activity } else { if (mAppsCustomizeContent != null) { mAppsCustomizeContent.setApps(apps); + mAppsCustomizeContent.onPackagesUpdated( + LauncherModel.getSortedWidgetsAndShortcuts(this)); } } } diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java index 01f72a7ce..e6c220b2a 100644 --- a/src/com/android/launcher3/LauncherAnimUtils.java +++ b/src/com/android/launcher3/LauncherAnimUtils.java @@ -25,11 +25,13 @@ import android.view.View; import android.view.ViewTreeObserver; import java.util.HashSet; +import java.util.WeakHashMap; public class LauncherAnimUtils { - static HashSet<Animator> sAnimators = new HashSet<Animator>(); + static WeakHashMap<Animator, Object> sAnimators = new WeakHashMap<Animator, Object>(); static Animator.AnimatorListener sEndAnimListener = new Animator.AnimatorListener() { public void onAnimationStart(Animator animation) { + sAnimators.put(animation, null); } public void onAnimationRepeat(Animator animation) { @@ -45,7 +47,6 @@ public class LauncherAnimUtils { }; public static void cancelOnDestroyActivity(Animator a) { - sAnimators.add(a); a.addListener(sEndAnimListener); } @@ -74,13 +75,12 @@ public class LauncherAnimUtils { } public static void onDestroyActivity() { - HashSet<Animator> animators = new HashSet<Animator>(sAnimators); + HashSet<Animator> animators = new HashSet<Animator>(sAnimators.keySet()); for (Animator a : animators) { if (a.isRunning()) { a.cancel(); - } else { - sAnimators.remove(a); } + sAnimators.remove(a); } } diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index a255b89a2..fe2b43fe4 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -28,7 +28,7 @@ import android.view.Display; import java.lang.ref.WeakReference; -public class LauncherAppState { +public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { private static final String TAG = "LauncherAppState"; private static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs"; @@ -182,12 +182,12 @@ public class LauncherAppState { context.getResources(), minWidth, minHeight, width, height, availableWidth, availableHeight); + mDynamicGrid.getDeviceProfile().addCallback(this); } // Update the icon size DeviceProfile grid = mDynamicGrid.getDeviceProfile(); - Utilities.setIconSize(grid.iconSizePx); - grid.updateFromConfiguration(context.getResources(), width, height, + grid.updateFromConfiguration(context, context.getResources(), width, height, availableWidth, availableHeight); return grid; } @@ -216,4 +216,9 @@ public class LauncherAppState { public int getLongPressTimeout() { return mLongPressTimeout; } + + @Override + public void onAvailableSizeChanged(DeviceProfile grid) { + Utilities.setIconSize(grid.iconSizePx); + } } diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index d0f6770d3..e992706de 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -1046,7 +1046,6 @@ public class LauncherProvider extends ContentProvider { // recursively load some more favorites, why not? i += loadFavorites(db, resId); added = false; - mMaxItemId = -1; } else { Log.w(TAG, String.format("Skipping <include workspace=0x%08x>", resId)); } diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index e724063df..9b891e45a 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -157,7 +157,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected int mTouchSlop; private int mPagingTouchSlop; private int mMaximumVelocity; - protected int mPageSpacing; protected int mPageLayoutPaddingTop; protected int mPageLayoutPaddingBottom; protected int mPageLayoutPaddingLeft; @@ -260,9 +259,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Drop to delete private View mDeleteDropTarget; - private boolean mAutoComputePageSpacing = false; - private boolean mRecomputePageSpacing = false; - // Bouncer private boolean mTopAlignPageWhenShrinkingForBouncer = false; @@ -285,10 +281,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagedView, defStyle, 0); - setPageSpacing(a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0)); - if (mPageSpacing < 0) { - mAutoComputePageSpacing = mRecomputePageSpacing = true; - } + mPageLayoutPaddingTop = a.getDimensionPixelSize( R.styleable.PagedView_pageLayoutPaddingTop, 0); mPageLayoutPaddingBottom = a.getDimensionPixelSize( @@ -513,33 +506,41 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } scrollTo(newX, 0); mScroller.setFinalX(newX); - mScroller.forceFinished(true); + forceFinishScroller(); } /** * Called during AllApps/Home transitions to avoid unnecessary work. When that other animation - * ends, {@link #resumeScrolling()} should be called, along with - * {@link #updateCurrentPageScroll()} to correctly set the final state and re-enable scrolling. + * {@link #updateCurrentPageScroll()} should be called, to correctly set the final state and + * re-enable scrolling. */ - void pauseScrolling() { - mScroller.forceFinished(true); + void stopScrolling() { + mCurrentPage = getNextPage(); + forceFinishScroller(); } - /** - * Enables scrolling again. - * @see #pauseScrolling() - */ - void resumeScrolling() { + private void abortScrollerAnimation(boolean resetNextPage) { + mScroller.abortAnimation(); + // We need to clean up the next page here to avoid computeScrollHelper from + // updating current page on the pass. + if (resetNextPage) { + mNextPage = INVALID_PAGE; + } + } + + private void forceFinishScroller() { + mScroller.forceFinished(true); + // We need to clean up the next page here to avoid computeScrollHelper from + // updating current page on the pass. + mNextPage = INVALID_PAGE; } + /** * Sets the current page. */ void setCurrentPage(int currentPage) { if (!mScroller.isFinished()) { - mScroller.abortAnimation(); - // We need to clean up the next page here to avoid computeScrollHelper from - // updating current page on the pass. - mNextPage = INVALID_PAGE; + abortScrollerAnimation(true); } // don't introduce any checks like mCurrentPage == currentPage here-- if we change the // the default @@ -875,26 +876,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } setMeasuredDimension(scaledWidthSize, scaledHeightSize); - - if (childCount > 0) { - // Calculate the variable page spacing if necessary - if (mAutoComputePageSpacing && mRecomputePageSpacing) { - // The gap between pages in the PagedView should be equal to the gap from the page - // to the edge of the screen (so it is not visible in the current screen). To - // account for unequal padding on each side of the paged view, we take the maximum - // of the left/right gap and use that as the gap between each page. - int offset = (getViewportWidth() - getChildWidth(0)) / 2; - int spacing = Math.max(offset, widthSize - offset - - getChildAt(0).getMeasuredWidth()); - setPageSpacing(spacing); - mRecomputePageSpacing = false; - } - } - } - - public void setPageSpacing(int pageSpacing) { - mPageSpacing = pageSpacing; - requestLayout(); } @Override @@ -1029,7 +1010,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // This ensures that when children are added, they get the correct transforms / alphas // in accordance with any scroll effects. mForceScreenScrolled = true; - mRecomputePageSpacing = true; updateFreescrollBounds(); invalidate(); } @@ -1301,9 +1281,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc int offset = (getViewportWidth() - getChildWidth(mCurrentPage)) / 2; if (isLayoutRtl()) { return (x > (getViewportOffsetX() + getViewportWidth() - - offset + mPageSpacing)); + offset)); } - return (x < getViewportOffsetX() + offset - mPageSpacing); + return (x < getViewportOffsetX() + offset); } /** @@ -1312,10 +1292,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected boolean hitsNextPage(float x, float y) { int offset = (getViewportWidth() - getChildWidth(mCurrentPage)) / 2; if (isLayoutRtl()) { - return (x < getViewportOffsetX() + offset - mPageSpacing); + return (x < getViewportOffsetX() + offset); } return (x > (getViewportOffsetX() + getViewportWidth() - - offset + mPageSpacing)); + offset)); } /** Returns whether x and y originated within the buffered viewport */ @@ -1394,7 +1374,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop); if (finishedScrolling) { mTouchState = TOUCH_STATE_REST; - mScroller.abortAnimation(); + abortScrollerAnimation(false); } else { if (isTouchPointInViewportWithBuffer((int) mDownMotionX, (int) mDownMotionY)) { mTouchState = TOUCH_STATE_SCROLLING; @@ -1506,7 +1486,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc protected float getScrollProgress(int screenCenter, View v, int page) { final int halfScreenSize = getViewportWidth() / 2; - int totalDistance = v.getMeasuredWidth() + mPageSpacing; + int offset = (getViewportWidth() - getChildWidth(page)) / 2; + int totalDistance = v.getMeasuredWidth() + offset; int delta = screenCenter - (getScrollForPage(page) + halfScreenSize); float scrollProgress = delta / (totalDistance * 1.0f); @@ -1692,7 +1673,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc * will be false if being flinged. */ if (!mScroller.isFinished()) { - mScroller.abortAnimation(); + abortScrollerAnimation(false); } // Remember where the motion event started @@ -1890,7 +1871,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc } } else { if (!mScroller.isFinished()) { - mScroller.abortAnimation(); + abortScrollerAnimation(true); } float scaleX = getScaleX(); @@ -2354,8 +2335,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc if (mContentIsRefreshable) { // Force all scrolling-related behavior to end - mScroller.forceFinished(true); - mNextPage = INVALID_PAGE; + forceFinishScroller(); // Update all the pages syncPages(); @@ -2595,9 +2575,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc int newX = 0; if (slideFromLeft) { if (i == 0) { + int pageSpace = (getViewportWidth() - getChildWidth(i)) / 2; // Simulate the page being offscreen with the page spacing oldX = getViewportOffsetX() + getChildOffset(i) - getChildWidth(i) - - mPageSpacing; + - pageSpace; } else { oldX = getViewportOffsetX() + getChildOffset(i - 1); } diff --git a/src/com/android/launcher3/PagedViewIcon.java b/src/com/android/launcher3/PagedViewIcon.java index 8bfe42d24..865c0b286 100644 --- a/src/com/android/launcher3/PagedViewIcon.java +++ b/src/com/android/launcher3/PagedViewIcon.java @@ -62,15 +62,19 @@ public class PagedViewIcon extends TextView { // Ensure we are using the right text size LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - setTextSize(TypedValue.COMPLEX_UNIT_SP, grid.iconTextSize); + setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); } public void applyFromApplicationInfo(AppInfo info, boolean scaleUp, PagedViewIcon.PressedCallback cb) { + LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); + mIcon = info.iconBitmap; mPressedCallback = cb; setCompoundDrawables(null, Utilities.createIconDrawable(mIcon), null, null); + setCompoundDrawablePadding(grid.iconDrawablePaddingPx); setText(info.title); setTag(info); } diff --git a/src/com/android/launcher3/PagedViewWidget.java b/src/com/android/launcher3/PagedViewWidget.java index 45320a484..d2f897a33 100644 --- a/src/com/android/launcher3/PagedViewWidget.java +++ b/src/com/android/launcher3/PagedViewWidget.java @@ -84,11 +84,11 @@ public class PagedViewWidget extends LinearLayout { DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); TextView name = (TextView) findViewById(R.id.widget_name); if (name != null) { - name.setTextSize(TypedValue.COMPLEX_UNIT_SP, grid.iconTextSize); + name.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); } TextView dims = (TextView) findViewById(R.id.widget_dims); if (dims != null) { - dims.setTextSize(TypedValue.COMPLEX_UNIT_SP, grid.iconTextSize); + dims.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); } } diff --git a/src/com/android/launcher3/SavedWallpaperImages.java b/src/com/android/launcher3/SavedWallpaperImages.java index c19692b9a..58add7022 100644 --- a/src/com/android/launcher3/SavedWallpaperImages.java +++ b/src/com/android/launcher3/SavedWallpaperImages.java @@ -60,13 +60,9 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter { public void onClick(WallpaperPickerActivity a) { String imageFilename = a.getSavedImages().getImageFilename(mDbId); File file = new File(a.getFilesDir(), imageFilename); - CropView v = a.getCropView(); - int rotation = WallpaperCropActivity.getRotationFromExif(file.getAbsolutePath()); - a.getDefaultWallpaperView().setVisibility(View.INVISIBLE); - v.setTileSource( - new BitmapRegionTileSource(a, file.getAbsolutePath(), 1024, rotation), null); - v.moveToLeft(); - v.setTouchEnabled(false); + BitmapRegionTileSource.FilePathBitmapSource bitmapSource = + new BitmapRegionTileSource.FilePathBitmapSource(file.getAbsolutePath(), 1024); + a.setCropViewTileSource(bitmapSource, false, true, null); } @Override public void onSave(WallpaperPickerActivity a) { diff --git a/src/com/android/launcher3/SmoothPagedView.java b/src/com/android/launcher3/SmoothPagedView.java index a45dbbf6e..64dcb34ca 100644 --- a/src/com/android/launcher3/SmoothPagedView.java +++ b/src/com/android/launcher3/SmoothPagedView.java @@ -52,8 +52,6 @@ public abstract class SmoothPagedView extends PagedView { } public float getInterpolation(float t) { - // _o(t) = t * t * ((tension + 1) * t + tension) - // o(t) = _o(t - 1) + 1 t -= 1.0f; return t * t * ((mTension + 1) * t + mTension) + 1.0f; } diff --git a/src/com/android/launcher3/WallpaperCropActivity.java b/src/com/android/launcher3/WallpaperCropActivity.java index 30ec340b1..f9aec2b08 100644 --- a/src/com/android/launcher3/WallpaperCropActivity.java +++ b/src/com/android/launcher3/WallpaperCropActivity.java @@ -37,15 +37,16 @@ import android.graphics.RectF; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; -import android.util.FloatMath; import android.util.Log; import android.view.Display; import android.view.View; import android.view.WindowManager; +import android.widget.Toast; import com.android.gallery3d.common.Utils; import com.android.gallery3d.exif.ExifInterface; import com.android.photos.BitmapRegionTileSource; +import com.android.photos.BitmapRegionTileSource.BitmapSource; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; @@ -96,9 +97,6 @@ public class WallpaperCropActivity extends Activity { return; } - int rotation = getRotationFromExif(this, imageUri); - mCropView.setTileSource(new BitmapRegionTileSource(this, imageUri, 1024, rotation), null); - mCropView.setTouchEnabled(true); // Action bar // Show the custom action bar view final ActionBar actionBar = getActionBar(); @@ -111,6 +109,63 @@ public class WallpaperCropActivity extends Activity { cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone); } }); + + // Load image in background + final BitmapRegionTileSource.UriBitmapSource bitmapSource = + new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024); + Runnable onLoad = new Runnable() { + public void run() { + if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) { + Toast.makeText(WallpaperCropActivity.this, + getString(R.string.wallpaper_load_fail), + Toast.LENGTH_LONG).show(); + finish(); + } + } + }; + setCropViewTileSource(bitmapSource, true, false, onLoad); + } + + public void setCropViewTileSource( + final BitmapRegionTileSource.BitmapSource bitmapSource, final boolean touchEnabled, + final boolean moveToLeft, final Runnable postExecute) { + final Context context = WallpaperCropActivity.this; + final View progressView = findViewById(R.id.loading); + final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() { + protected Void doInBackground(Void...args) { + if (!isCancelled()) { + bitmapSource.loadInBackground(); + } + return null; + } + protected void onPostExecute(Void arg) { + if (!isCancelled()) { + progressView.setVisibility(View.INVISIBLE); + if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) { + mCropView.setTileSource( + new BitmapRegionTileSource(context, bitmapSource), null); + mCropView.setTouchEnabled(touchEnabled); + if (moveToLeft) { + mCropView.moveToLeft(); + } + } + } + if (postExecute != null) { + postExecute.run(); + } + } + }; + // We don't want to show the spinner every time we load an image, because that would be + // annoying; instead, only start showing the spinner if loading the image has taken + // longer than 1 sec (ie 1000 ms) + progressView.postDelayed(new Runnable() { + public void run() { + if (loadBitmapTask.getStatus() != AsyncTask.Status.FINISHED) { + progressView.setVisibility(View.VISIBLE); + } + } + }, 1000); + loadBitmapTask.execute(); } public boolean enableRotation() { @@ -192,16 +247,18 @@ public class WallpaperCropActivity extends Activity { private static int getRotationFromExifHelper( String path, Resources res, int resId, Context context, Uri uri) { ExifInterface ei = new ExifInterface(); + InputStream is = null; + BufferedInputStream bis = null; try { if (path != null) { ei.readExif(path); } else if (uri != null) { - InputStream is = context.getContentResolver().openInputStream(uri); - BufferedInputStream bis = new BufferedInputStream(is); + is = context.getContentResolver().openInputStream(uri); + bis = new BufferedInputStream(is); ei.readExif(bis); } else { - InputStream is = res.openRawResource(resId); - BufferedInputStream bis = new BufferedInputStream(is); + is = res.openRawResource(resId); + bis = new BufferedInputStream(is); ei.readExif(bis); } Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION); @@ -210,6 +267,9 @@ public class WallpaperCropActivity extends Activity { } } catch (IOException e) { Log.w(LOGTAG, "Getting exif data failed", e); + } finally { + Utils.closeSilently(bis); + Utils.closeSilently(is); } return 0; } @@ -372,7 +432,6 @@ public class WallpaperCropActivity extends Activity { String mInFilePath; byte[] mInImageBytes; int mInResId = 0; - InputStream mInStream; RectF mCropBounds = null; int mOutWidth, mOutHeight; int mRotation; @@ -445,37 +504,36 @@ public class WallpaperCropActivity extends Activity { } // Helper to setup input stream - private void regenerateInputStream() { + private InputStream regenerateInputStream() { if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) { Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " + "image byte array given"); } else { - Utils.closeSilently(mInStream); try { if (mInUri != null) { - mInStream = new BufferedInputStream( + return new BufferedInputStream( mContext.getContentResolver().openInputStream(mInUri)); } else if (mInFilePath != null) { - mInStream = mContext.openFileInput(mInFilePath); + return mContext.openFileInput(mInFilePath); } else if (mInImageBytes != null) { - mInStream = new BufferedInputStream( - new ByteArrayInputStream(mInImageBytes)); + return new BufferedInputStream(new ByteArrayInputStream(mInImageBytes)); } else { - mInStream = new BufferedInputStream( - mResources.openRawResource(mInResId)); + return new BufferedInputStream(mResources.openRawResource(mInResId)); } } catch (FileNotFoundException e) { Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e); } } + return null; } public Point getImageBounds() { - regenerateInputStream(); - if (mInStream != null) { + InputStream is = regenerateInputStream(); + if (is != null) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; - BitmapFactory.decodeStream(mInStream, null, options); + BitmapFactory.decodeStream(is, null, options); + Utils.closeSilently(is); if (options.outWidth != 0 && options.outHeight != 0) { return new Point(options.outWidth, options.outHeight); } @@ -493,22 +551,26 @@ public class WallpaperCropActivity extends Activity { public boolean cropBitmap() { boolean failure = false; - regenerateInputStream(); WallpaperManager wallpaperManager = null; if (mSetWallpaper) { wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext()); } - if (mSetWallpaper && mNoCrop && mInStream != null) { + + + if (mSetWallpaper && mNoCrop) { try { - wallpaperManager.setStream(mInStream); + InputStream is = regenerateInputStream(); + if (is != null) { + wallpaperManager.setStream(is); + Utils.closeSilently(is); + } } catch (IOException e) { Log.w(LOGTAG, "cannot write stream to wallpaper", e); failure = true; } return !failure; - } - if (mInStream != null) { + } else { // Find crop bounds (scaled to original image size) Rect roundedTrueCrop = new Rect(); Matrix rotateMatrix = new Matrix(); @@ -521,6 +583,11 @@ public class WallpaperCropActivity extends Activity { mCropBounds = new RectF(roundedTrueCrop); Point bounds = getImageBounds(); + if (bounds == null) { + Log.w(LOGTAG, "cannot get bounds for image"); + failure = true; + return false; + } float[] rotatedBounds = new float[] { bounds.x, bounds.y }; rotateMatrix.mapPoints(rotatedBounds); @@ -531,7 +598,6 @@ public class WallpaperCropActivity extends Activity { inverseRotateMatrix.mapRect(mCropBounds); mCropBounds.offset(bounds.x/2, bounds.y/2); - regenerateInputStream(); } mCropBounds.roundOut(roundedTrueCrop); @@ -543,15 +609,25 @@ public class WallpaperCropActivity extends Activity { } // See how much we're reducing the size of the image - int scaleDownSampleSize = Math.min(roundedTrueCrop.width() / mOutWidth, - roundedTrueCrop.height() / mOutHeight); - + int scaleDownSampleSize = Math.max(1, Math.min(roundedTrueCrop.width() / mOutWidth, + roundedTrueCrop.height() / mOutHeight)); // Attempt to open a region decoder BitmapRegionDecoder decoder = null; + InputStream is = null; try { - decoder = BitmapRegionDecoder.newInstance(mInStream, true); + is = regenerateInputStream(); + if (is == null) { + Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString()); + failure = true; + return false; + } + decoder = BitmapRegionDecoder.newInstance(is, false); + Utils.closeSilently(is); } catch (IOException e) { Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e); + } finally { + Utils.closeSilently(is); + is = null; } Bitmap crop = null; @@ -567,14 +643,15 @@ public class WallpaperCropActivity extends Activity { if (crop == null) { // BitmapRegionDecoder has failed, try to crop in-memory - regenerateInputStream(); + is = regenerateInputStream(); Bitmap fullSize = null; - if (mInStream != null) { + if (is != null) { BitmapFactory.Options options = new BitmapFactory.Options(); if (scaleDownSampleSize > 1) { options.inSampleSize = scaleDownSampleSize; } - fullSize = BitmapFactory.decodeStream(mInStream, null, options); + fullSize = BitmapFactory.decodeStream(is, null, options); + Utils.closeSilently(is); } if (fullSize != null) { mCropBounds.left /= scaleDownSampleSize; diff --git a/src/com/android/launcher3/WallpaperPickerActivity.java b/src/com/android/launcher3/WallpaperPickerActivity.java index 2ad92181d..efc311070 100644 --- a/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/src/com/android/launcher3/WallpaperPickerActivity.java @@ -42,6 +42,7 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LevelListDrawable; import android.net.Uri; +import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.provider.MediaStore; @@ -66,8 +67,10 @@ import android.widget.HorizontalScrollView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListAdapter; +import android.widget.Toast; import com.android.photos.BitmapRegionTileSource; +import com.android.photos.BitmapRegionTileSource.BitmapSource; import java.io.File; import java.io.FileOutputStream; @@ -83,13 +86,12 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { private static final String TEMP_WALLPAPER_TILES = "TEMP_WALLPAPER_TILES"; private static final String DEFAULT_WALLPAPER_THUMBNAIL_FILENAME = "default_thumb.jpg"; - private View mSelectedThumb; + private View mSelectedTile; private boolean mIgnoreNextTap; private OnClickListener mThumbnailOnClickListener; private LinearLayout mWallpapersView; private View mWallpaperStrip; - private ImageView mDefaultWallpaperView; private ActionMode.Callback mActionModeCallback; private ActionMode mActionMode; @@ -128,16 +130,39 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { public static class UriWallpaperInfo extends WallpaperTileInfo { private Uri mUri; + private boolean mFirstClick = true; + private BitmapRegionTileSource.UriBitmapSource mBitmapSource; public UriWallpaperInfo(Uri uri) { mUri = uri; } @Override - public void onClick(WallpaperPickerActivity a) { - CropView v = a.getCropView(); - int rotation = WallpaperCropActivity.getRotationFromExif(a, mUri); - a.getDefaultWallpaperView().setVisibility(View.INVISIBLE); - v.setTileSource(new BitmapRegionTileSource(a, mUri, 1024, rotation), null); - v.setTouchEnabled(true); + public void onClick(final WallpaperPickerActivity a) { + final Runnable onLoad; + if (!mFirstClick) { + onLoad = null; + } else { + mFirstClick = false; + onLoad = new Runnable() { + public void run() { + if (mBitmapSource != null && + mBitmapSource.getLoadingState() == BitmapSource.State.LOADED) { + mView.setVisibility(View.VISIBLE); + a.selectTile(mView); + } else { + ViewGroup parent = (ViewGroup) mView.getParent(); + if (parent != null) { + parent.removeView(mView); + Toast.makeText(a, + a.getString(R.string.image_load_fail), + Toast.LENGTH_SHORT).show(); + } + } + } + }; + } + mBitmapSource = new BitmapRegionTileSource.UriBitmapSource( + a, mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE); + a.setCropViewTileSource(mBitmapSource, true, false, onLoad); } @Override public void onSave(final WallpaperPickerActivity a) { @@ -175,11 +200,12 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } @Override public void onClick(WallpaperPickerActivity a) { - int rotation = WallpaperCropActivity.getRotationFromExif(mResources, mResId); - BitmapRegionTileSource source = new BitmapRegionTileSource( - mResources, a, mResId, 1024, rotation); + BitmapRegionTileSource.ResourceBitmapSource bitmapSource = + new BitmapRegionTileSource.ResourceBitmapSource( + mResources, mResId, BitmapRegionTileSource.MAX_PREVIEW_SIZE); + bitmapSource.loadInBackground(); + BitmapRegionTileSource source = new BitmapRegionTileSource(a, bitmapSource); CropView v = a.getCropView(); - a.getDefaultWallpaperView().setVisibility(View.INVISIBLE); v.setTileSource(source, null); Point wallpaperSize = WallpaperCropActivity.getDefaultWallpaperSize( a.getResources(), a.getWindowManager()); @@ -211,15 +237,15 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } @Override public void onClick(WallpaperPickerActivity a) { - a.getCropView().setTouchEnabled(false); - ImageView defaultWallpaperView = a.getDefaultWallpaperView(); - defaultWallpaperView.setVisibility(View.VISIBLE); + CropView c = a.getCropView(); + Drawable defaultWallpaper = WallpaperManager.getInstance(a).getBuiltInDrawable( - defaultWallpaperView.getWidth(), defaultWallpaperView.getHeight(), - false, 0.5f, 0.5f); - if (defaultWallpaper != null) { - defaultWallpaperView.setBackgroundDrawable(defaultWallpaper); - } + c.getWidth(), c.getHeight(), false, 0.5f, 0.5f); + + c.setTileSource( + new DrawableTileSource(a, defaultWallpaper, DrawableTileSource.MAX_PREVIEW_SIZE), null); + c.setScale(1f); + c.setTouchEnabled(false); } @Override public void onSave(WallpaperPickerActivity a) { @@ -249,7 +275,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { setContentView(R.layout.wallpaper_picker); mCropView = (CropView) findViewById(R.id.cropView); - mDefaultWallpaperView = (ImageView) findViewById(R.id.defaultWallpaperView); mWallpaperStrip = findViewById(R.id.wallpaper_strip); mCropView.setTouchCallback(new CropView.TouchCallback() { LauncherViewPropertyAnimator mAnim; @@ -307,17 +332,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { return; } WallpaperTileInfo info = (WallpaperTileInfo) v.getTag(); - if (info.isSelectable()) { - if (mSelectedThumb != null) { - mSelectedThumb.setSelected(false); - mSelectedThumb = null; - } - mSelectedThumb = v; - v.setSelected(true); - // TODO: Remove this once the accessibility framework and - // services have better support for selection state. - v.announceForAccessibility( - getString(R.string.announce_selection, v.getContentDescription())); + if (info.isSelectable() && v.getVisibility() == View.VISIBLE) { + selectTile(v); } info.onClick(WallpaperPickerActivity.this); } @@ -410,7 +426,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { // Select the first item; wait for a layout pass so that we initialize the dimensions of // cropView or the defaultWallpaperView first - mDefaultWallpaperView.addOnLayoutChangeListener(new OnLayoutChangeListener() { + mCropView.addOnLayoutChangeListener(new OnLayoutChangeListener() { @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { @@ -443,8 +459,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { new View.OnClickListener() { @Override public void onClick(View v) { - if (mSelectedThumb != null) { - WallpaperTileInfo info = (WallpaperTileInfo) mSelectedThumb.getTag(); + if (mSelectedTile != null) { + WallpaperTileInfo info = (WallpaperTileInfo) mSelectedTile.getTag(); info.onSave(WallpaperPickerActivity.this); } } @@ -523,12 +539,25 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i); c.setChecked(false); } - mSelectedThumb.setSelected(true); + mSelectedTile.setSelected(true); mActionMode = null; } }; } + private void selectTile(View v) { + if (mSelectedTile != null) { + mSelectedTile.setSelected(false); + mSelectedTile = null; + } + mSelectedTile = v; + v.setSelected(true); + // TODO: Remove this once the accessibility framework and + // services have better support for selection state. + v.announceForAccessibility( + getString(R.string.announce_selection, v.getContentDescription())); + } + private void initializeScrollForRtl() { final HorizontalScrollView scroll = (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container); @@ -687,26 +716,35 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } } - private void addTemporaryWallpaperTile(Uri uri) { + private void addTemporaryWallpaperTile(final Uri uri) { mTempWallpaperTiles.add(uri); // Add a tile for the image picked from Gallery - FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater(). + final FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater(). inflate(R.layout.wallpaper_picker_item, mWallpapersView, false); + pickedImageThumbnail.setVisibility(View.GONE); setWallpaperItemPaddingToZero(pickedImageThumbnail); + mWallpapersView.addView(pickedImageThumbnail, 0); // Load the thumbnail - ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image); - Point defaultSize = getDefaultThumbnailSize(this.getResources()); - int rotation = WallpaperCropActivity.getRotationFromExif(this, uri); - Bitmap thumb = createThumbnail(defaultSize, this, uri, null, null, 0, rotation, false); - if (thumb != null) { - image.setImageBitmap(thumb); - Drawable thumbDrawable = image.getDrawable(); - thumbDrawable.setDither(true); - } else { - Log.e(TAG, "Error loading thumbnail for uri=" + uri); - } - mWallpapersView.addView(pickedImageThumbnail, 0); + final ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image); + final Point defaultSize = getDefaultThumbnailSize(this.getResources()); + final Context context = this; + new AsyncTask<Void, Bitmap, Bitmap>() { + protected Bitmap doInBackground(Void...args) { + int rotation = WallpaperCropActivity.getRotationFromExif(context, uri); + return createThumbnail(defaultSize, context, uri, null, null, 0, rotation, false); + + } + protected void onPostExecute(Bitmap thumb) { + if (thumb != null) { + image.setImageBitmap(thumb); + Drawable thumbDrawable = image.getDrawable(); + thumbDrawable.setDither(true); + } else { + Log.e(TAG, "Error loading thumbnail for uri=" + uri); + } + } + }.execute(); UriWallpaperInfo info = new UriWallpaperInfo(uri); pickedImageThumbnail.setTag(info); @@ -886,10 +924,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { return mCropView; } - public ImageView getDefaultWallpaperView() { - return mDefaultWallpaperView; - } - public SavedWallpaperImages getSavedImages() { return mSavedImages; } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index a1353c3c8..d742d429a 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -22,6 +22,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.LayoutTransition; import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; @@ -89,6 +90,9 @@ public class Workspace extends SmoothPagedView private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375; private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100; + protected static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400; + protected static final int FADE_EMPTY_SCREEN_DURATION = 150; + private static final int BACKGROUND_FADE_OUT_DURATION = 350; private static final int ADJACENT_SCREEN_DROP_DURATION = 300; private static final int FLING_THRESHOLD_VELOCITY = 500; @@ -128,6 +132,8 @@ public class Workspace extends SmoothPagedView private HashMap<Long, CellLayout> mWorkspaceScreens = new HashMap<Long, CellLayout>(); private ArrayList<Long> mScreenOrder = new ArrayList<Long>(); + private Runnable mRemoveEmptyScreenRunnable; + /** * CellInfo for the cell that is currently being dragged */ @@ -392,7 +398,6 @@ public class Workspace extends SmoothPagedView InstallShortcutReceiver.disableAndFlushInstallQueue(getContext()); UninstallShortcutReceiver.disableAndFlushUninstallQueue(getContext()); - removeExtraEmptyScreen(); mDragSourceInternal = null; mLauncher.onInteractionEnd(); } @@ -627,6 +632,9 @@ public class Workspace extends SmoothPagedView boolean lastChildOnScreen = false; boolean childOnFinalScreen = false; + // Cancel any pending removal of empty screen + mRemoveEmptyScreenRunnable = null; + if (mDragSourceInternal != null) { if (mDragSourceInternal.getChildCount() == 1) { lastChildOnScreen = true; @@ -654,13 +662,95 @@ public class Workspace extends SmoothPagedView return false; } - public void removeExtraEmptyScreen() { + private void convertFinalScreenToEmptyScreenIfNecessary() { + if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return; + long finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1); + + if (finalScreenId == CUSTOM_CONTENT_SCREEN_ID) return; + CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId); + + // If the final screen is empty, convert it to the extra empty screen + if (finalScreen.getShortcutsAndWidgets().getChildCount() == 0) { + mWorkspaceScreens.remove(finalScreenId); + mScreenOrder.remove(finalScreenId); + + // 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); + } + } + + public void removeExtraEmptyScreen(final boolean animate, final Runnable onComplete) { + removeExtraEmptyScreen(animate, onComplete, 0, false); + } + + public void removeExtraEmptyScreen(final boolean animate, final Runnable onComplete, + final int delay, final boolean stripEmptyScreens) { + if (delay > 0) { + postDelayed(new Runnable() { + @Override + public void run() { + removeExtraEmptyScreen(animate, onComplete, 0, stripEmptyScreens); + } + + }, delay); + return; + } + + convertFinalScreenToEmptyScreenIfNecessary(); if (hasExtraEmptyScreen()) { - CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID); - mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID); - mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID); - removeView(cl); + int emptyIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID); + if (getNextPage() == emptyIndex) { + snapToPage(getNextPage() - 1, SNAP_OFF_EMPTY_SCREEN_DURATION); + fadeAndRemoveEmptyScreen(SNAP_OFF_EMPTY_SCREEN_DURATION, FADE_EMPTY_SCREEN_DURATION, + onComplete, stripEmptyScreens); + } else { + fadeAndRemoveEmptyScreen(0, FADE_EMPTY_SCREEN_DURATION, + onComplete, stripEmptyScreens); + } + return; } + if (onComplete != null) { + onComplete.run(); + } + } + + private void fadeAndRemoveEmptyScreen(int delay, int duration, final Runnable onComplete, + final boolean stripEmptyScreens) { + PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f); + PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", 0f); + + final CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID); + + mRemoveEmptyScreenRunnable = new Runnable() { + @Override + public void run() { + if (hasExtraEmptyScreen()) { + mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID); + mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID); + removeView(cl); + if (stripEmptyScreens) { + stripEmptyScreens(); + } + } + } + }; + + ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(cl, alpha, bgAlpha); + oa.setDuration(duration); + oa.setStartDelay(delay); + oa.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (mRemoveEmptyScreenRunnable != null) { + mRemoveEmptyScreenRunnable.run(); + } + if (onComplete != null) { + onComplete.run(); + } + } + }); + oa.start(); } public boolean hasExtraEmptyScreen() { @@ -753,6 +843,7 @@ public class Workspace extends SmoothPagedView removeView(cl); } else { // if this is the last non-custom content screen, convert it to the empty screen + mRemoveEmptyScreenRunnable = null; mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl); mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID); } @@ -1103,11 +1194,15 @@ public class Workspace extends SmoothPagedView } protected void snapToPage(int whichPage, Runnable r) { + snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION, r); + } + + protected void snapToPage(int whichPage, int duration, Runnable r) { if (mDelayedSnapToPageRunnable != null) { mDelayedSnapToPageRunnable.run(); } mDelayedSnapToPageRunnable = r; - snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION); + snapToPage(whichPage, duration); } protected void snapToScreenId(long screenId, Runnable r) { @@ -2726,13 +2821,13 @@ public class Workspace extends SmoothPagedView // cell also contains a shortcut, then create a folder with the two shortcuts. if (!mInScrollArea && createUserFolderIfNecessary(cell, container, dropTargetLayout, mTargetCell, distance, false, d.dragView, null)) { - stripEmptyScreens(); + removeExtraEmptyScreen(true, null, 0, true); return; } if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell, distance, d, false)) { - stripEmptyScreens(); + removeExtraEmptyScreen(true, null, 0, true); return; } @@ -2840,7 +2935,7 @@ public class Workspace extends SmoothPagedView if (finalResizeRunnable != null) { finalResizeRunnable.run(); } - stripEmptyScreens(); + removeExtraEmptyScreen(true, null, 0, true); } }; mAnimatingViewIntoPlace = true; @@ -3491,7 +3586,13 @@ public class Workspace extends SmoothPagedView final Runnable exitSpringLoadedRunnable = new Runnable() { @Override public void run() { - mLauncher.exitSpringLoadedDragModeDelayed(true, false, null); + removeExtraEmptyScreen(false, new Runnable() { + @Override + public void run() { + mLauncher.exitSpringLoadedDragModeDelayed(true, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); + } + }); } }; @@ -3727,7 +3828,7 @@ public class Workspace extends SmoothPagedView external, scalePreview); Resources res = mLauncher.getResources(); - int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200; + final int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200; // In the case where we've prebound the widget, we remove it from the DragLayer if (finalView instanceof AppWidgetHostView && external) { @@ -3835,11 +3936,11 @@ public class Workspace extends SmoothPagedView final boolean isFlingToDelete, final boolean success) { if (mDeferDropAfterUninstall) { mDeferredAction = new Runnable() { - public void run() { - onDropCompleted(target, d, isFlingToDelete, success); - mDeferredAction = null; - } - }; + public void run() { + onDropCompleted(target, d, isFlingToDelete, success); + mDeferredAction = null; + } + }; return; } @@ -3857,7 +3958,7 @@ public class Workspace extends SmoothPagedView // If we move the item to anything not on the Workspace, check if any empty // screens need to be removed. If we dropped back on the workspace, this will // be done post drop animation. - stripEmptyScreens(); + removeExtraEmptyScreen(true, null, 0, true); } } else if (mDragInfo != null) { CellLayout cellLayout; @@ -4085,6 +4186,7 @@ public class Workspace extends SmoothPagedView } } mRestoredPages.clear(); + mSavedStates = null; } @Override diff --git a/src/com/android/photos/BitmapRegionTileSource.java b/src/com/android/photos/BitmapRegionTileSource.java index 5f6401868..2f203af53 100644 --- a/src/com/android/photos/BitmapRegionTileSource.java +++ b/src/com/android/photos/BitmapRegionTileSource.java @@ -24,6 +24,9 @@ import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.BitmapRegionDecoder; import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PorterDuff; import android.graphics.Rect; import android.net.Uri; import android.os.Build; @@ -31,14 +34,114 @@ import android.os.Build.VERSION_CODES; import android.util.Log; import com.android.gallery3d.common.BitmapUtils; +import com.android.gallery3d.common.Utils; +import com.android.gallery3d.exif.ExifInterface; import com.android.gallery3d.glrenderer.BasicTexture; import com.android.gallery3d.glrenderer.BitmapTexture; import com.android.photos.views.TiledImageRenderer; import java.io.BufferedInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +interface SimpleBitmapRegionDecoder { + int getWidth(); + int getHeight(); + Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options); +} + +class SimpleBitmapRegionDecoderWrapper implements SimpleBitmapRegionDecoder { + BitmapRegionDecoder mDecoder; + private SimpleBitmapRegionDecoderWrapper(BitmapRegionDecoder decoder) { + mDecoder = decoder; + } + public static SimpleBitmapRegionDecoderWrapper newInstance( + String pathName, boolean isShareable) { + try { + BitmapRegionDecoder d = BitmapRegionDecoder.newInstance(pathName, isShareable); + if (d != null) { + return new SimpleBitmapRegionDecoderWrapper(d); + } + } catch (IOException e) { + Log.w("BitmapRegionTileSource", "getting decoder failed for path " + pathName, e); + return null; + } + return null; + } + public static SimpleBitmapRegionDecoderWrapper newInstance( + InputStream is, boolean isShareable) { + try { + BitmapRegionDecoder d = BitmapRegionDecoder.newInstance(is, isShareable); + if (d != null) { + return new SimpleBitmapRegionDecoderWrapper(d); + } + } catch (IOException e) { + Log.w("BitmapRegionTileSource", "getting decoder failed", e); + return null; + } + return null; + } + public int getWidth() { + return mDecoder.getWidth(); + } + public int getHeight() { + return mDecoder.getHeight(); + } + public Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options) { + return mDecoder.decodeRegion(wantRegion, options); + } +} + +class DumbBitmapRegionDecoder implements SimpleBitmapRegionDecoder { + //byte[] mStreamCopy; + Bitmap mBuffer; + Canvas mTempCanvas; + Paint mTempPaint; + private DumbBitmapRegionDecoder(Bitmap b) { + mBuffer = b; + } + public static DumbBitmapRegionDecoder newInstance(String pathName) { + Bitmap b = BitmapFactory.decodeFile(pathName); + if (b != null) { + return new DumbBitmapRegionDecoder(b); + } + return null; + } + public static DumbBitmapRegionDecoder newInstance(InputStream is) { + Bitmap b = BitmapFactory.decodeStream(is); + if (b != null) { + return new DumbBitmapRegionDecoder(b); + } + return null; + } + public int getWidth() { + return mBuffer.getWidth(); + } + public int getHeight() { + return mBuffer.getHeight(); + } + public Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options) { + if (mTempCanvas == null) { + mTempCanvas = new Canvas(); + mTempPaint = new Paint(); + mTempPaint.setFilterBitmap(true); + } + int sampleSize = Math.max(options.inSampleSize, 1); + Bitmap newBitmap = Bitmap.createBitmap( + wantRegion.width() / sampleSize, + wantRegion.height() / sampleSize, + Bitmap.Config.ARGB_8888); + mTempCanvas.setBitmap(newBitmap); + mTempCanvas.save(); + mTempCanvas.scale(1f / sampleSize, 1f / sampleSize); + mTempCanvas.drawBitmap(mBuffer, -wantRegion.left, -wantRegion.top, mTempPaint); + mTempCanvas.restore(); + mTempCanvas.setBitmap(null); + return newBitmap; + } +} + /** * A {@link com.android.photos.views.TiledImageRenderer.TileSource} using * {@link BitmapRegionDecoder} to wrap a local file @@ -53,9 +156,214 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource { private static final int GL_SIZE_LIMIT = 2048; // This must be no larger than half the size of the GL_SIZE_LIMIT // due to decodePreview being allowed to be up to 2x the size of the target - private static final int MAX_PREVIEW_SIZE = 1024; + public static final int MAX_PREVIEW_SIZE = GL_SIZE_LIMIT / 2; + + public static abstract class BitmapSource { + private SimpleBitmapRegionDecoder mDecoder; + private Bitmap mPreview; + private int mPreviewSize; + private int mRotation; + public enum State { NOT_LOADED, LOADED, ERROR_LOADING }; + private State mState = State.NOT_LOADED; + public BitmapSource(int previewSize) { + mPreviewSize = previewSize; + } + public boolean loadInBackground() { + ExifInterface ei = new ExifInterface(); + if (readExif(ei)) { + Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION); + if (ori != null) { + mRotation = ExifInterface.getRotationForOrientationValue(ori.shortValue()); + } + } + mDecoder = loadBitmapRegionDecoder(); + if (mDecoder == null) { + mState = State.ERROR_LOADING; + return false; + } else { + int width = mDecoder.getWidth(); + int height = mDecoder.getHeight(); + if (mPreviewSize != 0) { + int previewSize = Math.min(mPreviewSize, MAX_PREVIEW_SIZE); + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inPreferredConfig = Bitmap.Config.ARGB_8888; + opts.inPreferQualityOverSpeed = true; + + float scale = (float) previewSize / Math.max(width, height); + opts.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale); + opts.inJustDecodeBounds = false; + mPreview = loadPreviewBitmap(opts); + } + mState = State.LOADED; + return true; + } + } - BitmapRegionDecoder mDecoder; + public State getLoadingState() { + return mState; + } + + public SimpleBitmapRegionDecoder getBitmapRegionDecoder() { + return mDecoder; + } + + public Bitmap getPreviewBitmap() { + return mPreview; + } + + public int getPreviewSize() { + return mPreviewSize; + } + + public int getRotation() { + return mRotation; + } + + public abstract boolean readExif(ExifInterface ei); + public abstract SimpleBitmapRegionDecoder loadBitmapRegionDecoder(); + public abstract Bitmap loadPreviewBitmap(BitmapFactory.Options options); + } + + public static class FilePathBitmapSource extends BitmapSource { + private String mPath; + public FilePathBitmapSource(String path, int previewSize) { + super(previewSize); + mPath = path; + } + @Override + public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() { + SimpleBitmapRegionDecoder d; + d = SimpleBitmapRegionDecoderWrapper.newInstance(mPath, true); + if (d == null) { + d = DumbBitmapRegionDecoder.newInstance(mPath); + } + return d; + } + @Override + public Bitmap loadPreviewBitmap(BitmapFactory.Options options) { + return BitmapFactory.decodeFile(mPath, options); + } + @Override + public boolean readExif(ExifInterface ei) { + try { + ei.readExif(mPath); + return true; + } catch (IOException e) { + Log.w("BitmapRegionTileSource", "getting decoder failed", e); + return false; + } + } + } + + public static class UriBitmapSource extends BitmapSource { + private Context mContext; + private Uri mUri; + public UriBitmapSource(Context context, Uri uri, int previewSize) { + super(previewSize); + mContext = context; + mUri = uri; + } + private InputStream regenerateInputStream() throws FileNotFoundException { + InputStream is = mContext.getContentResolver().openInputStream(mUri); + return new BufferedInputStream(is); + } + @Override + public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() { + try { + InputStream is = regenerateInputStream(); + SimpleBitmapRegionDecoder regionDecoder = + SimpleBitmapRegionDecoderWrapper.newInstance(is, false); + Utils.closeSilently(is); + if (regionDecoder == null) { + is = regenerateInputStream(); + regionDecoder = DumbBitmapRegionDecoder.newInstance(is); + Utils.closeSilently(is); + } + return regionDecoder; + } catch (FileNotFoundException e) { + Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e); + return null; + } catch (IOException e) { + Log.e("BitmapRegionTileSource", "Failure while reading URI " + mUri, e); + return null; + } + } + @Override + public Bitmap loadPreviewBitmap(BitmapFactory.Options options) { + try { + InputStream is = regenerateInputStream(); + Bitmap b = BitmapFactory.decodeStream(is, null, options); + Utils.closeSilently(is); + return b; + } catch (FileNotFoundException e) { + Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e); + return null; + } + } + @Override + public boolean readExif(ExifInterface ei) { + InputStream is = null; + try { + is = regenerateInputStream(); + ei.readExif(is); + Utils.closeSilently(is); + return true; + } catch (FileNotFoundException e) { + Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e); + return false; + } catch (IOException e) { + Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e); + return false; + } finally { + Utils.closeSilently(is); + } + } + } + + public static class ResourceBitmapSource extends BitmapSource { + private Resources mRes; + private int mResId; + public ResourceBitmapSource(Resources res, int resId, int previewSize) { + super(previewSize); + mRes = res; + mResId = resId; + } + private InputStream regenerateInputStream() { + InputStream is = mRes.openRawResource(mResId); + return new BufferedInputStream(is); + } + @Override + public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() { + InputStream is = regenerateInputStream(); + SimpleBitmapRegionDecoder regionDecoder = + SimpleBitmapRegionDecoderWrapper.newInstance(is, false); + Utils.closeSilently(is); + if (regionDecoder == null) { + is = regenerateInputStream(); + regionDecoder = DumbBitmapRegionDecoder.newInstance(is); + Utils.closeSilently(is); + } + return regionDecoder; + } + @Override + public Bitmap loadPreviewBitmap(BitmapFactory.Options options) { + return BitmapFactory.decodeResource(mRes, mResId, options); + } + @Override + public boolean readExif(ExifInterface ei) { + try { + InputStream is = regenerateInputStream(); + ei.readExif(is); + Utils.closeSilently(is); + return true; + } catch (IOException e) { + Log.e("BitmapRegionTileSource", "Error reading resource", e); + return false; + } + } + } + + SimpleBitmapRegionDecoder mDecoder; int mWidth; int mHeight; int mTileSize; @@ -68,58 +376,33 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource { private BitmapFactory.Options mOptions; private Canvas mCanvas; - public BitmapRegionTileSource(Context context, String path, int previewSize, int rotation) { - this(null, context, path, null, 0, previewSize, rotation); - } - - public BitmapRegionTileSource(Context context, Uri uri, int previewSize, int rotation) { - this(null, context, null, uri, 0, previewSize, rotation); - } - - public BitmapRegionTileSource(Resources res, - Context context, int resId, int previewSize, int rotation) { - this(res, context, null, null, resId, previewSize, rotation); - } - - private BitmapRegionTileSource(Resources res, - Context context, String path, Uri uri, int resId, int previewSize, int rotation) { + public BitmapRegionTileSource(Context context, BitmapSource source) { mTileSize = TiledImageRenderer.suggestedTileSize(context); - mRotation = rotation; - try { - if (path != null) { - mDecoder = BitmapRegionDecoder.newInstance(path, true); - } else if (uri != null) { - InputStream is = context.getContentResolver().openInputStream(uri); - BufferedInputStream bis = new BufferedInputStream(is); - mDecoder = BitmapRegionDecoder.newInstance(bis, true); - } else { - InputStream is = res.openRawResource(resId); - BufferedInputStream bis = new BufferedInputStream(is); - mDecoder = BitmapRegionDecoder.newInstance(bis, true); - } + mRotation = source.getRotation(); + mDecoder = source.getBitmapRegionDecoder(); + if (mDecoder != null) { mWidth = mDecoder.getWidth(); mHeight = mDecoder.getHeight(); - } catch (IOException e) { - Log.w("BitmapRegionTileSource", "ctor failed", e); - } - mOptions = new BitmapFactory.Options(); - mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888; - mOptions.inPreferQualityOverSpeed = true; - mOptions.inTempStorage = new byte[16 * 1024]; - if (previewSize != 0) { - previewSize = Math.min(previewSize, MAX_PREVIEW_SIZE); - // Although this is the same size as the Bitmap that is likely already - // loaded, the lifecycle is different and interactions are on a different - // thread. Thus to simplify, this source will decode its own bitmap. - Bitmap preview = decodePreview(res, context, path, uri, resId, previewSize); - if (preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) { - mPreview = new BitmapTexture(preview); - } else { - Log.w(TAG, String.format( - "Failed to create preview of apropriate size! " - + " in: %dx%d, out: %dx%d", - mWidth, mHeight, - preview.getWidth(), preview.getHeight())); + mOptions = new BitmapFactory.Options(); + mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888; + mOptions.inPreferQualityOverSpeed = true; + mOptions.inTempStorage = new byte[16 * 1024]; + int previewSize = source.getPreviewSize(); + if (previewSize != 0) { + previewSize = Math.min(previewSize, MAX_PREVIEW_SIZE); + // Although this is the same size as the Bitmap that is likely already + // loaded, the lifecycle is different and interactions are on a different + // thread. Thus to simplify, this source will decode its own bitmap. + Bitmap preview = decodePreview(source, previewSize); + if (preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) { + mPreview = new BitmapTexture(preview); + } else { + Log.w(TAG, String.format( + "Failed to create preview of apropriate size! " + + " in: %dx%d, out: %dx%d", + mWidth, mHeight, + preview.getWidth(), preview.getHeight())); + } } } } @@ -215,33 +498,15 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource { * Note that the returned bitmap may have a long edge that's longer * than the targetSize, but it will always be less than 2x the targetSize */ - private Bitmap decodePreview( - Resources res, Context context, String file, Uri uri, int resId, int targetSize) { - float scale = (float) targetSize / Math.max(mWidth, mHeight); - mOptions.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale); - mOptions.inJustDecodeBounds = false; - - Bitmap result = null; - if (file != null) { - result = BitmapFactory.decodeFile(file, mOptions); - } else if (uri != null) { - try { - InputStream is = context.getContentResolver().openInputStream(uri); - BufferedInputStream bis = new BufferedInputStream(is); - result = BitmapFactory.decodeStream(bis, null, mOptions); - } catch (IOException e) { - Log.w("BitmapRegionTileSource", "getting preview failed", e); - } - } else { - result = BitmapFactory.decodeResource(res, resId, mOptions); - } + private Bitmap decodePreview(BitmapSource source, int targetSize) { + Bitmap result = source.getPreviewBitmap(); if (result == null) { return null; } // We need to resize down if the decoder does not support inSampleSize // or didn't support the specified inSampleSize (some decoders only do powers of 2) - scale = (float) targetSize / (float) (Math.max(result.getWidth(), result.getHeight())); + float scale = (float) targetSize / (float) (Math.max(result.getWidth(), result.getHeight())); if (scale <= 0.5) { result = BitmapUtils.resizeBitmapByScale(result, scale, true); diff --git a/update_system_wallpaper_cropper.py b/update_system_wallpaper_cropper.py new file mode 100644 index 000000000..5d24f6ffe --- /dev/null +++ b/update_system_wallpaper_cropper.py @@ -0,0 +1,55 @@ +# This script is used to push the most up-to-date files from +# Launcher into frameworks' version of the WallpaperCropActivity +# (and supporting files) +# The framework versions have some small modifications that are +# necessary so do this with care +import os +import sys +files = """ +src/android/util/Pools.java +src/com/android/gallery3d/util/IntArray.java +src/com/android/gallery3d/common/Utils.java +src/com/android/gallery3d/exif/ByteBufferInputStream.java +src/com/android/gallery3d/exif/CountedDataInputStream.java +src/com/android/gallery3d/exif/ExifData.java +src/com/android/gallery3d/exif/ExifInterface.java +src/com/android/gallery3d/exif/ExifInvalidFormatException.java +src/com/android/gallery3d/exif/ExifModifier.java +src/com/android/gallery3d/exif/ExifOutputStream.java +src/com/android/gallery3d/exif/ExifParser.java +src/com/android/gallery3d/exif/ExifReader.java +src/com/android/gallery3d/exif/ExifTag.java +src/com/android/gallery3d/exif/IfdData.java +src/com/android/gallery3d/exif/IfdId.java +src/com/android/gallery3d/exif/JpegHeader.java +src/com/android/gallery3d/exif/OrderedDataOutputStream.java +src/com/android/gallery3d/exif/Rational.java +src/com/android/gallery3d/glrenderer/BasicTexture.java +src/com/android/gallery3d/glrenderer/BitmapTexture.java +src/com/android/gallery3d/glrenderer/GLCanvas.java +src/com/android/gallery3d/glrenderer/GLES20Canvas.java +src/com/android/gallery3d/glrenderer/GLES20IdImpl.java +src/com/android/gallery3d/glrenderer/GLId.java +src/com/android/gallery3d/glrenderer/GLPaint.java +src/com/android/gallery3d/glrenderer/RawTexture.java +src/com/android/gallery3d/glrenderer/Texture.java +src/com/android/gallery3d/glrenderer/UploadedTexture.java +src/com/android/photos/BitmapRegionTileSource.java +src/com/android/photos/views/BlockingGLTextureView.java +src/com/android/photos/views/TiledImageRenderer.java +src/com/android/photos/views/TiledImageView.java +src/com/android/gallery3d/common/BitmapUtils.java +src/com/android/launcher3/CropView.java +src/com/android/launcher3/WallpaperCropActivity.java +""" + +if len(sys.argv) != 2: + print "Usage: python update_sytem_wallpaper_cropper.py <framework_dir>" + exit() +framework_dir = sys.argv[1] + "/packages/WallpaperCropper" +for file_path in files.split(): + dir = os.path.dirname(file_path) + dir = dir.replace("launcher3", "wallpapercropper") + cmd = 'cp %s %s/%s' % (file_path, framework_dir, dir) + print cmd + os.system(cmd) |