summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk6
-rw-r--r--AndroidManifest.xml4
-rw-r--r--proguard.flags9
-rw-r--r--res/anim/all_apps_zoom_in.xml33
-rw-r--r--res/anim/all_apps_zoom_out.xml33
-rw-r--r--res/anim/home_customization_drawer_slide_down.xml22
-rw-r--r--res/anim/home_customization_drawer_slide_up.xml22
-rw-r--r--res/anim/paged_view_click_feedback.xml23
-rw-r--r--res/drawable-hdpi/add_button.pngbin0 -> 20921 bytes
-rw-r--r--res/drawable-hdpi/default_widget_preview.9.pngbin0 -> 12701 bytes
-rw-r--r--res/drawable-hdpi/pressed_application_background.9.pngbin0 -> 46116 bytes
-rw-r--r--res/drawable-hdpi/rounded_rect_green.9.pngbin0 -> 6854 bytes
-rw-r--r--res/drawable-hdpi/rounded_rect_red.9.pngbin0 -> 6854 bytes
-rw-r--r--res/drawable-xlarge/all_apps_button.xml20
-rw-r--r--res/drawable-xlarge/all_apps_button_normal.pngbin0 -> 698 bytes
-rw-r--r--res/drawable-xlarge/all_apps_button_pressed.pngbin0 -> 3606 bytes
-rw-r--r--res/drawable-xlarge/app_market_generic.pngbin0 -> 2750 bytes
-rw-r--r--res/drawable-xlarge/configure_button.xml20
-rw-r--r--res/drawable-xlarge/configure_button_normal.pngbin0 -> 416 bytes
-rw-r--r--res/drawable-xlarge/configure_button_pressed.pngbin0 -> 2748 bytes
-rw-r--r--res/drawable-xlarge/glow_wallpaper.pngbin0 -> 782067 bytes
-rw-r--r--res/drawable-xlarge/glow_wallpaper_small.pngbin0 -> 11585 bytes
-rw-r--r--res/drawable-xlarge/home_screen_bg.9.pngbin0 -> 557 bytes
-rw-r--r--res/drawable-xlarge/info_button.pngbin0 -> 2315 bytes
-rw-r--r--res/drawable-xlarge/mini_home_screen_bg.9.pngbin0 -> 7288 bytes
-rw-r--r--res/drawable-xlarge/mini_home_screen_bg_hover.9.pngbin0 -> 7069 bytes
-rw-r--r--res/drawable-xlarge/rotate_button.xml20
-rw-r--r--res/drawable-xlarge/rotate_button_normal.pngbin0 -> 6252 bytes
-rw-r--r--res/drawable-xlarge/rotate_button_pressed.pngbin0 -> 6388 bytes
-rw-r--r--res/drawable-xlarge/search_button.xml20
-rw-r--r--res/drawable-xlarge/search_button_normal.pngbin0 -> 1067 bytes
-rw-r--r--res/drawable-xlarge/search_button_pressed.pngbin0 -> 3706 bytes
-rw-r--r--res/drawable-xlarge/trashcan.pngbin0 -> 906 bytes
-rw-r--r--res/drawable-xlarge/trashcan_hover.pngbin0 -> 542 bytes
-rw-r--r--res/drawable/default_widget_preview.9.pngbin0 -> 634 bytes
-rw-r--r--res/drawable/rounded_rect_green.9.pngbin0 -> 361 bytes
-rw-r--r--res/drawable/rounded_rect_red.9.pngbin0 -> 342 bytes
-rw-r--r--res/drawable/widget_divider.pngbin0 -> 115 bytes
-rw-r--r--res/layout-land/all_apps_2d.xml2
-rw-r--r--res/layout-land/application.xml2
-rw-r--r--res/layout-land/launcher.xml6
-rw-r--r--res/layout-land/workspace_screen.xml10
-rw-r--r--res/layout-port/all_apps_2d.xml2
-rw-r--r--res/layout-port/application.xml6
-rw-r--r--res/layout-port/launcher.xml6
-rw-r--r--res/layout-port/workspace_screen.xml10
-rw-r--r--res/layout-xlarge-land/all_apps_tabbed.xml49
-rw-r--r--res/layout-xlarge-land/application.xml18
-rw-r--r--res/layout-xlarge-land/customization_drawer.xml28
-rw-r--r--res/layout-xlarge-land/workspace_screen.xml30
-rw-r--r--res/layout-xlarge-port/all_apps_tabbed.xml49
-rw-r--r--res/layout-xlarge-port/application.xml18
-rw-r--r--res/layout-xlarge-port/customization_drawer.xml28
-rw-r--r--res/layout-xlarge-port/workspace_screen.xml30
-rw-r--r--res/layout-xlarge/all_apps_paged_view_application.xml40
-rw-r--r--res/layout-xlarge/customize_paged_view_item.xml40
-rw-r--r--res/layout-xlarge/customize_paged_view_wallpaper_placeholder.xml36
-rw-r--r--res/layout-xlarge/customize_paged_view_widget.xml80
-rw-r--r--res/layout-xlarge/launcher.xml152
-rw-r--r--res/layout/home_customization_drawer_item.xml24
-rw-r--r--res/layout/home_customization_drawer_widget.xml24
-rw-r--r--res/raw/allapps.rs427
-rw-r--r--res/values-cs/strings.xml21
-rw-r--r--res/values-da/strings.xml21
-rw-r--r--res/values-de/strings.xml21
-rw-r--r--res/values-el/strings.xml21
-rw-r--r--res/values-es-rUS/strings.xml21
-rw-r--r--res/values-es/strings.xml21
-rw-r--r--res/values-fr/strings.xml21
-rw-r--r--res/values-it/strings.xml21
-rw-r--r--res/values-ja/strings.xml21
-rw-r--r--res/values-ko/strings.xml21
-rw-r--r--res/values-nb/strings.xml21
-rw-r--r--res/values-nl/strings.xml21
-rw-r--r--res/values-pl/strings.xml21
-rw-r--r--res/values-pt-rPT/strings.xml21
-rw-r--r--res/values-pt/strings.xml21
-rw-r--r--res/values-rm/strings.xml97
-rw-r--r--res/values-ru/strings.xml21
-rw-r--r--res/values-sv/strings.xml21
-rw-r--r--res/values-tr/strings.xml21
-rw-r--r--res/values-xlarge/colors.xml22
-rw-r--r--res/values-xlarge/config.xml29
-rw-r--r--res/values-xlarge/dimens.xml31
-rw-r--r--res/values-xlarge/styles.xml35
-rw-r--r--res/values-xlarge/wallpapers.xml22
-rw-r--r--res/values-zh-rCN/strings.xml21
-rw-r--r--res/values-zh-rTW/strings.xml21
-rw-r--r--res/values/attrs.xml59
-rw-r--r--res/values/config.xml1
-rw-r--r--res/values/dimens.xml4
-rw-r--r--res/values/strings.xml46
-rw-r--r--res/values/styles.xml5
-rw-r--r--src/com/android/launcher2/AllApps2D.java85
-rw-r--r--src/com/android/launcher2/AllApps3D.java464
-rw-r--r--src/com/android/launcher2/AllAppsList.java13
-rw-r--r--src/com/android/launcher2/AllAppsPagedView.java518
-rw-r--r--src/com/android/launcher2/AllAppsTabbed.java201
-rw-r--r--src/com/android/launcher2/AllAppsView.java2
-rw-r--r--src/com/android/launcher2/ApplicationInfo.java36
-rw-r--r--src/com/android/launcher2/ApplicationInfoDropTarget.java144
-rw-r--r--src/com/android/launcher2/BubbleTextView.java6
-rw-r--r--src/com/android/launcher2/CellLayout.java1315
-rw-r--r--src/com/android/launcher2/CustomizePagedView.java852
-rw-r--r--src/com/android/launcher2/DeferredHandler.java5
-rw-r--r--src/com/android/launcher2/DeleteZone.java92
-rw-r--r--src/com/android/launcher2/Dimmable.java6
-rw-r--r--src/com/android/launcher2/DimmableAppWidgetHostView.java115
-rw-r--r--src/com/android/launcher2/DimmableBubbleTextView.java98
-rw-r--r--src/com/android/launcher2/DragController.java152
-rw-r--r--src/com/android/launcher2/DragLayer.java6
-rw-r--r--src/com/android/launcher2/DragView.java50
-rw-r--r--src/com/android/launcher2/DropTarget.java40
-rw-r--r--src/com/android/launcher2/FastBitmapDrawable.java5
-rw-r--r--src/com/android/launcher2/Folder.java6
-rw-r--r--src/com/android/launcher2/FolderIcon.java21
-rw-r--r--src/com/android/launcher2/HolographicOutlineHelper.java116
-rw-r--r--src/com/android/launcher2/InstallShortcutReceiver.java49
-rw-r--r--src/com/android/launcher2/ItemInfo.java5
-rw-r--r--src/com/android/launcher2/Launcher.java1212
-rw-r--r--src/com/android/launcher2/LauncherAppWidgetHost.java8
-rw-r--r--src/com/android/launcher2/LauncherAppWidgetInfo.java30
-rw-r--r--src/com/android/launcher2/LauncherApplication.java12
-rw-r--r--src/com/android/launcher2/LauncherModel.java142
-rw-r--r--src/com/android/launcher2/LiveFolderInfo.java1
-rw-r--r--src/com/android/launcher2/PagedView.java1194
-rw-r--r--src/com/android/launcher2/PagedViewCellLayout.java445
-rw-r--r--src/com/android/launcher2/PagedViewIcon.java232
-rw-r--r--src/com/android/launcher2/PagedViewWidgetIcon.java156
-rw-r--r--src/com/android/launcher2/PendingAddItemInfo.java29
-rw-r--r--src/com/android/launcher2/ShortcutInfo.java6
-rw-r--r--src/com/android/launcher2/ShortcutsAdapter.java5
-rw-r--r--src/com/android/launcher2/SmoothPagedView.java193
-rw-r--r--src/com/android/launcher2/SymmetricalLinearTween.java2
-rw-r--r--src/com/android/launcher2/UserFolder.java17
-rw-r--r--src/com/android/launcher2/Utilities.java13
-rw-r--r--src/com/android/launcher2/Workspace.java1766
-rw-r--r--src/com/android/launcher2/allapps.rs390
138 files changed, 9692 insertions, 2683 deletions
diff --git a/Android.mk b/Android.mk
index dc72ec6db..dfe6788f6 100644
--- a/Android.mk
+++ b/Android.mk
@@ -14,6 +14,8 @@
# limitations under the License.
#
+ifneq ($(TARGET_SIMULATOR),true)
+
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
@@ -21,7 +23,7 @@ LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_JAVA_LIBRARIES := android-common
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SRC_FILES := $(call all-subdir-java-files) $(call all-renderscript-files-under, src)
LOCAL_PACKAGE_NAME := Launcher2
LOCAL_CERTIFICATE := shared
@@ -31,3 +33,5 @@ LOCAL_OVERRIDES_PACKAGES := Home
LOCAL_PROGUARD_FLAGS := -include $(LOCAL_PATH)/proguard.flags
include $(BUILD_PACKAGE)
+
+endif
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 84ee59909..4aa5d68b6 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -66,7 +66,8 @@
android:name="com.android.launcher2.LauncherApplication"
android:process="@string/process"
android:label="@string/application_name"
- android:icon="@drawable/ic_launcher_home">
+ android:icon="@drawable/ic_launcher_home"
+ android:hardwareAccelerated="@bool/config_hardwareAccelerated">
<activity
android:name="com.android.launcher2.Launcher"
@@ -74,7 +75,6 @@
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:theme="@style/Theme"
- android:screenOrientation="nosensor"
android:windowSoftInputMode="stateUnspecified|adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/proguard.flags b/proguard.flags
index 1187fd8a2..82411eb85 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -2,6 +2,15 @@
public void previousScreen(android.view.View);
public void nextScreen(android.view.View);
public void launchHotSeat(android.view.View);
+ public void onClickSearchButton(android.view.View);
+ public void onClickConfigureButton(android.view.View);
+ public void onClickAllAppsButton(android.view.View);
+ public void onClickAppMarketButton(android.view.View);
+}
+
+-keep class com.android.launcher2.CellLayout {
+ public float getBackgroundAlpha();
+ public void setBackgroundAlpha(float);
}
-keep class com.android.launcher2.AllApps3D$Defines {
diff --git a/res/anim/all_apps_zoom_in.xml b/res/anim/all_apps_zoom_in.xml
new file mode 100644
index 000000000..644d1cf4a
--- /dev/null
+++ b/res/anim/all_apps_zoom_in.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:shareInterpolator="true">
+ <translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromXDelta="0%p"
+ android:toXDelta="0%p"
+ android:fromYDelta="-20%p"
+ android:toYDelta="0%p"
+ android:duration="500" />
+ <scale
+ android:fromXScale="5.0"
+ android:toXScale="1.0"
+ android:fromYScale="5.0"
+ android:toYScale="1.0"
+ android:pivotX="50%"
+ android:pivotY="100%"
+ android:duration="500" />
+</set>
diff --git a/res/anim/all_apps_zoom_out.xml b/res/anim/all_apps_zoom_out.xml
new file mode 100644
index 000000000..23a8712e0
--- /dev/null
+++ b/res/anim/all_apps_zoom_out.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_interpolator"
+ android:shareInterpolator="true">
+ <translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromXDelta="0%p"
+ android:toXDelta="0%p"
+ android:fromYDelta="0%p"
+ android:toYDelta="-20%p"
+ android:duration="500" />
+ <scale
+ android:fromXScale="1.0"
+ android:toXScale="5.0"
+ android:fromYScale="1.0"
+ android:toYScale="5.0"
+ android:pivotX="50%"
+ android:pivotY="100%"
+ android:duration="500" />
+</set>
diff --git a/res/anim/home_customization_drawer_slide_down.xml b/res/anim/home_customization_drawer_slide_down.xml
new file mode 100644
index 000000000..b3041c79d
--- /dev/null
+++ b/res/anim/home_customization_drawer_slide_down.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromXDelta="0%p"
+ android:toXDelta="0%p"
+ android:fromYDelta="0%p"
+ android:toYDelta="100%p"
+
+ android:duration="500" />
diff --git a/res/anim/home_customization_drawer_slide_up.xml b/res/anim/home_customization_drawer_slide_up.xml
new file mode 100644
index 000000000..3df23209e
--- /dev/null
+++ b/res/anim/home_customization_drawer_slide_up.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromXDelta="0%p"
+ android:toXDelta="0%p"
+ android:fromYDelta="100%p"
+ android:toYDelta="0%p"
+
+ android:duration="500" />
diff --git a/res/anim/paged_view_click_feedback.xml b/res/anim/paged_view_click_feedback.xml
new file mode 100644
index 000000000..786d9744a
--- /dev/null
+++ b/res/anim/paged_view_click_feedback.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.65"
+ android:duration="100"
+ android:fillAfter="true"
+ android:repeatCount="1"
+ android:repeatMode="reverse" />
diff --git a/res/drawable-hdpi/add_button.png b/res/drawable-hdpi/add_button.png
new file mode 100644
index 000000000..7e60d8127
--- /dev/null
+++ b/res/drawable-hdpi/add_button.png
Binary files differ
diff --git a/res/drawable-hdpi/default_widget_preview.9.png b/res/drawable-hdpi/default_widget_preview.9.png
new file mode 100644
index 000000000..6596f0706
--- /dev/null
+++ b/res/drawable-hdpi/default_widget_preview.9.png
Binary files differ
diff --git a/res/drawable-hdpi/pressed_application_background.9.png b/res/drawable-hdpi/pressed_application_background.9.png
new file mode 100644
index 000000000..a6cbe9441
--- /dev/null
+++ b/res/drawable-hdpi/pressed_application_background.9.png
Binary files differ
diff --git a/res/drawable-hdpi/rounded_rect_green.9.png b/res/drawable-hdpi/rounded_rect_green.9.png
new file mode 100644
index 000000000..a846cbdc5
--- /dev/null
+++ b/res/drawable-hdpi/rounded_rect_green.9.png
Binary files differ
diff --git a/res/drawable-hdpi/rounded_rect_red.9.png b/res/drawable-hdpi/rounded_rect_red.9.png
new file mode 100644
index 000000000..21fd62c87
--- /dev/null
+++ b/res/drawable-hdpi/rounded_rect_red.9.png
Binary files differ
diff --git a/res/drawable-xlarge/all_apps_button.xml b/res/drawable-xlarge/all_apps_button.xml
new file mode 100644
index 000000000..46bc63203
--- /dev/null
+++ b/res/drawable-xlarge/all_apps_button.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:drawable="@drawable/all_apps_button_pressed" />
+ <item android:drawable="@drawable/all_apps_button_normal" />
+</selector>
diff --git a/res/drawable-xlarge/all_apps_button_normal.png b/res/drawable-xlarge/all_apps_button_normal.png
new file mode 100644
index 000000000..e1c8b7df3
--- /dev/null
+++ b/res/drawable-xlarge/all_apps_button_normal.png
Binary files differ
diff --git a/res/drawable-xlarge/all_apps_button_pressed.png b/res/drawable-xlarge/all_apps_button_pressed.png
new file mode 100644
index 000000000..9dcfaa56b
--- /dev/null
+++ b/res/drawable-xlarge/all_apps_button_pressed.png
Binary files differ
diff --git a/res/drawable-xlarge/app_market_generic.png b/res/drawable-xlarge/app_market_generic.png
new file mode 100644
index 000000000..0ceaeefde
--- /dev/null
+++ b/res/drawable-xlarge/app_market_generic.png
Binary files differ
diff --git a/res/drawable-xlarge/configure_button.xml b/res/drawable-xlarge/configure_button.xml
new file mode 100644
index 000000000..ac8729034
--- /dev/null
+++ b/res/drawable-xlarge/configure_button.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:drawable="@drawable/configure_button_pressed" />
+ <item android:drawable="@drawable/configure_button_normal" />
+</selector>
diff --git a/res/drawable-xlarge/configure_button_normal.png b/res/drawable-xlarge/configure_button_normal.png
new file mode 100644
index 000000000..b11c2a804
--- /dev/null
+++ b/res/drawable-xlarge/configure_button_normal.png
Binary files differ
diff --git a/res/drawable-xlarge/configure_button_pressed.png b/res/drawable-xlarge/configure_button_pressed.png
new file mode 100644
index 000000000..70db9b798
--- /dev/null
+++ b/res/drawable-xlarge/configure_button_pressed.png
Binary files differ
diff --git a/res/drawable-xlarge/glow_wallpaper.png b/res/drawable-xlarge/glow_wallpaper.png
new file mode 100644
index 000000000..eb294493c
--- /dev/null
+++ b/res/drawable-xlarge/glow_wallpaper.png
Binary files differ
diff --git a/res/drawable-xlarge/glow_wallpaper_small.png b/res/drawable-xlarge/glow_wallpaper_small.png
new file mode 100644
index 000000000..f83fc60ab
--- /dev/null
+++ b/res/drawable-xlarge/glow_wallpaper_small.png
Binary files differ
diff --git a/res/drawable-xlarge/home_screen_bg.9.png b/res/drawable-xlarge/home_screen_bg.9.png
new file mode 100644
index 000000000..d939d5c1b
--- /dev/null
+++ b/res/drawable-xlarge/home_screen_bg.9.png
Binary files differ
diff --git a/res/drawable-xlarge/info_button.png b/res/drawable-xlarge/info_button.png
new file mode 100644
index 000000000..253cdf74b
--- /dev/null
+++ b/res/drawable-xlarge/info_button.png
Binary files differ
diff --git a/res/drawable-xlarge/mini_home_screen_bg.9.png b/res/drawable-xlarge/mini_home_screen_bg.9.png
new file mode 100644
index 000000000..ecbbcb44c
--- /dev/null
+++ b/res/drawable-xlarge/mini_home_screen_bg.9.png
Binary files differ
diff --git a/res/drawable-xlarge/mini_home_screen_bg_hover.9.png b/res/drawable-xlarge/mini_home_screen_bg_hover.9.png
new file mode 100644
index 000000000..48af56494
--- /dev/null
+++ b/res/drawable-xlarge/mini_home_screen_bg_hover.9.png
Binary files differ
diff --git a/res/drawable-xlarge/rotate_button.xml b/res/drawable-xlarge/rotate_button.xml
new file mode 100644
index 000000000..c29efa446
--- /dev/null
+++ b/res/drawable-xlarge/rotate_button.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:drawable="@drawable/rotate_button_pressed" />
+ <item android:drawable="@drawable/rotate_button_normal" />
+</selector>
diff --git a/res/drawable-xlarge/rotate_button_normal.png b/res/drawable-xlarge/rotate_button_normal.png
new file mode 100644
index 000000000..fd014aaa1
--- /dev/null
+++ b/res/drawable-xlarge/rotate_button_normal.png
Binary files differ
diff --git a/res/drawable-xlarge/rotate_button_pressed.png b/res/drawable-xlarge/rotate_button_pressed.png
new file mode 100644
index 000000000..29fb8d919
--- /dev/null
+++ b/res/drawable-xlarge/rotate_button_pressed.png
Binary files differ
diff --git a/res/drawable-xlarge/search_button.xml b/res/drawable-xlarge/search_button.xml
new file mode 100644
index 000000000..8f18e677e
--- /dev/null
+++ b/res/drawable-xlarge/search_button.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:drawable="@drawable/search_button_pressed" />
+ <item android:drawable="@drawable/search_button_normal" />
+</selector>
diff --git a/res/drawable-xlarge/search_button_normal.png b/res/drawable-xlarge/search_button_normal.png
new file mode 100644
index 000000000..b172074d2
--- /dev/null
+++ b/res/drawable-xlarge/search_button_normal.png
Binary files differ
diff --git a/res/drawable-xlarge/search_button_pressed.png b/res/drawable-xlarge/search_button_pressed.png
new file mode 100644
index 000000000..c2926c8bc
--- /dev/null
+++ b/res/drawable-xlarge/search_button_pressed.png
Binary files differ
diff --git a/res/drawable-xlarge/trashcan.png b/res/drawable-xlarge/trashcan.png
new file mode 100644
index 000000000..839d4b854
--- /dev/null
+++ b/res/drawable-xlarge/trashcan.png
Binary files differ
diff --git a/res/drawable-xlarge/trashcan_hover.png b/res/drawable-xlarge/trashcan_hover.png
new file mode 100644
index 000000000..cc2fde7d5
--- /dev/null
+++ b/res/drawable-xlarge/trashcan_hover.png
Binary files differ
diff --git a/res/drawable/default_widget_preview.9.png b/res/drawable/default_widget_preview.9.png
new file mode 100644
index 000000000..5fedcc881
--- /dev/null
+++ b/res/drawable/default_widget_preview.9.png
Binary files differ
diff --git a/res/drawable/rounded_rect_green.9.png b/res/drawable/rounded_rect_green.9.png
new file mode 100644
index 000000000..5787c3dc2
--- /dev/null
+++ b/res/drawable/rounded_rect_green.9.png
Binary files differ
diff --git a/res/drawable/rounded_rect_red.9.png b/res/drawable/rounded_rect_red.9.png
new file mode 100644
index 000000000..7a5fdeebc
--- /dev/null
+++ b/res/drawable/rounded_rect_red.9.png
Binary files differ
diff --git a/res/drawable/widget_divider.png b/res/drawable/widget_divider.png
new file mode 100644
index 000000000..1b453c5fa
--- /dev/null
+++ b/res/drawable/widget_divider.png
Binary files differ
diff --git a/res/layout-land/all_apps_2d.xml b/res/layout-land/all_apps_2d.xml
index a253b937b..b7fcd4585 100644
--- a/res/layout-land/all_apps_2d.xml
+++ b/res/layout-land/all_apps_2d.xml
@@ -22,7 +22,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="2dip"
- >
+ android:background="#FF000000">
<GridView android:id="@+id/all_apps_2d_grid"
android:tag="all_apps_2d_grid"
diff --git a/res/layout-land/application.xml b/res/layout-land/application.xml
index 6de565878..6e8c31e11 100644
--- a/res/layout-land/application.xml
+++ b/res/layout-land/application.xml
@@ -15,4 +15,4 @@
-->
<com.android.launcher2.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
- style="@style/WorkspaceIcon.Landscape" />
+ style="@style/WorkspaceIcon.Landscape" />
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 1f13f1fb3..8d38a3d61 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -24,14 +24,16 @@
<include layout="@layout/all_apps" />
- <!-- The workspace contains 3 screens of cells -->
+ <!-- The workspace contains 5 screens of cells -->
<com.android.launcher2.Workspace
android:id="@+id/workspace"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="horizontal"
android:fadeScrollbars="true"
- launcher:defaultScreen="2">
+ launcher:defaultScreen="2"
+ launcher:cellCountX="4"
+ launcher:cellCountY="4">
<include android:id="@+id/cell1" layout="@layout/workspace_screen" />
<include android:id="@+id/cell2" layout="@layout/workspace_screen" />
diff --git a/res/layout-land/workspace_screen.xml b/res/layout-land/workspace_screen.xml
index 315e68b05..9323f58ac 100644
--- a/res/layout-land/workspace_screen.xml
+++ b/res/layout-land/workspace_screen.xml
@@ -24,9 +24,7 @@
launcher:cellWidth="@dimen/workspace_cell_width"
launcher:cellHeight="@dimen/workspace_cell_height"
- launcher:longAxisStartPadding="65dip"
- launcher:longAxisEndPadding="65dip"
- launcher:shortAxisStartPadding="0dip"
- launcher:shortAxisEndPadding="0dip"
- launcher:shortAxisCells="4"
- launcher:longAxisCells="4" />
+ launcher:xAxisStartPadding="65dip"
+ launcher:xAxisEndPadding="65dip"
+ launcher:yAxisStartPadding="0dip"
+ launcher:yAxisEndPadding="0dip"/>
diff --git a/res/layout-port/all_apps_2d.xml b/res/layout-port/all_apps_2d.xml
index 0607d62cc..081cba292 100644
--- a/res/layout-port/all_apps_2d.xml
+++ b/res/layout-port/all_apps_2d.xml
@@ -22,7 +22,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="2dip"
- >
+ android:background="#FF000000">
<GridView android:id="@+id/all_apps_2d_grid"
android:tag="all_apps_2d_grid"
diff --git a/res/layout-port/application.xml b/res/layout-port/application.xml
index f904a661f..32c1510d9 100644
--- a/res/layout-port/application.xml
+++ b/res/layout-port/application.xml
@@ -4,9 +4,9 @@
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.
@@ -15,4 +15,4 @@
-->
<com.android.launcher2.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
- style="@style/WorkspaceIcon.Portrait" />
+ style="@style/WorkspaceIcon.Portrait" />
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index 8dc509279..c50dbca93 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -24,12 +24,14 @@
<include layout="@layout/all_apps" />
- <!-- The workspace contains 3 screens of cells -->
+ <!-- The workspace contains 5 screens of cells -->
<com.android.launcher2.Workspace
android:id="@+id/workspace"
android:layout_width="match_parent"
android:layout_height="match_parent"
- launcher:defaultScreen="2">
+ launcher:defaultScreen="2"
+ launcher:cellCountX="4"
+ launcher:cellCountY="4">
<include android:id="@+id/cell1" layout="@layout/workspace_screen" />
<include android:id="@+id/cell2" layout="@layout/workspace_screen" />
diff --git a/res/layout-port/workspace_screen.xml b/res/layout-port/workspace_screen.xml
index 96df91be1..f400c40b1 100644
--- a/res/layout-port/workspace_screen.xml
+++ b/res/layout-port/workspace_screen.xml
@@ -24,9 +24,7 @@
launcher:cellWidth="@dimen/workspace_cell_width"
launcher:cellHeight="@dimen/workspace_cell_height"
- launcher:longAxisStartPadding="8dip"
- launcher:longAxisEndPadding="@dimen/button_bar_height"
- launcher:shortAxisStartPadding="0dip"
- launcher:shortAxisEndPadding="0dip"
- launcher:shortAxisCells="4"
- launcher:longAxisCells="4" />
+ launcher:yAxisStartPadding="8dip"
+ launcher:yAxisEndPadding="@dimen/button_bar_height"
+ launcher:xAxisStartPadding="0dip"
+ launcher:xAxisEndPadding="0dip" />
diff --git a/res/layout-xlarge-land/all_apps_tabbed.xml b/res/layout-xlarge-land/all_apps_tabbed.xml
new file mode 100644
index 000000000..4f573f8a8
--- /dev/null
+++ b/res/layout-xlarge-land/all_apps_tabbed.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher2.AllAppsTabbed
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
+ android:background="#30000000"
+ android:paddingTop="?android:attr/actionBarSize">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TabWidget
+ android:id="@android:id/tabs"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:tabStripEnabled="false"
+ android:paddingBottom="10dp" />
+ <FrameLayout
+ android:id="@android:id/tabcontent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <com.android.launcher2.AllAppsPagedView
+ android:id="@+id/all_apps_paged_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ launcher:cellCountX="7"
+ launcher:cellCountY="4"
+ launcher:pageLayoutPaddingTop="10dp"
+ launcher:pageLayoutPaddingBottom="15dp"
+ launcher:pageLayoutPaddingLeft="20dp"
+ launcher:pageLayoutPaddingRight="20dp">
+ </com.android.launcher2.AllAppsPagedView>
+ </FrameLayout>
+ </LinearLayout>
+</com.android.launcher2.AllAppsTabbed>
diff --git a/res/layout-xlarge-land/application.xml b/res/layout-xlarge-land/application.xml
new file mode 100644
index 000000000..d0c8d9bf6
--- /dev/null
+++ b/res/layout-xlarge-land/application.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.launcher2.DimmableBubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/WorkspaceIcon.Landscape" /> \ No newline at end of file
diff --git a/res/layout-xlarge-land/customization_drawer.xml b/res/layout-xlarge-land/customization_drawer.xml
new file mode 100644
index 000000000..7ad913236
--- /dev/null
+++ b/res/layout-xlarge-land/customization_drawer.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher2.CustomizePagedView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
+
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ launcher:widgetCellCountX="16"
+ launcher:cellCountX="7"
+ launcher:cellCountY="4"
+ launcher:pageLayoutPaddingTop="10dp"
+ launcher:pageLayoutPaddingBottom="15dp"
+ launcher:pageLayoutPaddingLeft="20dp"
+ launcher:pageLayoutPaddingRight="20dp" /> \ No newline at end of file
diff --git a/res/layout-xlarge-land/workspace_screen.xml b/res/layout-xlarge-land/workspace_screen.xml
new file mode 100644
index 000000000..f35844371
--- /dev/null
+++ b/res/layout-xlarge-land/workspace_screen.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.launcher2.CellLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
+
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:hapticFeedbackEnabled="false"
+
+ launcher:cellWidth="@dimen/workspace_cell_width"
+ launcher:cellHeight="@dimen/workspace_cell_height"
+ launcher:yAxisStartPadding="40dip"
+ launcher:yAxisEndPadding="40dip"
+ launcher:xAxisStartPadding="40dip"
+ launcher:xAxisEndPadding="40dip" />
diff --git a/res/layout-xlarge-port/all_apps_tabbed.xml b/res/layout-xlarge-port/all_apps_tabbed.xml
new file mode 100644
index 000000000..2ff044792
--- /dev/null
+++ b/res/layout-xlarge-port/all_apps_tabbed.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher2.AllAppsTabbed
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
+ android:background="#30000000"
+ android:paddingTop="?android:attr/actionBarSize">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TabWidget
+ android:id="@android:id/tabs"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:tabStripEnabled="false"
+ android:paddingBottom="10dp" />
+ <FrameLayout
+ android:id="@android:id/tabcontent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <com.android.launcher2.AllAppsPagedView
+ android:id="@+id/all_apps_paged_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ launcher:cellCountX="7"
+ launcher:cellCountY="4"
+ launcher:pageLayoutPaddingTop="10dp"
+ launcher:pageLayoutPaddingBottom="10dp"
+ launcher:pageLayoutPaddingLeft="0dp"
+ launcher:pageLayoutPaddingRight="0dp">
+ </com.android.launcher2.AllAppsPagedView>
+ </FrameLayout>
+ </LinearLayout>
+</com.android.launcher2.AllAppsTabbed>
diff --git a/res/layout-xlarge-port/application.xml b/res/layout-xlarge-port/application.xml
new file mode 100644
index 000000000..ec6697658
--- /dev/null
+++ b/res/layout-xlarge-port/application.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.launcher2.DimmableBubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/WorkspaceIcon.Portrait" />
diff --git a/res/layout-xlarge-port/customization_drawer.xml b/res/layout-xlarge-port/customization_drawer.xml
new file mode 100644
index 000000000..db264b228
--- /dev/null
+++ b/res/layout-xlarge-port/customization_drawer.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher2.CustomizePagedView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
+
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ launcher:widgetCellCountX="12"
+ launcher:cellCountX="7"
+ launcher:cellCountY="4"
+ launcher:pageLayoutPaddingTop="10dp"
+ launcher:pageLayoutPaddingBottom="15dp"
+ launcher:pageLayoutPaddingLeft="0dp"
+ launcher:pageLayoutPaddingRight="0dp" /> \ No newline at end of file
diff --git a/res/layout-xlarge-port/workspace_screen.xml b/res/layout-xlarge-port/workspace_screen.xml
new file mode 100644
index 000000000..7314e6046
--- /dev/null
+++ b/res/layout-xlarge-port/workspace_screen.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.launcher2.CellLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
+
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:hapticFeedbackEnabled="false"
+
+ launcher:cellWidth="@dimen/workspace_cell_width"
+ launcher:cellHeight="@dimen/workspace_cell_height"
+ launcher:yAxisStartPadding="40dip"
+ launcher:yAxisEndPadding="40dip"
+ launcher:xAxisStartPadding="40dip"
+ launcher:xAxisEndPadding="40dip"/>
diff --git a/res/layout-xlarge/all_apps_paged_view_application.xml b/res/layout-xlarge/all_apps_paged_view_application.xml
new file mode 100644
index 000000000..8e53e0656
--- /dev/null
+++ b/res/layout-xlarge/all_apps_paged_view_application.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.launcher2.PagedViewIcon
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
+
+ launcher:blurColor="#FF6B8CF0"
+ launcher:outlineColor="#FF8CD2FF"
+ launcher:checkedBlurColor="#FFBBE83C"
+ launcher:checkedOutlineColor="#FF8CD2FF"
+
+ android:id="@+id/application_icon"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:paddingTop="2dip"
+
+ android:textColor="#FFFFFFFF"
+ android:textSize="14sp"
+ android:shadowColor="#FF000000"
+ android:shadowDx="0.0"
+ android:shadowDy="1.0"
+ android:shadowRadius="1.0"
+
+ android:maxLines="2"
+ android:fadingEdge="horizontal" />
diff --git a/res/layout-xlarge/customize_paged_view_item.xml b/res/layout-xlarge/customize_paged_view_item.xml
new file mode 100644
index 000000000..e42734506
--- /dev/null
+++ b/res/layout-xlarge/customize_paged_view_item.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.launcher2.PagedViewIcon
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
+
+ launcher:blurColor="#FF6B8CF0"
+ launcher:outlineColor="#FF8CD2FF"
+ launcher:checkedBlurColor="#FFBBE83C"
+ launcher:checkedOutlineColor="#FF8CD2FF"
+
+ android:id="@+id/customize_icon"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:paddingTop="2dip"
+
+ android:textColor="#FFFFFFFF"
+ android:textSize="14sp"
+ android:shadowColor="#FF000000"
+ android:shadowDx="0.0"
+ android:shadowDy="1.0"
+ android:shadowRadius="1.0"
+
+ android:maxLines="2"
+ android:fadingEdge="horizontal" />
diff --git a/res/layout-xlarge/customize_paged_view_wallpaper_placeholder.xml b/res/layout-xlarge/customize_paged_view_wallpaper_placeholder.xml
new file mode 100644
index 000000000..6112532db
--- /dev/null
+++ b/res/layout-xlarge/customize_paged_view_wallpaper_placeholder.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+
+ android:id="@+id/wallpaper_icon"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:paddingTop="2dip"
+
+ android:textColor="#FFFFFFFF"
+ android:textSize="14sp"
+ android:shadowColor="#FF000000"
+ android:shadowDx="0.0"
+ android:shadowDy="1.0"
+ android:shadowRadius="1.0"
+ android:drawableLeft="@drawable/ic_launcher_wallpaper"
+ android:drawablePadding="10dip"
+
+ android:maxLines="2"
+ android:fadingEdge="horizontal" />
diff --git a/res/layout-xlarge/customize_paged_view_widget.xml b/res/layout-xlarge/customize_paged_view_widget.xml
new file mode 100644
index 000000000..e8f4ac904
--- /dev/null
+++ b/res/layout-xlarge/customize_paged_view_widget.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher2.PagedViewWidgetIcon
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
+
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingLeft="12.5dp"
+ android:paddingRight="12.5dp"
+ android:paddingTop="40dp"
+ android:paddingBottom="50dp"
+ android:gravity="top"
+ android:orientation="vertical"
+
+ launcher:checkedBlurColor="#FFDAFF71"
+ launcher:checkedOutlineColor="#FFCFFF9C">
+
+ <!-- The icon of the widget. -->
+ <ImageView
+ android:id="@+id/widget_preview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1.0"
+ android:scaleType="fitStart" />
+
+ <!-- The divider image. -->
+ <ImageView
+ android:id="@+id/divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="10dp"
+ android:paddingBottom="10dp"
+ android:src="@drawable/widget_divider" />
+
+ <!-- The name of the widget. -->
+ <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/widget_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+
+ android:textColor="#FFFFFFFF"
+ android:textSize="14sp"
+ android:shadowColor="#FF000000"
+ android:shadowDx="0.0"
+ android:shadowDy="1.0"
+ android:shadowRadius="1.0"
+
+ android:maxLines="2"
+ android:fadingEdge="horizontal" />
+
+ <!-- The original dimensions of the widget (can't be the same text as above due to different
+ style. -->
+ <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/widget_dims"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+
+ android:textColor="#FF999999"
+ android:textSize="14sp"
+ android:shadowColor="#99000000"
+ android:shadowDx="0.0"
+ android:shadowDy="1.0"
+ android:shadowRadius="1.0" />
+</com.android.launcher2.PagedViewWidgetIcon>
diff --git a/res/layout-xlarge/launcher.xml b/res/layout-xlarge/launcher.xml
new file mode 100644
index 000000000..9ed2dc534
--- /dev/null
+++ b/res/layout-xlarge/launcher.xml
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.launcher2.DragLayer
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
+
+ android:id="@+id/drag_layer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <include
+ layout="@layout/all_apps_tabbed"
+ android:id="@+id/all_apps_view"
+ android:layout_width="match_parent"
+ android:layout_height="550dip"
+ android:layout_gravity="top"/>
+
+ <!-- The workspace contains 5 screens of cells -->
+ <com.android.launcher2.Workspace
+ android:id="@+id/workspace"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="?android:attr/actionBarSize"
+ launcher:defaultScreen="2"
+ launcher:cellCountX="8"
+ launcher:cellCountY="7">
+
+ <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
+ <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
+ <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
+ <include android:id="@+id/cell4" layout="@layout/workspace_screen" />
+ <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
+ </com.android.launcher2.Workspace>
+
+ <RelativeLayout
+ android:id="@+id/all_apps_button_cluster"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:layout_gravity="top">
+
+ <ImageView
+ android:id="@+id/search_button"
+ android:src="@drawable/search_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="left"
+ android:layout_marginLeft="@dimen/toolbar_button_spacing"
+
+ android:onClick="onClickSearchButton"
+ android:focusable="true"
+ android:clickable="true"/>
+
+ <ImageView
+ android:id="@+id/all_apps_button"
+ android:src="@drawable/all_apps_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentRight="true"
+ android:layout_marginLeft="@dimen/toolbar_button_spacing"
+ android:layout_marginRight="@dimen/toolbar_button_spacing"
+
+ android:onClick="onClickAllAppsButton"
+ android:focusable="true"
+ android:clickable="true" />
+
+ <!-- The button to bring up the installed app market.
+ The icon for this button will be dynamically set. -->
+ <ImageView
+ android:id="@+id/market_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignLeft="@id/all_apps_button"
+
+ android:onClick="onClickAppMarketButton"
+ android:focusable="false"
+ android:clickable="false"
+ android:visibility="gone"/>
+
+ <ImageView
+ android:id="@+id/configure_button"
+ android:src="@drawable/configure_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_toLeftOf="@id/all_apps_button"
+ android:layout_marginLeft="@dimen/toolbar_button_spacing"
+
+ android:onClick="onClickConfigureButton"
+ android:focusable="true"
+ android:clickable="true" />
+
+ <com.android.launcher2.DeleteZone
+ android:id="@+id/delete_zone"
+ android:src="@drawable/delete_zone_selector"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignLeft="@id/all_apps_button"
+
+ android:visibility="gone"
+ launcher:direction="horizontal" />
+
+ <com.android.launcher2.ApplicationInfoDropTarget
+ android:id="@+id/info_button"
+ android:src="@drawable/info_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignLeft="@id/configure_button"
+
+ android:visibility="gone"
+ android:focusable="true"
+ android:clickable="true" />
+
+ </RelativeLayout>
+
+ <TabHost
+ android:id="@android:id/tabhost"
+ android:layout_width="match_parent"
+ android:layout_height="500dip"
+ android:layout_gravity="bottom">
+ <LinearLayout
+ android:orientation="vertical"
+ android:background="#40000000"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TabWidget
+ android:id="@android:id/tabs"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:tabStripEnabled="false"
+ android:paddingBottom="10dp" />
+ <FrameLayout
+ android:id="@android:id/tabcontent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ </FrameLayout>
+ </LinearLayout>
+ </TabHost>
+</com.android.launcher2.DragLayer>
diff --git a/res/layout/home_customization_drawer_item.xml b/res/layout/home_customization_drawer_item.xml
new file mode 100644
index 000000000..4d61dbfb4
--- /dev/null
+++ b/res/layout/home_customization_drawer_item.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="200dip"
+ android:layout_height="200dip"
+ android:padding="20dip"
+ android:orientation="vertical"
+ android:gravity="center_horizontal|center_vertical|clip_vertical"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="#ffffffff" /> \ No newline at end of file
diff --git a/res/layout/home_customization_drawer_widget.xml b/res/layout/home_customization_drawer_widget.xml
new file mode 100644
index 000000000..1581308b6
--- /dev/null
+++ b/res/layout/home_customization_drawer_widget.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="600dip"
+ android:layout_height="match_parent"
+ android:padding="20dip"
+ android:orientation="vertical"
+ android:gravity="center_horizontal|center_vertical|clip_vertical"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="#ffffffff" /> \ No newline at end of file
diff --git a/res/raw/allapps.rs b/res/raw/allapps.rs
deleted file mode 100644
index af5abd311..000000000
--- a/res/raw/allapps.rs
+++ /dev/null
@@ -1,427 +0,0 @@
-#pragma version(1)
-#pragma stateVertex(PV)
-#pragma stateFragment(PFTexNearest)
-#pragma stateStore(PSIcons)
-
-#define PI 3.14159f
-
-int g_SpecialHWWar;
-
-// Attraction to center values from page edge to page center.
-float g_AttractionTable[9];
-float g_FrictionTable[9];
-float g_PhysicsTableSize;
-
-float g_PosPage;
-float g_PosVelocity;
-float g_LastPositionX;
-int g_LastTouchDown;
-float g_DT;
-int g_LastTime;
-int g_PosMax;
-float g_Zoom;
-float g_Animation;
-float g_OldPosPage;
-float g_OldPosVelocity;
-float g_OldZoom;
-float g_MoveToTotalTime;
-float g_MoveToTime;
-float g_MoveToOldPos;
-
-int g_Cols;
-int g_Rows;
-
-// Drawing constants, should be parameters ======
-#define VIEW_ANGLE 1.28700222f
-
-int g_DrawLastFrame;
-int lastFrame(int draw) {
- // We draw one extra frame to work around the last frame post bug.
- // We also need to track if we drew the last frame to deal with large DT
- // in the physics.
- int ret = g_DrawLastFrame | draw;
- g_DrawLastFrame = draw;
- return ret; // should return draw instead.
-}
-
-void updateReadback() {
- if ((g_OldPosPage != g_PosPage) ||
- (g_OldPosVelocity != g_PosVelocity) ||
- (g_OldZoom != g_Zoom)) {
-
- g_OldPosPage = g_PosPage;
- g_OldPosVelocity = g_PosVelocity;
- g_OldZoom = g_Zoom;
-
- int i[3];
- i[0] = g_PosPage * (1 << 16);
- i[1] = g_PosVelocity * (1 << 16);
- i[2] = g_OldZoom * (1 << 16);
- sendToClient(&i[0], 1, 12, 1);
- }
-}
-
-void setColor(float r, float g, float b, float a) {
- if (g_SpecialHWWar) {
- color(0, 0, 0, 0.001f);
- } else {
- color(r, g, b, a);
- }
-}
-
-void init() {
- g_AttractionTable[0] = 20.0f;
- g_AttractionTable[1] = 20.0f;
- g_AttractionTable[2] = 20.0f;
- g_AttractionTable[3] = 10.0f;
- g_AttractionTable[4] = -10.0f;
- g_AttractionTable[5] = -20.0f;
- g_AttractionTable[6] = -20.0f;
- g_AttractionTable[7] = -20.0f;
- g_AttractionTable[8] = -20.0f; // dup 7 to avoid a clamp later
- g_FrictionTable[0] = 10.0f;
- g_FrictionTable[1] = 10.0f;
- g_FrictionTable[2] = 11.0f;
- g_FrictionTable[3] = 15.0f;
- g_FrictionTable[4] = 15.0f;
- g_FrictionTable[5] = 11.0f;
- g_FrictionTable[6] = 10.0f;
- g_FrictionTable[7] = 10.0f;
- g_FrictionTable[8] = 10.0f; // dup 7 to avoid a clamp later
- g_PhysicsTableSize = 7;
-
- g_PosVelocity = 0;
- g_PosPage = 0;
- g_LastTouchDown = 0;
- g_LastPositionX = 0;
- g_Zoom = 0;
- g_Animation = 1.f;
- g_SpecialHWWar = 1;
- g_MoveToTime = 0;
- g_MoveToOldPos = 0;
- g_MoveToTotalTime = 0.2f; // Duration of scrolling 1 line
-}
-
-void resetHWWar() {
- g_SpecialHWWar = 1;
-}
-
-void move() {
- if (g_LastTouchDown) {
- float dx = -(state->newPositionX - g_LastPositionX);
- g_PosVelocity = 0;
- g_PosPage += dx * 5.2f;
-
- float pmin = -0.49f;
- float pmax = g_PosMax + 0.49f;
- g_PosPage = clampf(g_PosPage, pmin, pmax);
- }
- g_LastTouchDown = state->newTouchDown;
- g_LastPositionX = state->newPositionX;
- g_MoveToTime = 0;
- //debugF("Move P", g_PosPage);
-}
-
-void moveTo() {
- g_MoveToTime = g_MoveToTotalTime;
- g_PosVelocity = 0;
- g_MoveToOldPos = g_PosPage;
-
- // debugF("======= moveTo", state->targetPos);
-}
-
-void setZoom() {
- g_Zoom = state->zoomTarget;
- g_DrawLastFrame = 1;
- updateReadback();
-}
-
-void fling() {
- g_LastTouchDown = 0;
- g_PosVelocity = -state->flingVelocity * 4;
- float av = fabsf(g_PosVelocity);
- float minVel = 3.5f;
-
- minVel *= 1.f - (fabsf(fracf(g_PosPage + 0.5f) - 0.5f) * 0.45f);
-
- if (av < minVel && av > 0.2f) {
- if (g_PosVelocity > 0) {
- g_PosVelocity = minVel;
- } else {
- g_PosVelocity = -minVel;
- }
- }
-
- if (g_PosPage <= 0) {
- g_PosVelocity = maxf(0, g_PosVelocity);
- }
- if (g_PosPage > g_PosMax) {
- g_PosVelocity = minf(0, g_PosVelocity);
- }
-}
-
-float
-modf(float x, float y)
-{
- return x-(y*floorf(x/y));
-}
-
-
-/*
- * Interpolates values in the range 0..1 to a curve that eases in
- * and out.
- */
-float
-getInterpolation(float input) {
- return (cosf((input + 1) * PI) / 2.0f) + 0.5f;
-}
-
-
-void updatePos() {
- if (g_LastTouchDown) {
- return;
- }
-
- float tablePosNorm = fracf(g_PosPage + 0.5f);
- float tablePosF = tablePosNorm * g_PhysicsTableSize;
- int tablePosI = tablePosF;
- float tablePosFrac = tablePosF - tablePosI;
- float accel = lerpf(g_AttractionTable[tablePosI],
- g_AttractionTable[tablePosI + 1],
- tablePosFrac) * g_DT;
- float friction = lerpf(g_FrictionTable[tablePosI],
- g_FrictionTable[tablePosI + 1],
- tablePosFrac) * g_DT;
-
- if (g_MoveToTime) {
- // New position is old posiition + (total distance) * (interpolated time)
- g_PosPage = g_MoveToOldPos + (state->targetPos - g_MoveToOldPos) * getInterpolation((g_MoveToTotalTime - g_MoveToTime) / g_MoveToTotalTime);
- g_MoveToTime -= g_DT;
- if (g_MoveToTime <= 0) {
- g_MoveToTime = 0;
- g_PosPage = state->targetPos;
- }
- return;
- }
-
- // If our velocity is low OR acceleration is opposing it, apply it.
- if (fabsf(g_PosVelocity) < 4.0f || (g_PosVelocity * accel) < 0) {
- g_PosVelocity += accel;
- }
- //debugF("g_PosPage", g_PosPage);
- //debugF(" g_PosVelocity", g_PosVelocity);
- //debugF(" friction", friction);
- //debugF(" accel", accel);
-
- // Normal physics
- if (g_PosVelocity > 0) {
- g_PosVelocity -= friction;
- g_PosVelocity = maxf(g_PosVelocity, 0);
- } else {
- g_PosVelocity += friction;
- g_PosVelocity = minf(g_PosVelocity, 0);
- }
-
- if ((friction > fabsf(g_PosVelocity)) && (friction > fabsf(accel))) {
- // Special get back to center and overcome friction physics.
- float t = tablePosNorm - 0.5f;
- if (fabsf(t) < (friction * g_DT)) {
- // really close, just snap
- g_PosPage = roundf(g_PosPage);
- g_PosVelocity = 0;
- } else {
- if (t > 0) {
- g_PosVelocity = -friction;
- } else {
- g_PosVelocity = friction;
- }
- }
- }
-
- // Check for out of boundry conditions.
- if (g_PosPage < 0 && g_PosVelocity < 0) {
- float damp = 1.0 + (g_PosPage * 4);
- damp = clampf(damp, 0.f, 0.9f);
- g_PosVelocity *= damp;
- }
- if (g_PosPage > g_PosMax && g_PosVelocity > 0) {
- float damp = 1.0 - ((g_PosPage - g_PosMax) * 4);
- damp = clampf(damp, 0.f, 0.9f);
- g_PosVelocity *= damp;
- }
-
- g_PosPage += g_PosVelocity * g_DT;
- g_PosPage = clampf(g_PosPage, -0.49, g_PosMax + 0.49);
-}
-
-
-void
-draw_home_button()
-{
- setColor(1.0f, 1.0f, 1.0f, 1.0f);
- bindTexture(NAMED_PFTexNearest, 0, state->homeButtonId);
-
- float w = getWidth();
- float h = getHeight();
-
- float x;
- float y;
- if (getWidth() > getHeight()) {
- x = w - (params->homeButtonTextureWidth * (1 - g_Animation)) + 20;
- y = (h - params->homeButtonTextureHeight) * 0.5f;
- } else {
- x = (w - params->homeButtonTextureWidth) / 2;
- y = -g_Animation * params->homeButtonTextureHeight;
- y -= 30; // move the house to the edge of the screen as it doesn't fill the texture.
- }
-
- drawSpriteScreenspace(x, y, 0, params->homeButtonTextureWidth, params->homeButtonTextureHeight);
-}
-
-void drawFrontGrid(float rowOffset, float p)
-{
- float h = getHeight();
- float w = getWidth();
-
- int intRowOffset = rowOffset;
- float rowFrac = rowOffset - intRowOffset;
- float colWidth = 120.f;//getWidth() / 4;
- float rowHeight = colWidth + 25.f;
- float yoff = 0.5f * h + 1.5f * rowHeight;
-
- int row, col;
- int colCount = 4;
- if (w > h) {
- colCount = 6;
- rowHeight -= 12.f;
- yoff = 0.47f * h + 1.0f * rowHeight;
- }
-
- int iconNum = (intRowOffset - 5) * colCount;
-
-
- bindProgramVertex(NAMED_PVCurve);
-
- vpConstants->Position.z = p;
-
- setColor(1.0f, 1.0f, 1.0f, 1.0f);
- for (row = -5; row < 15; row++) {
- float y = yoff - ((-rowFrac + row) * rowHeight);
-
- for (col=0; col < colCount; col++) {
- if (iconNum >= state->iconCount) {
- return;
- }
-
- if (iconNum >= 0) {
- float x = colWidth * col + (colWidth / 2);
- vpConstants->Position.x = x + 0.2f;
-
- if (state->selectedIconIndex == iconNum && !p) {
- bindProgramFragment(NAMED_PFTexNearest);
- bindTexture(NAMED_PFTexNearest, 0, state->selectedIconTexture);
- vpConstants->ImgSize.x = SELECTION_TEXTURE_WIDTH_PX;
- vpConstants->ImgSize.y = SELECTION_TEXTURE_HEIGHT_PX;
- vpConstants->Position.y = y - (SELECTION_TEXTURE_HEIGHT_PX - ICON_TEXTURE_HEIGHT_PX) * 0.5f;
- drawSimpleMesh(NAMED_SMCell);
- }
-
- bindProgramFragment(NAMED_PFTexMip);
- vpConstants->ImgSize.x = ICON_TEXTURE_WIDTH_PX;
- vpConstants->ImgSize.y = ICON_TEXTURE_HEIGHT_PX;
- vpConstants->Position.y = y - 0.2f;
- bindTexture(NAMED_PFTexMip, 0, loadI32(ALLOC_ICON_IDS, iconNum));
- drawSimpleMesh(NAMED_SMCell);
-
- bindProgramFragment(NAMED_PFTexMipAlpha);
- vpConstants->ImgSize.x = 120.f;
- vpConstants->ImgSize.y = 64.f;
- vpConstants->Position.y = y - 64.f - 0.2f;
- bindTexture(NAMED_PFTexMipAlpha, 0, loadI32(ALLOC_LABEL_IDS, iconNum));
- drawSimpleMesh(NAMED_SMCell);
- }
- iconNum++;
- }
- }
-}
-
-
-int
-main(int launchID)
-{
- // Compute dt in seconds.
- int newTime = uptimeMillis();
- g_DT = (newTime - g_LastTime) / 1000.f;
- g_LastTime = newTime;
-
- if (!g_DrawLastFrame) {
- // If we stopped rendering we cannot use DT.
- // assume 30fps in this case.
- g_DT = 0.033f;
- }
- // physics may break if DT is large.
- g_DT = minf(g_DT, 0.2f);
-
- if (g_Zoom != state->zoomTarget) {
- float dz = g_DT * 1.7f;
- if (state->zoomTarget < 0.5f) {
- dz = -dz;
- }
- if (fabsf(g_Zoom - state->zoomTarget) < fabsf(dz)) {
- g_Zoom = state->zoomTarget;
- } else {
- g_Zoom += dz;
- }
- updateReadback();
- }
- g_Animation = powf(1-g_Zoom, 3);
-
- // Set clear value to dim the background based on the zoom position.
- if ((g_Zoom < 0.001f) && (state->zoomTarget < 0.001f) && !g_SpecialHWWar) {
- pfClearColor(0.0f, 0.0f, 0.0f, 0.0f);
- // When we're zoomed out and not tracking motion events, reset the pos to 0.
- if (!g_LastTouchDown) {
- g_PosPage = 0;
- }
- return lastFrame(0);
- } else {
- pfClearColor(0.0f, 0.0f, 0.0f, g_Zoom);
- }
-
- // icons & labels
- int iconCount = state->iconCount;
- if (getWidth() > getHeight()) {
- g_Cols = 6;
- g_Rows = 3;
- } else {
- g_Cols = 4;
- g_Rows = 4;
- }
- g_PosMax = ((iconCount + (g_Cols-1)) / g_Cols) - g_Rows;
- if (g_PosMax < 0) g_PosMax = 0;
-
- updatePos();
- updateReadback();
-
- //debugF(" draw g_PosPage", g_PosPage);
-
- // Draw the icons ========================================
- drawFrontGrid(g_PosPage, g_Animation);
-
- bindProgramFragment(NAMED_PFTexNearest);
- draw_home_button();
-
- // This is a WAR to do a rendering pass without drawing during init to
- // force the driver to preload and compile its shaders.
- // Without this the first animation does not appear due to the time it
- // takes to init the driver state.
- if (g_SpecialHWWar) {
- g_SpecialHWWar = 0;
- return 1;
- }
-
- // Bug workaround where the last frame is not always displayed
- // So we keep rendering until the bug is fixed.
- return lastFrame((g_PosVelocity != 0) || fracf(g_PosPage) || g_Zoom != state->zoomTarget || (g_MoveToTime != 0));
-}
-
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index bad273061..b127e1ea7 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"Tapety"</string>
<string name="activity_not_found" msgid="3571057450431950427">"Aplikace není v telefonu nainstalována."</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"Konfigurace..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"Widgety"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"Složky"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"Tapety"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Toto bude karta Tapety"</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"Vše"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"Aplikace"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"Hry"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"Stažené"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"Název složky"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"Přejmenovat složku"</string>
<string name="rename_action" msgid="6016003384693240896">"OK"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"Oznámení"</string>
<string name="menu_gestures" msgid="514678675575912237">"Gesta"</string>
<string name="menu_settings" msgid="6233960148378443661">"Nastavení"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"instalovat zástupce"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"Povoluje aplikaci přidat zástupce bez zásahu uživatele."</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"odinstalovat zástupce"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"zápis nastavení a odkazů plochy"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"Povoluje aplikaci změnit nastavení a odkazy plochy."</string>
<string name="gadget_error_text" msgid="8359351016167075858">"Problém s načtením widgetu"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"Toto je systémová aplikace a nelze ji odinstalovat."</string>
</resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 6ec15168e..c3b069ef0 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"Tapeter"</string>
<string name="activity_not_found" msgid="3571057450431950427">"Programmet er ikke installeret på din telefon."</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"Konfigurer ..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"Widgets"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"Mapper"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"Tapeter"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Dette er fanen for tapeter"</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"Alle"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"Programmer"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"Spil"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"Downloadet"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"Mappenavn"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"Omdøb mappe"</string>
<string name="rename_action" msgid="6016003384693240896">"OK"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"Meddelelser"</string>
<string name="menu_gestures" msgid="514678675575912237">"Gestus"</string>
<string name="menu_settings" msgid="6233960148378443661">"Indstillinger"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"installer genveje"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"Tillader, at et program tilføjer genveje uden brugerindgriben."</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"afinstaller genveje"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"skriv indstillinger og genveje for Start"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"Tillader, at et program ændrer indstillingerne og genvejene i Start."</string>
<string name="gadget_error_text" msgid="8359351016167075858">"Der er problemer med indlæsning af widget"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"Dette er et systemprogram, som ikke kan afinstalleres."</string>
</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index e4f705e7b..6b5401f0b 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"Hintergrundbilder"</string>
<string name="activity_not_found" msgid="3571057450431950427">"Anwendung ist nicht auf dem Telefon installiert."</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"Konfigurieren..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"Widgets"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"Ordner"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"Hintergründe"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Dies ist der Tab \"Hintergründe\""</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"Alle"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"Anwendungen"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"Spiele"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"Heruntergeladen"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"Ordnername"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"Ordner umbenennen"</string>
<string name="rename_action" msgid="6016003384693240896">"OK"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"Benachrichtigungen"</string>
<string name="menu_gestures" msgid="514678675575912237">"Bewegungen"</string>
<string name="menu_settings" msgid="6233960148378443661">"Einstellungen"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"Verknüpfungen installieren"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"Ermöglicht einer Anwendung das Hinzufügen von Verknüpfungen ohne Eingriff des Nutzers."</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"Verknüpfungen deinstallieren"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"Einstellungen und Shortcuts für Startseite schreiben"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"Ermöglicht einer Anwendung, die Einstellungen und Shortcuts auf der Startseite zu ändern."</string>
<string name="gadget_error_text" msgid="8359351016167075858">"Problem beim Laden des Widgets"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"Dies ist eine Systemanwendung, die nicht deinstalliert werden kann."</string>
</resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 651496475..9326a932c 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"Ταπετσαρίες"</string>
<string name="activity_not_found" msgid="3571057450431950427">"Η εφαρμογή δεν έχει εγκατασταθεί στο τηλέφωνό σας."</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"Διαμόρφωση..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"Γραφικά στοιχεία"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"Φάκελοι"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"Ταπετσαρίες"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Αυτή θα είναι η καρτέλα ταπετσαριών"</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"Κάθε ηλικία"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"Εφαρμογές"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"Παιχνίδια"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"Η λήψη ολοκληρώθηκε"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"Όνομα φακέλου"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"Μετονομασία φακέλου"</string>
<string name="rename_action" msgid="6016003384693240896">"OK"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"Ειδοποιήσεις"</string>
<string name="menu_gestures" msgid="514678675575912237">"Χειρονομίες"</string>
<string name="menu_settings" msgid="6233960148378443661">"Ρυθμίσεις"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"εγκατάσταση συντομεύσεων"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"Επιτρέπει σε μια εφαρμογή την προσθήκη συντομεύσεων χωρίς την παρέμβαση του χρήστη."</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"κατάργηση εγκατάστασης συντομεύσεων"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"εγγραφή ρυθμίσεων και συντομεύσεων αρχικής οθόνης"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"Επιτρέπει σε μια εφαρμογή την αλλαγή των ρυθμίσεων και των συντομεύσεων στην αρχική οθόνη."</string>
<string name="gadget_error_text" msgid="8359351016167075858">"Παρουσιάστηκε πρόβλημα στη φόρτωση του γραφικού στοιχείου"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"Αυτή είναι εφαρμογή του συστήματος και δεν είναι δυνατή η κατάργηση της εγκατάστασής της."</string>
</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 5f6b4e505..b4e65d0f6 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"Papeles tapiz"</string>
<string name="activity_not_found" msgid="3571057450431950427">"La aplicación no está instalada en tu computadora."</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"Configurar..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"Widgets"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"Carpetas"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"Papeles tapiz"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Ésta será la pestaña para los papeles tapiz"</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"Todos"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"Google Apps"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"Juegos"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"Descargado"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"Nombre de carpeta"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"Cambiar nombre de carpeta"</string>
<string name="rename_action" msgid="6016003384693240896">"Aceptar"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"Notificaciones"</string>
<string name="menu_gestures" msgid="514678675575912237">"Gestos"</string>
<string name="menu_settings" msgid="6233960148378443661">"Configuración"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"instalar accesos directos"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"Permite a una aplicación agregar accesos directos sin intervención del usuario."</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"desinstalar papel tapiz"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"escribir configuración y accesos directos de la página principal"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"Permite a una aplicación cambiar la configuración y los accesos directos de la página principal."</string>
<string name="gadget_error_text" msgid="8359351016167075858">"Problema al cargar el widget"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"Esta es una aplicación de sistema y no puede desinstalarse."</string>
</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 5b46c59bf..ecbe8afe4 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"Fondos de pantalla"</string>
<string name="activity_not_found" msgid="3571057450431950427">"La aplicación no está instalada en el teléfono."</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"Configurar..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"Widgets"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"Carpetas"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"Fondos de pantalla"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Será la carpeta de fondos de pantalla."</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"Todas las aplicaciones"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"Aplicaciones"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"Juegos"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"Descargadas"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"Nombre de carpeta"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"Cambiar nombre de carpeta"</string>
<string name="rename_action" msgid="6016003384693240896">"Aceptar"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"Notificaciones"</string>
<string name="menu_gestures" msgid="514678675575912237">"Gestos"</string>
<string name="menu_settings" msgid="6233960148378443661">"Ajustes"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"instalar accesos directos"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"Permite que una aplicación añada accesos directos sin intervención del usuario."</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"desinstalar accesos directos"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"escribir información de accesos directos y de configuración del escritorio"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"Permite que una aplicación modifique la configuración y los accesos directos de la página de inicio."</string>
<string name="gadget_error_text" msgid="8359351016167075858">"Problema al cargar el widget"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"Se trata de una aplicación del sistema y no se puede desinstalar."</string>
</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index f1fb44014..c6d530dd6 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"Fonds d\'écran"</string>
<string name="activity_not_found" msgid="3571057450431950427">"L\'application n\'est pas installée sur votre téléphone."</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"Configurer..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"Widgets"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"Dossiers"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"Fonds d\'écran"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Onglet des fonds d\'écran"</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"Toutes"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"Applications"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"Jeux"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"Téléchargées"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"Nom du dossier"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"Renommer le dossier"</string>
<string name="rename_action" msgid="6016003384693240896">"OK"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"Notifications"</string>
<string name="menu_gestures" msgid="514678675575912237">"Gestes"</string>
<string name="menu_settings" msgid="6233960148378443661">"Paramètres"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"installer des raccourcis"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"Permet à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur."</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"désinstaller les raccourcis"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"Enregistrer les paramètres de la page d\'accueil et des raccourcis"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"Permet à une application de modifier les paramètres et les raccourcis de la page d\'accueil."</string>
<string name="gadget_error_text" msgid="8359351016167075858">"Problème lors du chargement du widget"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"Il s\'agit d\'une application système que vous ne pouvez pas désinstaller."</string>
</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index c94d74d3a..f25156a3d 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"Sfondi"</string>
<string name="activity_not_found" msgid="3571057450431950427">"Applicazione non installata sul telefono."</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"Configura..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"Widget"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"Cartelle"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"Sfondi"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Questa sarà la scheda degli sfondi"</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"Tutte"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"Applicazioni"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"Giochi"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"Scaricate"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"Nome cartella"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"Rinomina cartella"</string>
<string name="rename_action" msgid="6016003384693240896">"OK"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"Notifiche"</string>
<string name="menu_gestures" msgid="514678675575912237">"Gesti"</string>
<string name="menu_settings" msgid="6233960148378443661">"Impostazioni"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"aggiungere scorciatoie"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"Consente a un\'applicazione di aggiungere scorciatoie automaticamente."</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"eliminare scorciatoie"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"creare impostazioni e scorciatoie in Home"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"Consente a un\'applicazione di modificare le impostazioni e le scorciatoie in Home."</string>
<string name="gadget_error_text" msgid="8359351016167075858">"Errore durante il caricamento del widget"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"Questa è un\'applicazione di sistema e non può essere disinstallata."</string>
</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 021b895b2..98f042bec 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"壁紙"</string>
<string name="activity_not_found" msgid="3571057450431950427">"アプリケーションがインストールされていません。"</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"設定..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"ウィジェット"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"フォルダ"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"壁紙"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"ここが壁紙タブになります"</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"すべて"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"アプリ"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"ゲーム"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"ダウンロード済み"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"フォルダ名"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"フォルダ名を変更"</string>
<string name="rename_action" msgid="6016003384693240896">"OK"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"通知"</string>
<string name="menu_gestures" msgid="514678675575912237">"ジェスチャー"</string>
<string name="menu_settings" msgid="6233960148378443661">"設定"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"ショートカットのインストール"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"ユーザー操作なしでショートカットの追加をアプリケーションに許可します。"</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"ショートカットのアンインストール"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"ホームの設定とショートカットの書き込み"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"ホームの設定とショートカットの変更をアプリケーションに許可します。"</string>
<string name="gadget_error_text" msgid="8359351016167075858">"ウィジェットを表示できません"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"このシステムアプリケーションはアンインストールできません。"</string>
</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 341c7ee07..521332611 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"배경화면"</string>
<string name="activity_not_found" msgid="3571057450431950427">"휴대전화에 설치되어 있지 않은 애플리케이션입니다."</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"구성..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"위젯"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"폴더"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"배경화면"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"배경화면 탭이 됩니다."</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"모두"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"애플리케이션"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"게임"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"다운로드앱"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"폴더 이름"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"폴더 이름 바꾸기"</string>
<string name="rename_action" msgid="6016003384693240896">"확인"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"알림"</string>
<string name="menu_gestures" msgid="514678675575912237">"동작"</string>
<string name="menu_settings" msgid="6233960148378443661">"설정"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"바로가기 설치"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"애플리케이션이 사용자의 작업 없이 바로가기를 추가할 수 있도록 합니다."</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"바로가기 제거"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"홈 설정 및 바로가기 쓰기"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"애플리케이션이 홈에 있는 설정 및 바로가기를 변경할 수 있도록 합니다."</string>
<string name="gadget_error_text" msgid="8359351016167075858">"위젯을 로드하는 중 문제가 발생했습니다."</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"시스템 애플리케이션은 제거할 수 없습니다."</string>
</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 54418e189..0d24eb954 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"Bakgrunner"</string>
<string name="activity_not_found" msgid="3571057450431950427">"Applikasjonen er ikke installert."</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"Konfigurer"</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"Moduler"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"Mapper"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"Bakgrunner"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Dette er nå bakgrunnsfanen"</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"Alle"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"Programmer"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"Spill"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"Nedlastet"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"Mappenavn"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"Gi nytt navn til mappe"</string>
<string name="rename_action" msgid="6016003384693240896">"OK"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"Varslinger"</string>
<string name="menu_gestures" msgid="514678675575912237">"Bevegelser"</string>
<string name="menu_settings" msgid="6233960148378443661">"Innstillinger"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"installere snarveier"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"Lar applikasjonen legge til snarveier uten å involvere brukeren."</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"avinstallere snarveier"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"skrive skrivebordsinnstillinger og -snarveier"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"Lar applikasjonen endre innstillinger og snarveier på skrivebordet."</string>
<string name="gadget_error_text" msgid="8359351016167075858">"Problem under lasting av gadget"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"Dette er et systemprogram og kan ikke avinstalleres."</string>
</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index f5ac90e4e..5a2a269a8 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"Achtergronden"</string>
<string name="activity_not_found" msgid="3571057450431950427">"Deze toepassing is niet geïnstalleerd op uw telefoon."</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"Configureren..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"Widgets"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"Mappen"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"Achtergronden"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Dit wordt het tabblad \'Achtergronden\'"</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"Alle"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"Toepassingen"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"Games"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"Gedownload"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"Mapnaam"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"Naam van map wijzigen"</string>
<string name="rename_action" msgid="6016003384693240896">"OK"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"Meldingen"</string>
<string name="menu_gestures" msgid="514678675575912237">"Gebaren"</string>
<string name="menu_settings" msgid="6233960148378443661">"Instellingen"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"snelkoppelingen installeren"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"Een toepassing toestaan om snelkoppelingen toe te voegen zonder tussenkomst van de gebruiker."</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"snelkoppelingen verwijderen"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"instellingen en snelkoppelingen voor de startpagina schrijven"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"Hiermee kan een toepassing de instellingen en snelkoppelingen op de startpagina wijzigen."</string>
<string name="gadget_error_text" msgid="8359351016167075858">"Probleem bij het laden van widget"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"Dit is een systeemtoepassing die niet kan worden verwijderd."</string>
</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index d50a59a30..0c8e91be4 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"Tapety"</string>
<string name="activity_not_found" msgid="3571057450431950427">"Aplikacja nie jest zainstalowana w telefonie."</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"Konfiguruj..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"Widżety"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"Foldery"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"Tapety"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"To będzie karta tapet"</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"Wszystkie"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"Aplikacje"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"Gry"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"Pobrane"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"Nazwa folderu"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"Zmień nazwę folderu"</string>
<string name="rename_action" msgid="6016003384693240896">"OK"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"Powiadomienia"</string>
<string name="menu_gestures" msgid="514678675575912237">"Gesty"</string>
<string name="menu_settings" msgid="6233960148378443661">"Ustawienia"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"zainstaluj skróty"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"Umożliwia aplikacji dodawanie skrótów bez interwencji użytkownika."</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"odinstaluj skróty"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"zapisywanie ustawień i skrótów strony głównej"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"Umożliwia aplikacji zmianę ustawień i skrótów strony głównej."</string>
<string name="gadget_error_text" msgid="8359351016167075858">"Problem podczas ładowania widżetu"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"To jest aplikacja systemowa i nie można jej odinstalować."</string>
</resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index df63f3085..cdff3bfd0 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"Imagens de fundo"</string>
<string name="activity_not_found" msgid="3571057450431950427">"A aplicação não está instalada no telefone."</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"Configurar..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"Widgets"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"Pastas"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"Imagens de fundo"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Isto será o separador de imagens de fundo"</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"Todas"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"Aplicações"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"Jogos"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"Transferidas"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"Nome da pasta"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"Mudar o nome da pasta"</string>
<string name="rename_action" msgid="6016003384693240896">"OK"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"Notificações"</string>
<string name="menu_gestures" msgid="514678675575912237">"Gestos"</string>
<string name="menu_settings" msgid="6233960148378443661">"Definições"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"instalar atalhos"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"Permite que uma aplicação adicione atalhos sem a intervenção do utilizador."</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"desinstalar atalhos"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"escrever definições e atalhos do ecrã principal"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"Permite que uma aplicação altere as definições e os atalhos do ecrã principal."</string>
<string name="gadget_error_text" msgid="8359351016167075858">"Erro ao carregar o widget"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"É uma aplicação de sistema e não pode ser desinstalada."</string>
</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index b72f7e074..bdebb87c5 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"Papéis de parede"</string>
<string name="activity_not_found" msgid="3571057450431950427">"O aplicativo não está instalado no seu telefone."</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"Configurar..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"Widgets"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"Pastas"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"Papéis de parede"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Esta será a guia de papéis de parede"</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"Todos"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"Aplicativos"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"Jogos"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"Download concluído"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"Nome da pasta"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"Renomear pasta"</string>
<string name="rename_action" msgid="6016003384693240896">"OK"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"Notificações"</string>
<string name="menu_gestures" msgid="514678675575912237">"Gestos"</string>
<string name="menu_settings" msgid="6233960148378443661">"Configurações"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"instalar atalhos"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"Permite que um aplicativo adicione os atalhos sem a intervenção do usuário."</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"desinstalar atalhos"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"gravar configurações e atalhos da Página inicial"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"Permite que um aplicativo altere as configurações e atalhos na Página inicial."</string>
<string name="gadget_error_text" msgid="8359351016167075858">"Problema ao carregar o widget"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"Este é um aplicativo do sistema e não pode ser desinstalado."</string>
</resources>
diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml
new file mode 100644
index 000000000..0e4e4da7c
--- /dev/null
+++ b/res/values-rm/strings.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="8424725141379931883">"Lantschader"</string>
+ <string name="uid_name" msgid="3371120195364560632">"Applicaziuns da basa dad Android"</string>
+ <string name="folder_name" msgid="4588446541914685904">"Ordinatur"</string>
+ <string name="chooser_wallpaper" msgid="5988031014201479733">"Tscherner in fund davos"</string>
+ <string name="wallpaper_instructions" msgid="4215640646180727542">"Definir in fund davos"</string>
+ <string name="pick_wallpaper" msgid="5630222540525626723">"Maletgs da fund davos"</string>
+ <string name="activity_not_found" msgid="3571057450431950427">"L\'applicaziun n\'è betg installada sin quest telefonin."</string>
+ <string name="configure_wallpaper" msgid="2820186271419674623">"Configurar…"</string>
+ <!-- no translation found for widgets_tab_label (9145860100000983599) -->
+ <skip />
+ <!-- no translation found for folders_tab_label (1145293785541489736) -->
+ <skip />
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <!-- no translation found for wallpapers_tab_label (1617804870364119879) -->
+ <skip />
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <!-- no translation found for wallpapers_temp_tab_text (1660218201190495279) -->
+ <skip />
+ <!-- no translation found for all_apps_tab_all (2942727589595027258) -->
+ <skip />
+ <!-- no translation found for all_apps_tab_apps (5468972551904071712) -->
+ <skip />
+ <!-- no translation found for all_apps_tab_games (1855736784923494918) -->
+ <skip />
+ <!-- no translation found for all_apps_tab_downloaded (2300935549064726963) -->
+ <skip />
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
+ <string name="rename_folder_label" msgid="5646236631298452787">"Num da l\'ordinatur"</string>
+ <string name="rename_folder_title" msgid="4544573104191526550">"Renumnar l\'ordinatur"</string>
+ <string name="rename_action" msgid="6016003384693240896">"OK"</string>
+ <string name="cancel_action" msgid="3811860427489435048">"Interrumper"</string>
+ <string name="menu_item_add_item" msgid="6233177331075781114">"Agiuntar al visur da partenza"</string>
+ <string name="group_applications" msgid="4118484163419674240">"Applicaziuns"</string>
+ <string name="group_shortcuts" msgid="9133529424900391877">"Scursanidas"</string>
+ <string name="group_folder" msgid="5143593791798929193">"Nov ordinatur"</string>
+ <string name="group_live_folders" msgid="2664945399140647217">"Ordinatur"</string>
+ <string name="group_widgets" msgid="6704978494073105844">"Widgets"</string>
+ <string name="group_wallpapers" msgid="1568191644272224858">"Funds davos"</string>
+ <string name="add_folder" msgid="3521088587367839879">"Ordinatur"</string>
+ <string name="add_clock" msgid="2337943840175865746">"Ura"</string>
+ <string name="add_photo_frame" msgid="3154058437359487954">"Rom da maletgs"</string>
+ <string name="out_of_space" msgid="8365249326091984698">"Nagin spazi liber sin il visur da partenza."</string>
+ <string name="shortcut_installed" msgid="7071557296331322355">"\"Creà ina scursanida \"\"<xliff:g id="NAME">%s</xliff:g>\"\".\""</string>
+ <string name="shortcut_uninstalled" msgid="2129499669449749995">"\"La scursanida \"\"<xliff:g id="NAME">%s</xliff:g>\"\" è vegnida stizzada.\""</string>
+ <string name="shortcut_duplicate" msgid="4757756326465060694">"\"La scursanida \"\"<xliff:g id="NAME">%s</xliff:g>\"\" exista gia.\""</string>
+ <string name="title_select_shortcut" msgid="2858897527672831763">"Tscherner ina cumbinaziun da tastas"</string>
+ <string name="title_select_live_folder" msgid="3753447798805166749">"Tscherner l\'ordinatur"</string>
+ <string name="all_apps_button_label" msgid="3953036962111614813">"Tut las applicaziuns"</string>
+ <string name="all_apps_home_button_label" msgid="1022222300329398558">"Pagina da partenza"</string>
+ <string name="menu_add" msgid="3065046628354640854">"Agiuntar"</string>
+ <string name="menu_wallpaper" msgid="5837429080911269832">"Fund davos"</string>
+ <string name="menu_search" msgid="4826514464423239041">"Tschertgar"</string>
+ <string name="menu_notifications" msgid="6424587053194766192">"Avis"</string>
+ <string name="menu_gestures" msgid="514678675575912237">"Moviments"</string>
+ <string name="menu_settings" msgid="6233960148378443661">"Parameters"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
+ <string name="permlab_install_shortcut" msgid="1201690825493376489">"Installar scursanidas"</string>
+ <string name="permdesc_install_shortcut" msgid="7429365847558984148">"Pussibilitescha ch\'ina applicaziun agiunta scursanidas senza l\'intervenziun da l\'utilisader."</string>
+ <string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"deinstallar scursanidas"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="959972195916090900">"Pussibilitescha ch\'ina applicaziun possia stizzar scursanidas senza ina intervenziun da l\'utilisader."</string>
+ <string name="permlab_read_settings" msgid="3452408290738106747">"Leger ils parameters e las scursanidas da la pagina da partenza"</string>
+ <string name="permdesc_read_settings" msgid="8377434937176025492">"Pussibilitescha ch\'ina applicaziun possia leger ils parameters e las scursanidas da la pagina da partenza."</string>
+ <string name="permlab_write_settings" msgid="1360567537236705628">"Definir ils parameters e las scursanidas per la pagina da partenza"</string>
+ <string name="permdesc_write_settings" msgid="1098648778383349818">"Pussibilitescha ch\'ina applicaziun possia midar ils parameters e las scursanidas sin la pagina da partenza."</string>
+ <string name="gadget_error_text" msgid="8359351016167075858">"Problems cun chargiar il widget"</string>
+ <!-- no translation found for uninstall_system_app_text (7488523163288397451) -->
+ <skip />
+</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index b15161865..bb874451d 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"Обои"</string>
<string name="activity_not_found" msgid="3571057450431950427">"Приложение не установлено на телефоне."</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"Настроить..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"Виджеты"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"Папки"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"Обои"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Это будет вкладка обоев"</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"Все"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"Приложения"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"Игры"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"Загруженные"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"Название папки"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"Переименовать папку"</string>
<string name="rename_action" msgid="6016003384693240896">"ОК"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"Уведомления"</string>
<string name="menu_gestures" msgid="514678675575912237">"Жесты"</string>
<string name="menu_settings" msgid="6233960148378443661">"Настройки"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"устанавливать ярлыки"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"Позволяет приложению добавлять ярлыки без вмешательства пользователя"</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"удалять ярлыки"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"изменять настройки и ярлыки главного экрана"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"Позволяет приложению изменять настройки и ярлыки на главном экране."</string>
<string name="gadget_error_text" msgid="8359351016167075858">"Не удалось загрузить виджет"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"Это системное приложение. Удалить его невозможно."</string>
</resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 9c29ef13a..be6ee7fdd 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"Bakgrundsbilder"</string>
<string name="activity_not_found" msgid="3571057450431950427">"Programmet är inte installerat på din telefon."</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"Konfigurera..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"Widgetar"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"Mappar"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"Bakgrundsbilder"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Det här kommer att vara fliken för bakgrundsbilder"</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"Alla"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"Program"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"Spel"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"Hämtade"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"Mappnamn"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"Byt namn på mapp"</string>
<string name="rename_action" msgid="6016003384693240896">"OK"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"Aviseringar"</string>
<string name="menu_gestures" msgid="514678675575912237">"Gester"</string>
<string name="menu_settings" msgid="6233960148378443661">"Inställningar"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"installera genvägar"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"Tillåter att ett program lägger till genvägar utan åtgärd från användaren."</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"avinstallera genvägar"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"skriva inställningar och genvägar för startsidan"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"Tillåter att ett program ändrar inställningar och genvägar på startsidan."</string>
<string name="gadget_error_text" msgid="8359351016167075858">"Det gick inte att läsa in widgeten"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"Det här är ett systemprogram och kan inte avinstalleras."</string>
</resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 43f7520b1..59c816d61 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"Duvar Kağıtları"</string>
<string name="activity_not_found" msgid="3571057450431950427">"Uygulama telefonunuza yüklenmemiş."</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"Yapılandır..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"Widget\'lar"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"Klasörler"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"Duvar Kağıtları"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"Bu duvar kağıdı sekmesi olacaktır"</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"Tümü"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"Uygulamalar"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"Oyunlar"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"İndirilenler"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"Klasör adı"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"Klasörü yeniden adlandır"</string>
<string name="rename_action" msgid="6016003384693240896">"Tamam"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"Bildirimler"</string>
<string name="menu_gestures" msgid="514678675575912237">"Hareketler"</string>
<string name="menu_settings" msgid="6233960148378443661">"Ayarlar"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"kısayolları yükle"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"Bir uygulamaya, kısayolları kullanıcı müdahale etmeden ekleme izni verir."</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"kısayolları kaldır"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"Ana Sayfa ayarlarını ve kısayollarını yaz"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"Bir uygulamaya Ana Sayfadaki ayarları ve kısayolları değiştirme izni verir."</string>
<string name="gadget_error_text" msgid="8359351016167075858">"Widget yüklenirken sorun oluştu"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"Bu bir sistem uygulamasıdır ve kaldırılamaz."</string>
</resources>
diff --git a/res/values-xlarge/colors.xml b/res/values-xlarge/colors.xml
new file mode 100644
index 000000000..d4c6fadb4
--- /dev/null
+++ b/res/values-xlarge/colors.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+ <color name="app_info_filter">#A50000FE</color>
+ <color name="dimmed_view_color">#FF7F7F7F</color>
+</resources> \ No newline at end of file
diff --git a/res/values-xlarge/config.xml b/res/values-xlarge/config.xml
new file mode 100644
index 000000000..9b1bc0d13
--- /dev/null
+++ b/res/values-xlarge/config.xml
@@ -0,0 +1,29 @@
+<resources>
+ <!-- NOTE: Many of the all apps values here are also used for the customization drawer -->
+
+ <!-- Duration in milliseconds of the all apps zoom-in animation. -->
+ <!-- NB: This should be less than the workspaceShrinkTime as they happen together. -->
+ <integer name="config_allAppsZoomInTime">350</integer>
+
+ <!-- Duration in milliseconds of the all apps zoom-out animation -->
+ <!-- NB: This should be less than the workspaceUnshrinkTime as they happen together. -->
+ <integer name="config_allAppsZoomOutTime">350</integer>
+
+ <!-- Scaling factor used in the all apps zooming animations -->
+ <integer name="config_allAppsZoomScaleFactor">5</integer>
+
+ <!-- The extra distance off-screen that all apps appears from -->
+ <integer name="config_allAppsVerticalOffset">200</integer>
+
+ <!-- Duration in milliseconds of the animations between all apps, customize, & home.
+ NOTE: If these are changed, the toolbar animation times below should also be. -->
+ <integer name="config_allAppsCameraPanTime">700</integer>
+ <integer name="config_workspaceShrinkTime">700</integer>
+ <integer name="config_workspaceUnshrinkTime">700</integer>
+
+ <!-- Duration in milliseconds toolbar fade in and fade out animations.
+ NOTE: Fade in and fade out time should together be less the transition
+ animations between all apps, customize, & the workspace. -->
+ <integer name="config_toolbarButtonFadeInTime">350</integer>
+ <integer name="config_toolbarButtonFadeOutTime">350</integer>
+</resources>
diff --git a/res/values-xlarge/dimens.xml b/res/values-xlarge/dimens.xml
new file mode 100644
index 000000000..4662aad9f
--- /dev/null
+++ b/res/values-xlarge/dimens.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<resources>
+ <dimen name="workspace_cell_width">76dip</dimen>
+ <dimen name="workspace_cell_height">76dip</dimen>
+
+ <!-- extra horizontal spacing between mini screen thumbnails ie. in all
+ apps and in customization mode -->
+ <dimen name="smallScreenExtraSpacing">0dip</dimen>
+
+ <!-- vertical spacing between edge of screen and mini screen thumbnails -->
+ <dimen name="smallScreenVerticalMargin">60dip</dimen>
+
+ <dimen name="toolbar_padding">10dip</dimen>
+
+ <dimen name="toolbar_button_spacing">20dip</dimen>
+</resources>
diff --git a/res/values-xlarge/styles.xml b/res/values-xlarge/styles.xml
new file mode 100644
index 000000000..637b6ce7a
--- /dev/null
+++ b/res/values-xlarge/styles.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+
+<resources>
+ <style name="Theme" parent="Theme.Base">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowActionModeOverlay">true</item>
+ </style>
+
+ <style name="WorkspaceIcon.Portrait">
+ <item name="android:drawablePadding">4dip</item>
+ <item name="android:paddingTop">1dip</item>
+ </style>
+
+ <style name="WorkspaceIcon.Landscape">
+ <item name="android:drawablePadding">4dip</item>
+ <item name="android:paddingTop">1dip</item>
+ </style>
+</resources>
diff --git a/res/values-xlarge/wallpapers.xml b/res/values-xlarge/wallpapers.xml
new file mode 100644
index 000000000..f22654d3a
--- /dev/null
+++ b/res/values-xlarge/wallpapers.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2009 Google Inc.
+ *
+ * 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.
+ -->
+
+<resources>
+ <string-array name="wallpapers" translatable="false">
+ <item>glow_wallpaper</item>
+ </string-array>
+</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 0705787bd..58f61787b 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"壁纸"</string>
<string name="activity_not_found" msgid="3571057450431950427">"您的手机上未安装应用程序。"</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"配置..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"窗口小部件"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"文件夹"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"壁纸"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"这将会成为壁纸标签"</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"全部"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"应用程序"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"游戏"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"已下载"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"文件夹名称"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"重命名文件夹"</string>
<string name="rename_action" msgid="6016003384693240896">"确定"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"通知"</string>
<string name="menu_gestures" msgid="514678675575912237">"手势"</string>
<string name="menu_settings" msgid="6233960148378443661">"设置"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"安装快捷方式"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"允许应用程序在没有用户介入的情况下添加快捷方式。"</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"卸载快捷方式"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"写入主屏幕的设置和快捷方式"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"允许应用程序更改主屏幕的设置和快捷方式。"</string>
<string name="gadget_error_text" msgid="8359351016167075858">"载入窗口小部件时出现问题"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"这是系统应用程序,无法卸载。"</string>
</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index de37e1925..a7cd0988b 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -27,6 +27,20 @@
<string name="pick_wallpaper" msgid="5630222540525626723">"桌布"</string>
<string name="activity_not_found" msgid="3571057450431950427">"應用程式未安裝到手機。"</string>
<string name="configure_wallpaper" msgid="2820186271419674623">"設定..."</string>
+ <string name="widgets_tab_label" msgid="9145860100000983599">"小工具"</string>
+ <string name="folders_tab_label" msgid="1145293785541489736">"資料夾"</string>
+ <!-- no translation found for shortcuts_tab_label (8640731503933155644) -->
+ <skip />
+ <string name="wallpapers_tab_label" msgid="1617804870364119879">"桌布"</string>
+ <!-- no translation found for applications_tab_label (9046797126882613707) -->
+ <skip />
+ <string name="wallpapers_temp_tab_text" msgid="1660218201190495279">"桌布標籤保留位"</string>
+ <string name="all_apps_tab_all" msgid="2942727589595027258">"全部"</string>
+ <string name="all_apps_tab_apps" msgid="5468972551904071712">"應用程式"</string>
+ <string name="all_apps_tab_games" msgid="1855736784923494918">"遊戲"</string>
+ <string name="all_apps_tab_downloaded" msgid="2300935549064726963">"下載內容"</string>
+ <!-- no translation found for widget_dims_format (412876472704567784) -->
+ <skip />
<string name="rename_folder_label" msgid="5646236631298452787">"資料夾名稱"</string>
<string name="rename_folder_title" msgid="4544573104191526550">"重新命名資料夾"</string>
<string name="rename_action" msgid="6016003384693240896">"確定"</string>
@@ -55,6 +69,12 @@
<string name="menu_notifications" msgid="6424587053194766192">"通知"</string>
<string name="menu_gestures" msgid="514678675575912237">"觸控動作"</string>
<string name="menu_settings" msgid="6233960148378443661">"設定"</string>
+ <!-- no translation found for cab_menu_delete_app (1242619904941293871) -->
+ <skip />
+ <!-- no translation found for cab_menu_app_info (5180426909324882018) -->
+ <skip />
+ <!-- no translation found for cab_selection_text (8712157849499378915) -->
+ <skip />
<string name="permlab_install_shortcut" msgid="1201690825493376489">"安裝捷徑"</string>
<string name="permdesc_install_shortcut" msgid="7429365847558984148">"允許應用程式自動新增快速鍵。"</string>
<string name="permlab_uninstall_shortcut" msgid="7696645932555926449">"解除安裝捷徑"</string>
@@ -64,4 +84,5 @@
<string name="permlab_write_settings" msgid="1360567537236705628">"寫入首頁設定和捷徑"</string>
<string name="permdesc_write_settings" msgid="1098648778383349818">"允許應用程式變更首頁中的設定和捷徑。"</string>
<string name="gadget_error_text" msgid="8359351016167075858">"載入小工具時發生問題"</string>
+ <string name="uninstall_system_app_text" msgid="7488523163288397451">"這是系統應用程式,無法將其解除安裝。"</string>
</resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 78391206e..3e2a8e9d7 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -32,6 +32,10 @@
<declare-styleable name="Workspace">
<!-- The first screen the workspace should display. -->
<attr name="defaultScreen" format="integer" />
+ <!-- The number of horizontal cells in the CellLayout -->
+ <attr name="cellCountX" format="integer" />
+ <!-- The number of vertical cells in the CellLayout -->
+ <attr name="cellCountY" format="integer" />
</declare-styleable>
<!-- CellLayout specific attributes. These attributes are used to customize
@@ -42,17 +46,56 @@
<!-- The height of a single cell -->
<attr name="cellHeight" format="dimension" />
<!-- Padding to apply at the start of the long axis -->
- <attr name="longAxisStartPadding" format="dimension" />
+ <attr name="xAxisStartPadding" format="dimension" />
<!-- Padding to apply at the end of the long axis -->
- <attr name="longAxisEndPadding" format="dimension" />
+ <attr name="xAxisEndPadding" format="dimension" />
<!-- Padding to apply at the start of the short axis -->
- <attr name="shortAxisStartPadding" format="dimension" />
+ <attr name="yAxisStartPadding" format="dimension" />
<!-- Padding to apply at the end of the short axis -->
- <attr name="shortAxisEndPadding" format="dimension" />
- <!-- Number of cells on the short axis of the CellLayout -->
- <attr name="shortAxisCells" format="integer" />
- <!-- Number of cells on the long axis of the CellLayout -->
- <attr name="longAxisCells" format="integer" />
+ <attr name="yAxisEndPadding" format="dimension" />
+ </declare-styleable>
+
+ <!-- PagedViewIcon specific attributes. These attributes are used to customize
+ a PagedViewIcon view in XML files. -->
+ <declare-styleable name="PagedViewIcon">
+ <!-- The blur color of the holographic outline -->
+ <attr name="blurColor" format="color" />
+ <!-- The outline color of the holographic outline -->
+ <attr name="outlineColor" format="color" />
+ <!-- The checked blur color of the holographic outline -->
+ <attr name="checkedBlurColor" format="color" />
+ <!-- The checked outline color of the holographic outline -->
+ <attr name="checkedOutlineColor" format="color" />
+ </declare-styleable>
+
+ <!-- PagedViewWidgetIcon specific attributes. These attributes are used to
+ customize a PagedViewWidgetIcon view in XML files. -->
+ <declare-styleable name="PagedViewWidgetIcon">
+ <!-- The checked blur color of the holographic outline -->
+ <attr name="checkedBlurColor" />
+ <!-- The checked outline color of the holographic outline -->
+ <attr name="checkedOutlineColor" />
+ </declare-styleable>
+
+ <!-- PagedView specific attributes. These attributes are used to customize
+ a PagedView view in XML files. -->
+ <declare-styleable name="PagedView">
+ <!-- The number of horizontal cells in a page -->
+ <attr name="cellCountX" />
+ <!-- The number of vertical cells in a page -->
+ <attr name="cellCountY" />
+ <!-- The padding of the pages that are dynamically created per page -->
+ <attr name="pageLayoutPaddingTop" format="dimension" />
+ <attr name="pageLayoutPaddingBottom" format="dimension" />
+ <attr name="pageLayoutPaddingLeft" format="dimension" />
+ <attr name="pageLayoutPaddingRight" format="dimension" />
+ </declare-styleable>
+
+ <!-- CustomizePagedView specific attributes. These attributes are used to customize
+ a CustomizePagedView view in XML files. -->
+ <declare-styleable name="CustomizePagedView">
+ <!-- The number of horizontal cells for the widget tab -->
+ <attr name="widgetCellCountX" format="integer" />
</declare-styleable>
<!-- DeleteZone specific attributes. These attributes are used to customize
diff --git a/res/values/config.xml b/res/values/config.xml
index 15ec05b42..781188272 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -3,4 +3,5 @@
<integer name="config_allAppsFadeOutTime">700</integer>
<integer name="config_allAppsBatchLoadDelay">0</integer>
<integer name="config_allAppsBatchSize">0</integer>
+ <bool name="config_hardwareAccelerated">false</bool>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index c83986be2..790f83599 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -37,8 +37,4 @@
<!-- delete_zone_size_full - button_bar_height_portrait -->
<dimen name="delete_zone_padding">14dip</dimen>
-
- <!-- the area at the edge of the screen that makes the workspace go left
- or right while you're dragging. -->
- <dimen name="scroll_zone">20dp</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f4dfffb0a..98de9db2a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -43,6 +43,33 @@
<string name="activity_not_found">Application is not installed on your phone.</string>
<!-- List item for configuring the current wallpaper -->
<string name="configure_wallpaper">Configure...</string>
+ <!-- Labels for the tabs in the customize drawer -->
+ <string name="widgets_tab_label">Widgets</string>
+ <!-- Title of tab containing all possible folders (ie standard Folders and Live Folders) that
+ can be added to the home screen -->
+ <string name="folders_tab_label">Folders</string>
+ <!-- Title of tab containing all possible shortcuts (eg Contacts, Bookmarks) that can be
+ added to the home screen -->
+ <string name="shortcuts_tab_label">More</string>
+ <!-- Title of tab for configuring wallpapers -->
+ <string name="wallpapers_tab_label">Wallpapers</string>
+ <!-- Title of tab for configuring applications -->
+ <string name="applications_tab_label">App Shortcuts</string>
+ <!-- Placeholder text, will be removed -->
+ <string name="wallpapers_temp_tab_text">This will be the wallpapers tab</string>
+ <!-- Labels for the tabs in All Apps -->
+ <!-- Title of the tab for all applications (includes games and non-games) [CHAR_LIMIT=12] -->
+ <string name="all_apps_tab_all">All</string>
+ <!-- Title of the tab for all applications *except* games [CHAR_LIMIT=12] -->
+ <string name="all_apps_tab_apps">Apps</string>
+ <!-- Title of the tab for applications labeled as games [CHAR_LIMIT=12] -->
+ <string name="all_apps_tab_games">Games</string>
+ <!-- Tile of the tab for applications that were downloaded from market [CHAR_LIMIT=12] -->
+ <string name="all_apps_tab_downloaded">Downloaded</string>
+
+ <!-- Customization Drawer -->
+ <!-- The format string for the dimensions of a widget in the drawer -->
+ <string name="widget_dims_format">%1$d x %2$d</string>
<!-- Folders -->
<skip />
@@ -114,6 +141,23 @@
<!-- Noun, menu item used to show the system settings -->
<string name="menu_settings">Settings</string>
+ <!-- Strings for the contextual action bar (CAB) in All Apps -->
+ <skip />
+ <!-- Describes the button for uninstalling the currently selected application.
+ Text is not displayed, but provided for accessibility. [CHAR_LIMIT=none] -->
+ <string name="cab_menu_delete_app">Uninstall application</string>
+ <!-- Describes the button for getting details/info about currently selected application.
+ Text is not displayed, but provided for accessibility. [CHAR_LIMIT=none] -->
+ <string name="cab_menu_app_info">Application details</string>
+ <!-- Appears in the CAB when an app is selected in All Apps or Customize mode. [CHAR_LIMIT=50] -->
+ <string name="cab_app_selection_text">1 application selected</string>
+ <!-- Appears in the CAB when a widget is selected in Customize mode. [CHAR_LIMIT=50] -->
+ <string name="cab_widget_selection_text">1 widget selected</string>
+ <!-- Appears in the CAB when a folder is selected in Customize mode. [CHAR_LIMIT=50] -->
+ <string name="cab_folder_selection_text">1 folder selected</string>
+ <!-- Appears in the CAB when a shortcut is selected in Customize mode. [CHAR_LIMIT=50] -->
+ <string name="cab_shortcut_selection_text">1 shortcut selected</string>
+
<!-- Permissions: -->
<skip />
<!-- Permission short label -->
@@ -149,4 +193,6 @@
<string name="default_browser_url" translatable="false">
http://www.google.com/m?client=ms-{CID}&amp;source=android-home-hotseat</string>
+ <!-- Text to inform the user that they can't uninstall a system application -->
+ <string name="uninstall_system_app_text">This is a system application and cannot be uninstalled.</string>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index c2082111c..a97b3c50b 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -18,7 +18,10 @@
-->
<resources>
- <style name="Theme" parent="android:Theme.Wallpaper">
+ <style name="Theme.Base" parent="android:Theme.Wallpaper">
+ </style>
+
+ <style name="Theme" parent="Theme.Base">
<item name="android:windowNoTitle">true</item>
</style>
diff --git a/src/com/android/launcher2/AllApps2D.java b/src/com/android/launcher2/AllApps2D.java
index 7ad5e4915..9764f2338 100644
--- a/src/com/android/launcher2/AllApps2D.java
+++ b/src/com/android/launcher2/AllApps2D.java
@@ -16,32 +16,30 @@
package com.android.launcher2;
+import com.android.launcher.R;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.Bitmap;
-import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
-import android.view.ViewGroup;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
-import android.view.ViewConfiguration;
import android.widget.AdapterView;
-import android.widget.ImageButton;
-import android.widget.TextView;
import android.widget.ArrayAdapter;
import android.widget.GridView;
+import android.widget.ImageButton;
import android.widget.RelativeLayout;
+import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collections;
-import com.android.launcher.R;
-
public class AllApps2D
extends RelativeLayout
implements AllAppsView,
@@ -58,8 +56,16 @@ public class AllApps2D
private GridView mGrid;
+ /** All applications in the system (we might only be showing a subset) */
private ArrayList<ApplicationInfo> mAllAppsList = new ArrayList<ApplicationInfo>();
+ /** Currently visible applications in the grid */
+ private ArrayList<ApplicationInfo> mVisibleAppsList = new ArrayList<ApplicationInfo>();
+
+ public enum AppType { APP, GAME, DOWNLOADED, ALL };
+
+ private AppType mCurrentFilter = AppType.ALL;
+
// preserve compatibility with 3D all apps:
// 0.0 -> hidden
// 1.0 -> shown and opaque
@@ -120,30 +126,27 @@ public class AllApps2D
setVisibility(View.GONE);
setSoundEffectsEnabled(false);
- mAppsAdapter = new AppsAdapter(getContext(), mAllAppsList);
- mAppsAdapter.setNotifyOnChange(false);
+ mAppsAdapter = new AppsAdapter(getContext(), mVisibleAppsList);
}
@Override
protected void onFinishInflate() {
- setBackgroundColor(Color.BLACK);
-
try {
mGrid = (GridView)findViewWithTag("all_apps_2d_grid");
if (mGrid == null) throw new Resources.NotFoundException();
mGrid.setOnItemClickListener(this);
mGrid.setOnItemLongClickListener(this);
- mGrid.setBackgroundColor(Color.BLACK);
- mGrid.setCacheColorHint(Color.BLACK);
+ // The home button is optional; some layouts might not use it
ImageButton homeButton = (ImageButton) findViewWithTag("all_apps_2d_home");
- if (homeButton == null) throw new Resources.NotFoundException();
- homeButton.setOnClickListener(
- new View.OnClickListener() {
- public void onClick(View v) {
- mLauncher.closeAllApps(true);
- }
- });
+ if (homeButton != null) {
+ homeButton.setOnClickListener(
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ mLauncher.closeAllApps(true);
+ }
+ });
+ }
} catch (Resources.NotFoundException e) {
Log.e(TAG, "Can't find necessary layout elements for AllApps2D");
}
@@ -250,19 +253,17 @@ public class AllApps2D
return mZoom > 0.001f;
}
- @Override
- public boolean isOpaque() {
- return mZoom > 0.999f;
+ public boolean isAnimating() {
+ return (getAnimation() != null);
}
public void setApps(ArrayList<ApplicationInfo> list) {
mAllAppsList.clear();
addApps(list);
+ filterApps(mCurrentFilter);
}
public void addApps(ArrayList<ApplicationInfo> list) {
-// Log.d(TAG, "addApps: " + list.size() + " apps: " + list.toString());
-
final int N = list.size();
for (int i=0; i<N; i++) {
@@ -274,11 +275,12 @@ public class AllApps2D
}
mAllAppsList.add(index, item);
}
- mAppsAdapter.notifyDataSetChanged();
+ filterApps(mCurrentFilter);
}
public void removeApps(ArrayList<ApplicationInfo> list) {
final int N = list.size();
+
for (int i=0; i<N; i++) {
final ApplicationInfo item = list.get(i);
int index = findAppByComponent(mAllAppsList, item);
@@ -289,7 +291,7 @@ public class AllApps2D
// Try to recover. This should keep us from crashing for now.
}
}
- mAppsAdapter.notifyDataSetChanged();
+ filterApps(mCurrentFilter);
}
public void updateApps(ArrayList<ApplicationInfo> list) {
@@ -298,6 +300,33 @@ public class AllApps2D
addApps(list);
}
+ public void filterApps(AppType appType) {
+ mCurrentFilter = appType;
+
+ mAppsAdapter.setNotifyOnChange(false);
+ mVisibleAppsList.clear();
+ if (appType == AppType.ALL) {
+ mVisibleAppsList.addAll(mAllAppsList);
+ } else {
+ int searchFlags = 0;
+
+ if (appType == AppType.APP) {
+ searchFlags = ApplicationInfo.APP_FLAG;
+ } else if (appType == AppType.GAME) {
+ searchFlags = ApplicationInfo.GAME_FLAG;
+ } else if (appType == AppType.DOWNLOADED) {
+ searchFlags = ApplicationInfo.DOWNLOADED_FLAG;
+ }
+
+ for (ApplicationInfo info : mAllAppsList) {
+ if ((info.flags & searchFlags) != 0) {
+ mVisibleAppsList.add(info);
+ }
+ }
+ }
+ mAppsAdapter.notifyDataSetChanged();
+ }
+
private static int findAppByComponent(ArrayList<ApplicationInfo> list, ApplicationInfo item) {
ComponentName component = item.intent.getComponent();
final int N = list.size();
diff --git a/src/com/android/launcher2/AllApps3D.java b/src/com/android/launcher2/AllApps3D.java
index 308ad28d9..3fa6e7cbf 100644
--- a/src/com/android/launcher2/AllApps3D.java
+++ b/src/com/android/launcher2/AllApps3D.java
@@ -16,6 +16,12 @@
package com.android.launcher2;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+import com.android.launcher.R;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
@@ -23,19 +29,7 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.ProgramFragment;
-import android.renderscript.ProgramStore;
-import android.renderscript.ProgramVertex;
-import android.renderscript.RSSurfaceView;
-import android.renderscript.RenderScriptGL;
-import android.renderscript.RenderScript;
-import android.renderscript.Sampler;
-import android.renderscript.Script;
-import android.renderscript.ScriptC;
-import android.renderscript.SimpleMesh;
-import android.renderscript.Type;
+import android.renderscript.*;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -48,12 +42,6 @@ import android.view.View;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-
-import com.android.launcher.R;
-
public class AllApps3D extends RSSurfaceView
implements AllAppsView, View.OnClickListener, View.OnLongClickListener, DragSource {
private static final String TAG = "Launcher.AllApps3D";
@@ -131,27 +119,16 @@ public class AllApps3D extends RSSurfaceView
private boolean mSurrendered;
private int mRestoreFocusIndex = -1;
-
+
@SuppressWarnings({"UnusedDeclaration"})
static class Defines {
- public static final int ALLOC_PARAMS = 0;
- public static final int ALLOC_STATE = 1;
- public static final int ALLOC_ICON_IDS = 3;
- public static final int ALLOC_LABEL_IDS = 4;
- public static final int ALLOC_VP_CONSTANTS = 5;
-
public static final int COLUMNS_PER_PAGE_PORTRAIT = 4;
public static final int ROWS_PER_PAGE_PORTRAIT = 4;
public static final int COLUMNS_PER_PAGE_LANDSCAPE = 6;
public static final int ROWS_PER_PAGE_LANDSCAPE = 3;
- public static final int ICON_WIDTH_PX = 64;
- public static final int ICON_TEXTURE_WIDTH_PX = 74;
public static final int SELECTION_TEXTURE_WIDTH_PX = 74 + 20;
-
- public static final int ICON_HEIGHT_PX = 64;
- public static final int ICON_TEXTURE_HEIGHT_PX = 74;
public static final int SELECTION_TEXTURE_HEIGHT_PX = 74 + 20;
}
@@ -159,7 +136,6 @@ public class AllApps3D extends RSSurfaceView
super(context, attrs);
setFocusable(true);
setSoundEffectsEnabled(false);
- getHolder().setFormat(PixelFormat.TRANSLUCENT);
final ViewConfiguration config = ViewConfiguration.get(context);
mSlop = config.getScaledTouchSlop();
mMaxFlingVelocity = config.getScaledMaximumFlingVelocity();
@@ -274,26 +250,11 @@ public class AllApps3D extends RSSurfaceView
sRollo.dirtyCheck();
sRollo.resize(w, h);
+ Log.d(TAG, "sc " + sRS);
if (sRS != null) {
sRS.mMessageCallback = mMessageProc = new AAMessage();
}
- if (sRollo.mUniformAlloc != null) {
- float tf[] = new float[] {72.f, 72.f,
- 120.f, 120.f, 0.f, 0.f,
- 120.f, 680.f,
- (2.f / 480.f), 0, -((float)w / 2) - 0.25f, -380.25f};
- if (w > h) {
- tf[6] = 40.f;
- tf[7] = h - 40.f;
- tf[9] = 1.f;
- tf[10] = -((float)w / 2) - 0.25f;
- tf[11] = -((float)h / 2) - 0.25f;
- }
-
- sRollo.mUniformAlloc.data(tf);
- }
-
//long endTime = SystemClock.uptimeMillis();
//Log.d(TAG, "surfaceChanged took " + (endTime-startTime) + "ms");
}
@@ -307,18 +268,17 @@ public class AllApps3D extends RSSurfaceView
if (mArrowNavigation) {
if (!hasWindowFocus) {
// Clear selection when we lose window focus
- mLastSelectedIcon = sRollo.mState.selectedIconIndex;
+ mLastSelectedIcon = sRollo.mScript.get_gSelectedIconIndex();
sRollo.setHomeSelected(SELECTED_NONE);
sRollo.clearSelectedIcon();
- sRollo.mState.save();
} else {
- if (sRollo.mState.iconCount > 0) {
+ if (sRollo.mScript.get_gIconCount() > 0) {
if (mLastSelection == SELECTION_ICONS) {
int selection = mLastSelectedIcon;
final int firstIcon = Math.round(sRollo.mScrollPos) * mColumnsPerPage;
if (selection < 0 || // No selection
selection < firstIcon || // off the top of the screen
- selection >= sRollo.mState.iconCount || // past last icon
+ selection >= sRollo.mScript.get_gIconCount() || // past last icon
selection >= firstIcon + // past last icon on screen
(mColumnsPerPage * mRowsPerPage)) {
selection = firstIcon;
@@ -326,10 +286,8 @@ public class AllApps3D extends RSSurfaceView
// Select the first icon when we gain window focus
sRollo.selectIcon(selection, SELECTED_FOCUSED);
- sRollo.mState.save();
} else if (mLastSelection == SELECTION_HOME) {
sRollo.setHomeSelected(SELECTED_FOCUSED);
- sRollo.mState.save();
}
}
}
@@ -356,7 +314,6 @@ public class AllApps3D extends RSSurfaceView
// Clear selection when we lose focus
sRollo.clearSelectedIcon();
sRollo.setHomeSelected(SELECTED_NONE);
- sRollo.mState.save();
mArrowNavigation = false;
}
} else {
@@ -366,11 +323,10 @@ public class AllApps3D extends RSSurfaceView
}
private void gainFocus() {
- if (!mArrowNavigation && sRollo.mState.iconCount > 0) {
+ if (!mArrowNavigation && sRollo.mScript.get_gIconCount() > 0) {
// Select the first icon when we gain keyboard focus
mArrowNavigation = true;
sRollo.selectIcon(Math.round(sRollo.mScrollPos) * mColumnsPerPage, SELECTED_FOCUSED);
- sRollo.mState.save();
}
}
@@ -382,7 +338,7 @@ public class AllApps3D extends RSSurfaceView
if (!isVisible()) {
return false;
}
- final int iconCount = sRollo.mState.iconCount;
+ final int iconCount = sRollo.mScript.get_gIconCount();
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
if (mArrowNavigation) {
@@ -390,7 +346,7 @@ public class AllApps3D extends RSSurfaceView
reallyPlaySoundEffect(SoundEffectConstants.CLICK);
mLauncher.closeAllApps(true);
} else {
- int whichApp = sRollo.mState.selectedIconIndex;
+ int whichApp = sRollo.mScript.get_gSelectedIconIndex();
if (whichApp >= 0) {
ApplicationInfo app = mAllAppsList.get(whichApp);
mLauncher.startActivitySafely(app.intent, app);
@@ -402,10 +358,10 @@ public class AllApps3D extends RSSurfaceView
if (iconCount > 0) {
final boolean isPortrait = getWidth() < getHeight();
-
+
mArrowNavigation = true;
- int currentSelection = sRollo.mState.selectedIconIndex;
+ int currentSelection = sRollo.mScript.get_gSelectedIconIndex();
int currentTopRow = Math.round(sRollo.mScrollPos);
// The column of the current selection, in the range 0..COLUMNS_PER_PAGE_PORTRAIT-1
@@ -511,7 +467,6 @@ public class AllApps3D extends RSSurfaceView
}
if (newSelection != currentSelection) {
sRollo.selectIcon(newSelection, SELECTED_FOCUSED);
- sRollo.mState.save();
}
}
return handled;
@@ -611,7 +566,6 @@ public class AllApps3D extends RSSurfaceView
(!isPortrait && x > mTouchXBorders[mTouchXBorders.length-1])) {
mTouchTracking = TRACKING_HOME;
sRollo.setHomeSelected(SELECTED_PRESSED);
- sRollo.mState.save();
mCurrentIconIndex = -1;
} else {
mTouchTracking = TRACKING_FLING;
@@ -619,9 +573,6 @@ public class AllApps3D extends RSSurfaceView
mMotionDownRawX = (int)ev.getRawX();
mMotionDownRawY = (int)ev.getRawY();
- sRollo.mState.newPositionX = ev.getRawY() / getHeight();
- sRollo.mState.newTouchDown = 1;
-
if (!sRollo.checkClickOK()) {
sRollo.clearSelectedIcon();
} else {
@@ -632,8 +583,7 @@ public class AllApps3D extends RSSurfaceView
cancelLongPress();
}
}
- sRollo.mState.save();
- sRollo.move();
+ sRollo.move(ev.getRawY() / getHeight());
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(ev);
mStartedScrolling = false;
@@ -646,7 +596,6 @@ public class AllApps3D extends RSSurfaceView
y > mTouchYBorders[mTouchYBorders.length-1]) || (!isPortrait
&& x > mTouchXBorders[mTouchXBorders.length-1])
? SELECTED_PRESSED : SELECTED_NONE);
- sRollo.mState.save();
} else if (mTouchTracking == TRACKING_FLING) {
int rawY = (int)ev.getRawY();
int slop;
@@ -667,14 +616,11 @@ public class AllApps3D extends RSSurfaceView
cancelLongPress();
mCurrentIconIndex = -1;
}
- sRollo.mState.newPositionX = ev.getRawY() / getHeight();
- sRollo.mState.newTouchDown = 1;
- sRollo.move();
+ sRollo.move(ev.getRawY() / getHeight());
mStartedScrolling = true;
sRollo.clearSelectedIcon();
mVelocityTracker.addMovement(ev);
- sRollo.mState.save();
}
}
break;
@@ -688,18 +634,13 @@ public class AllApps3D extends RSSurfaceView
mLauncher.closeAllApps(true);
}
sRollo.setHomeSelected(SELECTED_NONE);
- sRollo.mState.save();
}
mCurrentIconIndex = -1;
} else if (mTouchTracking == TRACKING_FLING) {
- sRollo.mState.newTouchDown = 0;
- sRollo.mState.newPositionX = ev.getRawY() / getHeight();
-
mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, mMaxFlingVelocity);
- sRollo.mState.flingVelocity = mVelocityTracker.getYVelocity() / getHeight();
sRollo.clearSelectedIcon();
- sRollo.mState.save();
- sRollo.fling();
+ sRollo.fling(ev.getRawY() / getHeight(),
+ mVelocityTracker.getYVelocity() / getHeight());
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
@@ -766,7 +707,7 @@ public class AllApps3D extends RSSurfaceView
int pos = -1;
switch (mLastSelection) {
case SELECTION_ICONS:
- index = sRollo.mState.selectedIconIndex;
+ index = sRollo.mScript.get_gSelectedIconIndex();
if (index >= 0) {
ApplicationInfo info = mAllAppsList.get(index);
if (info.title != null) {
@@ -825,8 +766,8 @@ public class AllApps3D extends RSSurfaceView
return sRollo != null && mZoom > 0.001f;
}
- public boolean isOpaque() {
- return mZoom > 0.999f;
+ public boolean isAnimating() {
+ return isVisible() && mZoom <= 0.999f;
}
public void setApps(ArrayList<ApplicationInfo> list) {
@@ -858,13 +799,12 @@ public class AllApps3D extends RSSurfaceView
if (sRollo != null && reload) {
sRollo.setApps(list);
}
-
+
if (hasFocus() && mRestoreFocusIndex != -1) {
sRollo.selectIcon(mRestoreFocusIndex, SELECTED_FOCUSED);
- sRollo.mState.save();
mRestoreFocusIndex = -1;
}
-
+
mLocks &= ~LOCK_ICONS_PENDING;
}
@@ -881,7 +821,7 @@ public class AllApps3D extends RSSurfaceView
final int N = list.size();
if (sRollo != null) {
sRollo.pause();
- sRollo.reallocAppsList(sRollo.mState.iconCount + N);
+ sRollo.reallocAppsList(sRollo.mScript.get_gIconCount() + N);
}
for (int i=0; i<N; i++) {
@@ -951,17 +891,6 @@ public class AllApps3D extends RSSurfaceView
return -1;
}
- /*
- private static int countPages(int iconCount) {
- int iconsPerPage = getColumnsCount() * Defines.ROWS_PER_PAGE_PORTRAIT;
- int pages = iconCount / iconsPerPage;
- if (pages*iconsPerPage != iconCount) {
- pages++;
- }
- return pages;
- }
- */
-
class AAMessage extends RenderScript.RSMessage {
public void run() {
sRollo.mScrollPos = ((float)mData[0]) / (1 << 16);
@@ -993,23 +922,12 @@ public class AllApps3D extends RSSurfaceView
private int mHeight;
private Resources mRes;
- private Script mScript;
- private Script.Invokable mInvokeMove;
- private Script.Invokable mInvokeMoveTo;
- private Script.Invokable mInvokeFling;
- private Script.Invokable mInvokeResetWAR;
- private Script.Invokable mInvokeSetZoom;
-
- private ProgramStore mPSIcons;
- private ProgramFragment mPFTexMip;
- private ProgramFragment mPFTexMipAlpha;
- private ProgramFragment mPFTexNearest;
- private ProgramVertex mPV;
- private ProgramVertex mPVCurve;
- private SimpleMesh mMesh;
+ ScriptC_allapps mScript;
+
+ private Mesh mMesh;
private ProgramVertex.MatrixAllocation mPVA;
- private Allocation mUniformAlloc;
+ private ScriptField_VpConsts mUniformAlloc;
private Allocation mHomeButtonNormal;
private Allocation mHomeButtonFocused;
@@ -1022,28 +940,15 @@ public class AllApps3D extends RSSurfaceView
private Allocation[] mLabels;
private int[] mLabelIds;
private Allocation mAllocLabelIds;
- private Allocation mSelectedIcon;
private Bitmap mSelectionBitmap;
private Canvas mSelectionCanvas;
-
- private float mScrollPos;
- Params mParams;
- State mState;
+ private float mScrollPos;
AllApps3D mAllApps;
boolean mInitialize;
- class BaseAlloc {
- Allocation mAlloc;
- Type mType;
-
- void save() {
- mAlloc.data(this);
- }
- }
-
private boolean checkClickOK() {
return (Math.abs(mAllApps.mVelocity) < 0.4f) &&
(Math.abs(mScrollPos - Math.round(mScrollPos)) < 0.4f);
@@ -1061,41 +966,6 @@ public class AllApps3D extends RSSurfaceView
}
}
- class Params extends BaseAlloc {
- Params() {
- mType = Type.createFromClass(sRS, Params.class, 1, "ParamsClass");
- mAlloc = Allocation.createTyped(sRS, mType);
- save();
- }
- public int bubbleWidth;
- public int bubbleHeight;
- public int bubbleBitmapWidth;
- public int bubbleBitmapHeight;
-
- public int homeButtonWidth;
- public int homeButtonHeight;
- public int homeButtonTextureWidth;
- public int homeButtonTextureHeight;
- }
-
- class State extends BaseAlloc {
- public float newPositionX;
- public int newTouchDown;
- public float flingVelocity;
- public int iconCount;
- public int selectedIconIndex = -1;
- public int selectedIconTexture;
- public float zoomTarget;
- public int homeButtonId;
- public float targetPos;
-
- State() {
- mType = Type.createFromClass(sRS, State.class, 1, "StateClass");
- mAlloc = Allocation.createTyped(sRS, mType);
- save();
- }
- }
-
public RolloRS(AllApps3D allApps) {
mAllApps = allApps;
}
@@ -1104,16 +974,21 @@ public class AllApps3D extends RSSurfaceView
mRes = res;
mWidth = width;
mHeight = height;
+ mScript = new ScriptC_allapps(sRS, mRes, R.raw.allapps, true);
+
initProgramVertex();
initProgramFragment();
initProgramStore();
initGl();
initData();
- initRs();
+
+ mScript.bind_gIconIDs(mAllocIconIds);
+ mScript.bind_gLabelIDs(mAllocLabelIds);
+ sRS.contextBindRootScript(mScript);
}
public void initMesh() {
- SimpleMesh.TriangleMeshBuilder tm = new SimpleMesh.TriangleMeshBuilder(sRS, 2, 0);
+ Mesh.TriangleMeshBuilder tm = new Mesh.TriangleMeshBuilder(sRS, 2, 0);
for (int ct=0; ct < 16; ct++) {
float pos = (1.f / (16.f - 1)) * ct;
@@ -1124,12 +999,55 @@ public class AllApps3D extends RSSurfaceView
tm.addTriangle(ct, ct+1, ct+2);
tm.addTriangle(ct+1, ct+3, ct+2);
}
- mMesh = tm.create();
- mMesh.setName("SMCell");
+ mMesh = tm.create(true);
+ mScript.set_gSMCell(mMesh);
+ }
+
+ Matrix4f getProjectionMatrix(int w, int h) {
+ // range -1,1 in the narrow axis at z = 0.
+ Matrix4f m1 = new Matrix4f();
+ Matrix4f m2 = new Matrix4f();
+
+ if(w > h) {
+ float aspect = ((float)w) / h;
+ m1.loadFrustum(-aspect,aspect, -1,1, 1,100);
+ } else {
+ float aspect = ((float)h) / w;
+ m1.loadFrustum(-1,1, -aspect,aspect, 1,100);
+ }
+
+ m2.loadRotate(180, 0, 1, 0);
+ m1.loadMultiply(m1, m2);
+
+ m2.loadScale(-2, 2, 1);
+ m1.loadMultiply(m1, m2);
+
+ m2.loadTranslate(0, 0, 2);
+ m1.loadMultiply(m1, m2);
+ return m1;
}
void resize(int w, int h) {
- mPVA.setupProjectionNormalized(w, h);
+ Matrix4f proj = getProjectionMatrix(w, h);
+ mPVA.loadProjection(proj);
+
+ if (mUniformAlloc != null) {
+ ScriptField_VpConsts.Item i = new ScriptField_VpConsts.Item();
+ i.Proj = proj;
+ i.ScaleOffset.x = (2.f / 480.f);
+ i.ScaleOffset.y = 0;
+ i.ScaleOffset.z = -((float)w / 2) - 0.25f;
+ i.ScaleOffset.w = -380.25f;
+ i.BendPos.x = 120.f;
+ i.BendPos.y = 680.f;
+ if (w > h) {
+ i.ScaleOffset.z = 40.f;
+ i.ScaleOffset.w = h - 40.f;
+ i.BendPos.y = 1.f;
+ }
+ mUniformAlloc.set(i, 0, true);
+ }
+
mWidth = w;
mHeight = h;
}
@@ -1140,22 +1058,18 @@ public class AllApps3D extends RSSurfaceView
ProgramVertex.Builder pvb = new ProgramVertex.Builder(sRS, null, null);
pvb.setTextureMatrixEnable(true);
- mPV = pvb.create();
- mPV.setName("PV");
- mPV.bindAllocation(mPVA);
+ ProgramVertex pv = pvb.create();
+ pv.bindAllocation(mPVA);
+ sRS.contextBindProgramVertex(pv);
- Element.Builder eb = new Element.Builder(sRS);
- eb.add(Element.createVector(sRS, Element.DataType.FLOAT_32, 2), "ImgSize");
- eb.add(Element.createVector(sRS, Element.DataType.FLOAT_32, 4), "Position");
- eb.add(Element.createVector(sRS, Element.DataType.FLOAT_32, 2), "BendPos");
- eb.add(Element.createVector(sRS, Element.DataType.FLOAT_32, 4), "ScaleOffset");
- Element e = eb.create();
-
- mUniformAlloc = Allocation.createSized(sRS, e, 1);
+ mUniformAlloc = new ScriptField_VpConsts(sRS, 1);
+ mScript.bind_vpConstants(mUniformAlloc);
initMesh();
ProgramVertex.ShaderBuilder sb = new ProgramVertex.ShaderBuilder(sRS);
- String t = "void main() {\n" +
+ String t = "varying vec4 varColor;\n" +
+ "varying vec4 varTex0;\n" +
+ "void main() {\n" +
// Animation
" float ani = UNI_Position.z;\n" +
@@ -1205,7 +1119,7 @@ public class AllApps3D extends RSSurfaceView
" pos.z -= ani * 1.5;\n" +
" lum *= 1.0 - ani;\n" +
- " gl_Position = UNI_MVP * pos;\n" +
+ " gl_Position = UNI_Proj * pos;\n" +
" varColor.rgba = vec4(lum, lum, lum, 1.0);\n" +
" varTex0.xy = ATTRIB_position;\n" +
" varTex0.y = 1.0 - varTex0.y;\n" +
@@ -1213,13 +1127,11 @@ public class AllApps3D extends RSSurfaceView
"}\n";
sb.setShader(t);
sb.addConstant(mUniformAlloc.getType());
- sb.addInput(mMesh.getVertexType(0).getElement());
- mPVCurve = sb.create();
- mPVCurve.setName("PVCurve");
- mPVCurve.bindAllocation(mPVA);
- mPVCurve.bindConstants(mUniformAlloc, 1);
+ sb.addInput(mMesh.getVertexAllocation(0).getType().getElement());
+ ProgramVertex pvc = sb.create();
+ pvc.bindConstants(mUniformAlloc.getAllocation(), 0);
- sRS.contextBindProgramVertex(mPV);
+ mScript.set_gPVCurve(pvc);
}
private void initProgramFragment() {
@@ -1237,20 +1149,23 @@ public class AllApps3D extends RSSurfaceView
ProgramFragment.Builder bf = new ProgramFragment.Builder(sRS);
bf.setTexture(ProgramFragment.Builder.EnvMode.MODULATE,
ProgramFragment.Builder.Format.RGBA, 0);
- mPFTexMip = bf.create();
- mPFTexMip.setName("PFTexMip");
- mPFTexMip.bindSampler(linear, 0);
+ bf.setVaryingColor(true);
+ ProgramFragment pfTexMip = bf.create();
+ pfTexMip.bindSampler(linear, 0);
- mPFTexNearest = bf.create();
- mPFTexNearest.setName("PFTexNearest");
- mPFTexNearest.bindSampler(nearest, 0);
+ bf.setVaryingColor(false);
+ ProgramFragment pfTexNearest = bf.create();
+ pfTexNearest.bindSampler(nearest, 0);
bf.setTexture(ProgramFragment.Builder.EnvMode.MODULATE,
ProgramFragment.Builder.Format.ALPHA, 0);
- mPFTexMipAlpha = bf.create();
- mPFTexMipAlpha.setName("PFTexMipAlpha");
- mPFTexMipAlpha.bindSampler(linear, 0);
+ bf.setVaryingColor(true);
+ ProgramFragment pfTexMipAlpha = bf.create();
+ pfTexMipAlpha.bindSampler(linear, 0);
+ mScript.set_gPFTexNearest(pfTexNearest);
+ mScript.set_gPFTexMip(pfTexMip);
+ mScript.set_gPFTexMipAlpha(pfTexMipAlpha);
}
private void initProgramStore() {
@@ -1260,23 +1175,17 @@ public class AllApps3D extends RSSurfaceView
bs.setDitherEnable(true);
bs.setBlendFunc(ProgramStore.BlendSrcFunc.SRC_ALPHA,
ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA);
- mPSIcons = bs.create();
- mPSIcons.setName("PSIcons");
+ mScript.set_gPS(bs.create());
}
private void initGl() {
}
private void initData() {
- mParams = new Params();
- mState = new State();
-
- final Utilities.BubbleText bubble = new Utilities.BubbleText(mAllApps.getContext());
-
- mParams.bubbleWidth = bubble.getBubbleWidth();
- mParams.bubbleHeight = bubble.getMaxBubbleHeight();
- mParams.bubbleBitmapWidth = bubble.getBitmapWidth();
- mParams.bubbleBitmapHeight = bubble.getBitmapHeight();
+ mScript.set_COLUMNS_PER_PAGE_PORTRAIT(Defines.COLUMNS_PER_PAGE_PORTRAIT);
+ mScript.set_ROWS_PER_PAGE_PORTRAIT(Defines.ROWS_PER_PAGE_PORTRAIT);
+ mScript.set_COLUMNS_PER_PAGE_LANDSCAPE(Defines.COLUMNS_PER_PAGE_LANDSCAPE);
+ mScript.set_ROWS_PER_PAGE_LANDSCAPE(Defines.ROWS_PER_PAGE_LANDSCAPE);
mHomeButtonNormal = Allocation.createFromBitmapResource(sRS, mRes,
R.drawable.home_button_normal, Element.RGBA_8888(sRS), false);
@@ -1287,15 +1196,8 @@ public class AllApps3D extends RSSurfaceView
mHomeButtonPressed = Allocation.createFromBitmapResource(sRS, mRes,
R.drawable.home_button_pressed, Element.RGBA_8888(sRS), false);
mHomeButtonPressed.uploadToTexture(0);
- mParams.homeButtonWidth = 76;
- mParams.homeButtonHeight = 68;
- mParams.homeButtonTextureWidth = 128;
- mParams.homeButtonTextureHeight = 128;
- mState.homeButtonId = mHomeButtonNormal.getID();
-
- mParams.save();
- mState.save();
+ mScript.set_gHomeButton(mHomeButtonNormal);
mSelectionBitmap = Bitmap.createBitmap(Defines.SELECTION_TEXTURE_WIDTH_PX,
Defines.SELECTION_TEXTURE_HEIGHT_PX, Bitmap.Config.ARGB_8888);
@@ -1304,30 +1206,6 @@ public class AllApps3D extends RSSurfaceView
setApps(null);
}
- private void initRs() {
- ScriptC.Builder sb = new ScriptC.Builder(sRS);
- sb.setScript(mRes, R.raw.allapps);
- sb.setRoot(true);
- sb.addDefines(mAllApps.mDefines);
- sb.setType(mParams.mType, "params", Defines.ALLOC_PARAMS);
- sb.setType(mState.mType, "state", Defines.ALLOC_STATE);
- sb.setType(mUniformAlloc.getType(), "vpConstants", Defines.ALLOC_VP_CONSTANTS);
- mInvokeMove = sb.addInvokable("move");
- mInvokeFling = sb.addInvokable("fling");
- mInvokeMoveTo = sb.addInvokable("moveTo");
- mInvokeResetWAR = sb.addInvokable("resetHWWar");
- mInvokeSetZoom = sb.addInvokable("setZoom");
- mScript = sb.create();
- mScript.setClearColor(0.0f, 0.0f, 0.0f, 0.0f);
- mScript.bindAllocation(mParams.mAlloc, Defines.ALLOC_PARAMS);
- mScript.bindAllocation(mState.mAlloc, Defines.ALLOC_STATE);
- mScript.bindAllocation(mAllocIconIds, Defines.ALLOC_ICON_IDS);
- mScript.bindAllocation(mAllocLabelIds, Defines.ALLOC_LABEL_IDS);
- mScript.bindAllocation(mUniformAlloc, Defines.ALLOC_VP_CONSTANTS);
-
- sRS.contextBindRootScript(mScript);
- }
-
void dirtyCheck() {
if (sZoomDirty) {
setZoom(mAllApps.sNextZoom, mAllApps.sAnimateNextZoom);
@@ -1345,20 +1223,21 @@ public class AllApps3D extends RSSurfaceView
mIcons = new Allocation[count];
mIconIds = new int[allocCount];
- mAllocIconIds = Allocation.createSized(sRS, Element.USER_I32(sRS), allocCount);
+ mAllocIconIds = Allocation.createSized(sRS, Element.I32(sRS), allocCount);
mLabels = new Allocation[count];
mLabelIds = new int[allocCount];
- mAllocLabelIds = Allocation.createSized(sRS, Element.USER_I32(sRS), allocCount);
+ mAllocLabelIds = Allocation.createSized(sRS, Element.I32(sRS), allocCount);
- mState.iconCount = count;
- for (int i=0; i < mState.iconCount; i++) {
+ mScript.set_gIconCount(count);
+ for (int i=0; i < count; i++) {
createAppIconAllocations(i, list.get(i));
}
- for (int i=0; i < mState.iconCount; i++) {
+ for (int i=0; i < count; i++) {
uploadAppIcon(i, list.get(i));
}
saveAppsList();
+ android.util.Log.e("rs", "setApps");
sRollo.resume();
}
@@ -1367,15 +1246,7 @@ public class AllApps3D extends RSSurfaceView
sRollo.clearSelectedIcon();
sRollo.setHomeSelected(SELECTED_NONE);
}
- if (zoom > 0.001f) {
- sRollo.mState.zoomTarget = zoom;
- } else {
- sRollo.mState.zoomTarget = 0;
- }
- sRollo.mState.save();
- if (!animate) {
- sRollo.mInvokeSetZoom.execute();
- }
+ sRollo.mScript.invoke_setZoom(zoom, animate ? 1 : 0);
}
private void createAppIconAllocations(int index, ApplicationInfo item) {
@@ -1405,13 +1276,13 @@ public class AllApps3D extends RSSurfaceView
private void reallocAppsList(int count) {
Allocation[] icons = new Allocation[count];
int[] iconIds = new int[count];
- mAllocIconIds = Allocation.createSized(sRS, Element.USER_I32(sRS), count);
+ mAllocIconIds = Allocation.createSized(sRS, Element.I32(sRS), count);
Allocation[] labels = new Allocation[count];
int[] labelIds = new int[count];
- mAllocLabelIds = Allocation.createSized(sRS, Element.USER_I32(sRS), count);
+ mAllocLabelIds = Allocation.createSized(sRS, Element.I32(sRS), count);
- final int oldCount = sRollo.mState.iconCount;
+ final int oldCount = sRollo.mScript.get_gIconCount();
System.arraycopy(mIcons, 0, icons, 0, oldCount);
System.arraycopy(mIconIds, 0, iconIds, 0, oldCount);
@@ -1428,7 +1299,7 @@ public class AllApps3D extends RSSurfaceView
* Handle the allocations for the new app. Make sure you call saveAppsList when done.
*/
private void addApp(int index, ApplicationInfo item) {
- final int count = mState.iconCount - index;
+ final int count = mScript.get_gIconCount() - index;
final int dest = index + 1;
System.arraycopy(mIcons, index, mIcons, dest, count);
@@ -1438,14 +1309,15 @@ public class AllApps3D extends RSSurfaceView
createAppIconAllocations(index, item);
uploadAppIcon(index, item);
- sRollo.mState.iconCount++;
+
+ mScript.set_gIconCount(mScript.get_gIconCount() + 1);
}
/**
* Handle the allocations for the removed app. Make sure you call saveAppsList when done.
*/
private void removeApp(int index) {
- final int count = mState.iconCount - index - 1;
+ final int count = mScript.get_gIconCount() - index - 1;
final int src = index + 1;
System.arraycopy(mIcons, src, mIcons, index, count);
@@ -1453,8 +1325,8 @@ public class AllApps3D extends RSSurfaceView
System.arraycopy(mLabels, src, mLabels, index, count);
System.arraycopy(mLabelIds, src, mLabelIds, index, count);
- sRollo.mState.iconCount--;
- final int last = mState.iconCount;
+ mScript.set_gIconCount(mScript.get_gIconCount() - 1);
+ final int last = mScript.get_gIconCount();
mIcons[last] = null;
mIconIds[last] = 0;
@@ -1471,31 +1343,21 @@ public class AllApps3D extends RSSurfaceView
mAllocIconIds.data(mIconIds);
mAllocLabelIds.data(mLabelIds);
- mScript.bindAllocation(mAllocIconIds, Defines.ALLOC_ICON_IDS);
- mScript.bindAllocation(mAllocLabelIds, Defines.ALLOC_LABEL_IDS);
-
- mState.save();
-
- // Note: mScript may be null if we haven't initialized it yet.
- // In that case, this is a no-op.
- if (mInvokeResetWAR != null) {
- mInvokeResetWAR.execute();
- }
+ mScript.bind_gIconIDs(mAllocIconIds);
+ mScript.bind_gLabelIDs(mAllocLabelIds);
}
}
- void fling() {
- mInvokeFling.execute();
+ void fling(float pos, float v) {
+ mScript.invoke_fling(pos, v);
}
- void move() {
- mInvokeMove.execute();
+ void move(float pos) {
+ mScript.invoke_move(pos);
}
void moveTo(float row) {
- mState.targetPos = row;
- mState.save();
- mInvokeMoveTo.execute();
+ mScript.invoke_moveTo(row);
}
/**
@@ -1525,7 +1387,7 @@ public class AllApps3D extends RSSurfaceView
if (mAllApps != null) {
mAllApps.mRestoreFocusIndex = index;
}
- mState.selectedIconIndex = -1;
+ mScript.set_gSelectedIconIndex(-1);
if (mAllApps.mLastSelection == SELECTION_ICONS) {
mAllApps.mLastSelection = SELECTION_NONE;
}
@@ -1534,8 +1396,8 @@ public class AllApps3D extends RSSurfaceView
mAllApps.mLastSelection = SELECTION_ICONS;
}
- int prev = mState.selectedIconIndex;
- mState.selectedIconIndex = index;
+ int prev = mScript.get_gSelectedIconIndex();
+ mScript.set_gSelectedIconIndex(index);
ApplicationInfo info = appsList.get(index);
Bitmap selectionBitmap = mSelectionBitmap;
@@ -1544,10 +1406,10 @@ public class AllApps3D extends RSSurfaceView
selectionBitmap.getWidth(), selectionBitmap.getHeight(),
pressed == SELECTED_PRESSED, info.iconBitmap);
- mSelectedIcon = Allocation.createFromBitmap(sRS, selectionBitmap,
+ Allocation si = Allocation.createFromBitmap(sRS, selectionBitmap,
Element.RGBA_8888(sRS), false);
- mSelectedIcon.uploadToTexture(0);
- mState.selectedIconTexture = mSelectedIcon.getID();
+ si.uploadToTexture(0);
+ mScript.set_gSelectedIconTexture(si);
if (prev != index) {
if (info.title != null && info.title.length() > 0) {
@@ -1562,24 +1424,24 @@ public class AllApps3D extends RSSurfaceView
* You need to call save() on mState on your own after calling this.
*/
void clearSelectedIcon() {
- mState.selectedIconIndex = -1;
+ mScript.set_gSelectedIconIndex(-1);
}
void setHomeSelected(int mode) {
final int prev = mAllApps.mLastSelection;
switch (mode) {
case SELECTED_NONE:
- mState.homeButtonId = mHomeButtonNormal.getID();
+ mScript.set_gHomeButton(mHomeButtonNormal);
break;
case SELECTED_FOCUSED:
mAllApps.mLastSelection = SELECTION_HOME;
- mState.homeButtonId = mHomeButtonFocused.getID();
+ mScript.set_gHomeButton(mHomeButtonFocused);
if (prev != SELECTION_HOME) {
mAllApps.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
}
break;
case SELECTED_PRESSED:
- mState.homeButtonId = mHomeButtonPressed.getID();
+ mScript.set_gHomeButton(mHomeButtonPressed);
break;
}
}
@@ -1599,23 +1461,15 @@ public class AllApps3D extends RSSurfaceView
Log.d(TAG, "sRollo.mLabelIds.length=" + mLabelIds.length);
}
Log.d(TAG, "sRollo.mLabelIds=" + Arrays.toString(mLabelIds));
- Log.d(TAG, "sRollo.mState.newPositionX=" + mState.newPositionX);
- Log.d(TAG, "sRollo.mState.newTouchDown=" + mState.newTouchDown);
- Log.d(TAG, "sRollo.mState.flingVelocity=" + mState.flingVelocity);
- Log.d(TAG, "sRollo.mState.iconCount=" + mState.iconCount);
- Log.d(TAG, "sRollo.mState.selectedIconIndex=" + mState.selectedIconIndex);
- Log.d(TAG, "sRollo.mState.selectedIconTexture=" + mState.selectedIconTexture);
- Log.d(TAG, "sRollo.mState.zoomTarget=" + mState.zoomTarget);
- Log.d(TAG, "sRollo.mState.homeButtonId=" + mState.homeButtonId);
- Log.d(TAG, "sRollo.mState.targetPos=" + mState.targetPos);
- Log.d(TAG, "sRollo.mParams.bubbleWidth=" + mParams.bubbleWidth);
- Log.d(TAG, "sRollo.mParams.bubbleHeight=" + mParams.bubbleHeight);
- Log.d(TAG, "sRollo.mParams.bubbleBitmapWidth=" + mParams.bubbleBitmapWidth);
- Log.d(TAG, "sRollo.mParams.bubbleBitmapHeight=" + mParams.bubbleBitmapHeight);
- Log.d(TAG, "sRollo.mParams.homeButtonWidth=" + mParams.homeButtonWidth);
- Log.d(TAG, "sRollo.mParams.homeButtonHeight=" + mParams.homeButtonHeight);
- Log.d(TAG, "sRollo.mParams.homeButtonTextureWidth=" + mParams.homeButtonTextureWidth);
- Log.d(TAG, "sRollo.mParams.homeButtonTextureHeight=" + mParams.homeButtonTextureHeight);
+ //Log.d(TAG, "sRollo.mState.newPositionX=" + mState.newPositionX);
+ //Log.d(TAG, "sRollo.mState.newTouchDown=" + mState.newTouchDown);
+ //Log.d(TAG, "sRollo.mState.flingVelocity=" + mState.flingVelocity);
+ //Log.d(TAG, "sRollo.mState.iconCount=" + mState.iconCount);
+ //Log.d(TAG, "sRollo.mState.selectedIconIndex=" + mState.selectedIconIndex);
+ //Log.d(TAG, "sRollo.mState.selectedIconTexture=" + mState.selectedIconTexture);
+ //Log.d(TAG, "sRollo.mState.zoomTarget=" + mState.zoomTarget);
+ //Log.d(TAG, "sRollo.mState.homeButtonId=" + mState.homeButtonId);
+ //Log.d(TAG, "sRollo.mState.targetPos=" + mState.targetPos);
}
}
@@ -1646,5 +1500,3 @@ public class AllApps3D extends RSSurfaceView
}
}
}
-
-
diff --git a/src/com/android/launcher2/AllAppsList.java b/src/com/android/launcher2/AllAppsList.java
index 3a5baeaa3..4c9bc5eca 100644
--- a/src/com/android/launcher2/AllAppsList.java
+++ b/src/com/android/launcher2/AllAppsList.java
@@ -16,17 +16,16 @@
package com.android.launcher2;
+import java.util.ArrayList;
+import java.util.List;
+
import android.content.ComponentName;
-import android.content.Intent;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
/**
* Stores the list of all applications for the all apps view.
@@ -92,7 +91,7 @@ class AllAppsList {
if (matches.size() > 0) {
for (ResolveInfo info : matches) {
- add(new ApplicationInfo(info, mIconCache));
+ add(new ApplicationInfo(context.getPackageManager(), info, mIconCache));
}
}
}
@@ -143,7 +142,7 @@ class AllAppsList {
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
if (applicationInfo == null) {
- add(new ApplicationInfo(info, mIconCache));
+ add(new ApplicationInfo(context.getPackageManager(), info, mIconCache));
} else {
mIconCache.remove(applicationInfo.componentName);
mIconCache.getTitleAndIcon(applicationInfo, info);
diff --git a/src/com/android/launcher2/AllAppsPagedView.java b/src/com/android/launcher2/AllAppsPagedView.java
new file mode 100644
index 000000000..3fb167986
--- /dev/null
+++ b/src/com/android/launcher2/AllAppsPagedView.java
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import com.android.launcher.R;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.ActionMode;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+import android.widget.Checkable;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * An implementation of PagedView that populates the pages of the workspace
+ * with all of the user's applications.
+ */
+public class AllAppsPagedView extends PagedView
+ implements AllAppsView, View.OnClickListener, View.OnLongClickListener, DragSource,
+ DropTarget, ActionMode.Callback {
+
+ private static final String TAG = "AllAppsPagedView";
+ private static final boolean DEBUG = false;
+
+ private static final int MENU_DELETE_APP = 1;
+ private static final int MENU_APP_INFO = 2;
+
+ private Launcher mLauncher;
+ private DragController mDragController;
+
+ // preserve compatibility with 3D all apps:
+ // 0.0 -> hidden
+ // 1.0 -> shown and opaque
+ // intermediate values -> partially shown & partially opaque
+ private float mZoom;
+
+ // set of all applications
+ private ArrayList<ApplicationInfo> mApps;
+ private ArrayList<ApplicationInfo> mFilteredApps;
+
+ // the types of applications to filter
+ static final int ALL_APPS_FLAG = -1;
+ private int mAppFilter = ALL_APPS_FLAG;
+
+ private int mCellCountX;
+ private int mCellCountY;
+ private int mPageLayoutPaddingTop;
+ private int mPageLayoutPaddingBottom;
+ private int mPageLayoutPaddingLeft;
+ private int mPageLayoutPaddingRight;
+
+ private final LayoutInflater mInflater;
+
+ private ViewGroup mOrigInfoButtonParent;
+ private LayoutParams mOrigInfoButtonLayoutParams;
+
+ private ViewGroup mOrigDeleteZoneParent;
+ private LayoutParams mOrigDeleteZoneLayoutParams;
+
+ public AllAppsPagedView(Context context) {
+ this(context, null);
+ }
+
+ public AllAppsPagedView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AllAppsPagedView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagedView, defStyle, 0);
+ mCellCountX = a.getInt(R.styleable.PagedView_cellCountX, 6);
+ mCellCountY = a.getInt(R.styleable.PagedView_cellCountY, 4);
+ mPageLayoutPaddingTop = a.getDimensionPixelSize(
+ R.styleable.PagedView_pageLayoutPaddingTop, 10);
+ mPageLayoutPaddingBottom = a.getDimensionPixelSize(
+ R.styleable.PagedView_pageLayoutPaddingBottom, 10);
+ mPageLayoutPaddingLeft = a.getDimensionPixelSize(
+ R.styleable.PagedView_pageLayoutPaddingLeft, 10);
+ mPageLayoutPaddingRight = a.getDimensionPixelSize(
+ R.styleable.PagedView_pageLayoutPaddingRight, 10);
+ mInflater = LayoutInflater.from(context);
+ a.recycle();
+ setSoundEffectsEnabled(false);
+ }
+
+ @Override
+ public void setLauncher(Launcher launcher) {
+ mLauncher = launcher;
+ mLauncher.setAllAppsPagedView(this);
+ }
+
+ @Override
+ public void setDragController(DragController dragger) {
+ mDragController = dragger;
+ }
+
+ public void setAppFilter(int filterType) {
+ mAppFilter = filterType;
+ if (mApps != null) {
+ mFilteredApps = rebuildFilteredApps(mApps);
+ setCurrentPage(0);
+ invalidatePageData();
+ }
+ }
+
+ @Override
+ public void zoom(float zoom, boolean animate) {
+ mZoom = zoom;
+ cancelLongPress();
+
+ if (isVisible()) {
+ getParent().bringChildToFront(this);
+ setVisibility(View.VISIBLE);
+ if (animate) {
+ startAnimation(AnimationUtils.loadAnimation(getContext(),
+ R.anim.all_apps_2d_fade_in));
+ } else {
+ onAnimationEnd();
+ }
+ } else {
+ if (animate) {
+ startAnimation(AnimationUtils.loadAnimation(getContext(),
+ R.anim.all_apps_2d_fade_out));
+ } else {
+ onAnimationEnd();
+ }
+ }
+ }
+
+ protected void onAnimationEnd() {
+ if (!isVisible()) {
+ setVisibility(View.GONE);
+ mZoom = 0.0f;
+
+ endChoiceMode();
+ } else {
+ mZoom = 1.0f;
+ }
+
+ if (mLauncher != null)
+ mLauncher.zoomed(mZoom);
+ }
+
+ private int getChildIndexForGrandChild(View v) {
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; ++i) {
+ final PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(i);
+ if (layout.indexOfChild(v) > -1) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ public void onClick(View v) {
+ // if we are already in a choice mode, then just change the selection
+ if (v instanceof Checkable) {
+ if (!isChoiceMode(CHOICE_MODE_NONE)) {
+ Checkable c = (Checkable) v;
+ if (isChoiceMode(CHOICE_MODE_SINGLE)) {
+ // Uncheck all the other grandchildren, and toggle the clicked one
+ boolean wasChecked = c.isChecked();
+ resetCheckedGrandchildren();
+ c.setChecked(!wasChecked);
+ } else {
+ c.toggle();
+ }
+ if (getCheckedGrandchildren().size() == 0) {
+ endChoiceMode();
+ }
+
+ return;
+ }
+ }
+
+ // otherwise continue and launch the application
+ int childIndex = getChildIndexForGrandChild(v);
+ if (childIndex == getCurrentPage()) {
+ final ApplicationInfo app = (ApplicationInfo) v.getTag();
+
+ // animate some feedback to the click
+ animateClickFeedback(v, new Runnable() {
+ @Override
+ public void run() {
+ mLauncher.startActivitySafely(app.intent, app);
+ }
+ });
+
+ endChoiceMode();
+ }
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (!v.isInTouchMode()) {
+ return false;
+ }
+
+ if (v instanceof Checkable) {
+ // In preparation for drag, we always reset the checked grand children regardless of
+ // what choice mode we are in
+ resetCheckedGrandchildren();
+
+ // Toggle the selection on the dragged app
+ Checkable c = (Checkable) v;
+ c.toggle();
+ }
+
+ // Start choice mode AFTER the item is selected
+ if (isChoiceMode(CHOICE_MODE_NONE)) {
+ startChoiceMode(CHOICE_MODE_SINGLE, this);
+ }
+
+ ApplicationInfo app = (ApplicationInfo) v.getTag();
+ app = new ApplicationInfo(app);
+
+ mDragController.startDrag(v, this, app, DragController.DRAG_ACTION_COPY);
+ return true;
+ }
+
+ @Override
+ public void onDropCompleted(View target, boolean success) {
+ // close the choice action mode if we have a proper drop
+ if (target != this) {
+ endChoiceMode();
+ }
+ }
+
+ @Override
+ public boolean isVisible() {
+ return mZoom > 0.001f;
+ }
+
+ @Override
+ public boolean isAnimating() {
+ return (getAnimation() != null);
+ }
+
+ private ArrayList<ApplicationInfo> rebuildFilteredApps(ArrayList<ApplicationInfo> apps) {
+ ArrayList<ApplicationInfo> filteredApps = new ArrayList<ApplicationInfo>();
+ if (mAppFilter == ALL_APPS_FLAG) {
+ return apps;
+ } else {
+ final int length = apps.size();
+ for (int i = 0; i < length; ++i) {
+ ApplicationInfo info = apps.get(i);
+ if ((info.flags & mAppFilter) > 0) {
+ filteredApps.add(info);
+ }
+ }
+ }
+ return filteredApps;
+ }
+
+ @Override
+ public void setApps(ArrayList<ApplicationInfo> list) {
+ mApps = list;
+ Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR);
+ mFilteredApps = rebuildFilteredApps(mApps);
+ mPageViewIconCache.clear();
+ invalidatePageData();
+ }
+
+ private void addAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
+ // we add it in place, in alphabetical order
+ final int count = list.size();
+ for (int i = 0; i < count; ++i) {
+ final ApplicationInfo info = list.get(i);
+ final int index = Collections.binarySearch(mApps, info, LauncherModel.APP_NAME_COMPARATOR);
+ if (index < 0) {
+ mApps.add(-(index + 1), info);
+ }
+ }
+ mFilteredApps = rebuildFilteredApps(mApps);
+ }
+ @Override
+ public void addApps(ArrayList<ApplicationInfo> list) {
+ addAppsWithoutInvalidate(list);
+ invalidatePageData();
+ }
+
+ private void removeAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
+ // loop through all the apps and remove apps that have the same component
+ final int length = list.size();
+ for (int i = 0; i < length; ++i) {
+ final ApplicationInfo info = list.get(i);
+ int removeIndex = findAppByComponent(mApps, info);
+ if (removeIndex > -1) {
+ mApps.remove(removeIndex);
+ mPageViewIconCache.removeOutline(info);
+ }
+ }
+ mFilteredApps = rebuildFilteredApps(mApps);
+ }
+ @Override
+ public void removeApps(ArrayList<ApplicationInfo> list) {
+ removeAppsWithoutInvalidate(list);
+ invalidatePageData();
+ }
+
+ @Override
+ public void updateApps(ArrayList<ApplicationInfo> list) {
+ removeAppsWithoutInvalidate(list);
+ addAppsWithoutInvalidate(list);
+ invalidatePageData();
+ }
+
+ private int findAppByComponent(ArrayList<ApplicationInfo> list, ApplicationInfo item) {
+ ComponentName removeComponent = item.intent.getComponent();
+ final int length = list.size();
+ for (int i = 0; i < length; ++i) {
+ ApplicationInfo info = list.get(i);
+ if (info.intent.getComponent().equals(removeComponent)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ public void dumpState() {
+ ApplicationInfo.dumpApplicationInfoList(TAG, "mApps", mApps);
+ }
+
+ @Override
+ public void surrender() {
+ // do nothing?
+ }
+
+ @Override
+ public void syncPages() {
+ // ensure that we have the right number of pages
+ int numPages = (int) Math.ceil((float) mFilteredApps.size() / (mCellCountX * mCellCountY));
+ int curNumPages = getChildCount();
+ // remove any extra pages after the "last" page
+ int extraPageDiff = curNumPages - numPages;
+ for (int i = 0; i < extraPageDiff; ++i) {
+ removeViewAt(numPages);
+ }
+ // add any necessary pages
+ for (int i = curNumPages; i < numPages; ++i) {
+ PagedViewCellLayout layout = new PagedViewCellLayout(getContext());
+ layout.setCellCount(mCellCountX, mCellCountY);
+ layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop,
+ mPageLayoutPaddingRight, mPageLayoutPaddingBottom);
+ addView(layout);
+ }
+
+ // bound the current page
+ setCurrentPage(Math.max(0, Math.min(numPages - 1, getCurrentPage())));
+ }
+
+ @Override
+ public void syncPageItems(int page) {
+ // ensure that we have the right number of items on the pages
+ final int cellsPerPage = mCellCountX * mCellCountY;
+ final int startIndex = page * cellsPerPage;
+ final int endIndex = Math.min(startIndex + cellsPerPage, mFilteredApps.size());
+ PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page);
+
+ final int curNumPageItems = layout.getChildCount();
+ final int numPageItems = endIndex - startIndex;
+
+ // remove any extra items
+ int extraPageItemsDiff = curNumPageItems - numPageItems;
+ for (int i = 0; i < extraPageItemsDiff; ++i) {
+ layout.removeViewAt(numPageItems);
+ }
+ // add any necessary items
+ for (int i = curNumPageItems; i < numPageItems; ++i) {
+ TextView text = (TextView) mInflater.inflate(R.layout.all_apps_paged_view_application, layout, false);
+ text.setOnClickListener(this);
+ text.setOnLongClickListener(this);
+
+ layout.addViewToCellLayout(text, -1, i,
+ new PagedViewCellLayout.LayoutParams(0, 0, 1, 1));
+ }
+
+ // actually reapply to the existing text views
+ for (int i = startIndex; i < endIndex; ++i) {
+ final int index = i - startIndex;
+ final ApplicationInfo info = mFilteredApps.get(i);
+ PagedViewIcon icon = (PagedViewIcon) layout.getChildAt(index);
+ icon.applyFromApplicationInfo(info, mPageViewIconCache);
+
+ PagedViewCellLayout.LayoutParams params =
+ (PagedViewCellLayout.LayoutParams) icon.getLayoutParams();
+ params.cellX = index % mCellCountX;
+ params.cellY = index / mCellCountX;
+ }
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ mode.setTitle(R.string.cab_app_selection_text);
+
+ // Until the workspace has a selection mode and the CAB supports drag-and-drop, we
+ // take a hybrid approach: grab the views from the workspace and stuff them into the CAB.
+ // When the action mode is done, restore the views to their original place in the toolbar.
+
+ ApplicationInfoDropTarget infoButton =
+ (ApplicationInfoDropTarget) mLauncher.findViewById(R.id.info_button);
+ mOrigInfoButtonParent = (ViewGroup) infoButton.getParent();
+ mOrigInfoButtonLayoutParams = infoButton.getLayoutParams();
+ mOrigInfoButtonParent.removeView(infoButton);
+ infoButton.setManageVisibility(false);
+ infoButton.setVisibility(View.VISIBLE);
+
+ DeleteZone deleteZone = (DeleteZone) mLauncher.findViewById(R.id.delete_zone);
+ mOrigDeleteZoneParent = (ViewGroup) deleteZone.getParent();
+ mOrigDeleteZoneLayoutParams = deleteZone.getLayoutParams();
+ mOrigDeleteZoneParent.removeView(deleteZone);
+ deleteZone.setManageVisibility(false);
+ deleteZone.setVisibility(View.VISIBLE);
+
+ menu.add(0, MENU_APP_INFO, 0, R.string.cab_menu_app_info).setActionView(infoButton);
+ menu.add(0, MENU_DELETE_APP, 0, R.string.cab_menu_delete_app).setActionView(deleteZone);
+
+ return true;
+ }
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ mDragController.addDropTarget(this);
+ return true;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ // Re-parent the drop targets into the toolbar, and restore their layout params
+ ApplicationInfoDropTarget infoButton =
+ (ApplicationInfoDropTarget) mLauncher.findViewById(R.id.info_button);
+ ((ViewGroup) infoButton.getParent()).removeView(infoButton);
+ mOrigInfoButtonParent.addView(infoButton, mOrigInfoButtonLayoutParams);
+ infoButton.setVisibility(View.GONE);
+ infoButton.setManageVisibility(true);
+
+ DeleteZone deleteZone = (DeleteZone) mLauncher.findViewById(R.id.delete_zone);
+ ((ViewGroup) deleteZone.getParent()).removeView(deleteZone);
+ mOrigDeleteZoneParent.addView(deleteZone, mOrigDeleteZoneLayoutParams);
+ deleteZone.setVisibility(View.GONE);
+ deleteZone.setManageVisibility(true);
+
+ mDragController.removeDropTarget(this);
+ endChoiceMode();
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ final int id = item.getItemId();
+
+ // Assumes that we are in CHOICE_MODE_SINGLE
+ final ApplicationInfo appInfo = (ApplicationInfo) getChosenItem();
+
+ if (id == MENU_APP_INFO) {
+ mLauncher.startApplicationDetailsActivity(appInfo.componentName);
+ } else if (id == MENU_DELETE_APP) {
+ mLauncher.startApplicationUninstallActivity(appInfo);
+ }
+ return false;
+ }
+
+ /*
+ * We don't actually use AllAppsPagedView as a drop target... it's only used to intercept a drop
+ * to the workspace.
+ */
+ @Override
+ public boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset,
+ DragView dragView, Object dragInfo) {
+ return false;
+ }
+ @Override
+ public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset,
+ int yOffset, DragView dragView, Object dragInfo) {
+ return null;
+ }
+ @Override
+ public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
+ DragView dragView, Object dragInfo) {}
+ @Override
+ public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,
+ DragView dragView, Object dragInfo) {}
+ @Override
+ public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
+ DragView dragView, Object dragInfo) {}
+ @Override
+ public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
+ DragView dragView, Object dragInfo) {}
+
+ public boolean isDropEnabled() {
+ return true;
+ }
+}
diff --git a/src/com/android/launcher2/AllAppsTabbed.java b/src/com/android/launcher2/AllAppsTabbed.java
new file mode 100644
index 000000000..368306f50
--- /dev/null
+++ b/src/com/android/launcher2/AllAppsTabbed.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import java.util.ArrayList;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TabHost;
+import android.widget.TabWidget;
+import android.widget.TextView;
+
+import com.android.launcher.R;
+
+/**
+ * Implements a tabbed version of AllApps2D.
+ */
+public class AllAppsTabbed extends TabHost implements AllAppsView {
+
+ private static final String TAG = "Launcher.AllAppsTabbed";
+
+ private static final String TAG_ALL = "ALL";
+ private static final String TAG_APPS = "APPS";
+ private static final String TAG_GAMES = "GAMES";
+ private static final String TAG_DOWNLOADED = "DOWNLOADED";
+
+ private AllAppsPagedView mAllApps;
+ private Context mContext;
+
+ public AllAppsTabbed(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ // setup the tab host
+ setup();
+
+ try {
+ mAllApps = (AllAppsPagedView) findViewById(R.id.all_apps_paged_view);
+ if (mAllApps == null) throw new Resources.NotFoundException();
+ } catch (Resources.NotFoundException e) {
+ Log.e(TAG, "Can't find necessary layout elements for AllAppsTabbed");
+ }
+
+ // share the same AllApps workspace across all the tabs
+ TabContentFactory contentFactory = new TabContentFactory() {
+ public View createTabContent(String tag) {
+ return mAllApps;
+ }
+ };
+
+ String label = mContext.getString(R.string.all_apps_tab_all);
+ addTab(newTabSpec(TAG_ALL).setIndicator(label).setContent(contentFactory));
+
+ label = mContext.getString(R.string.all_apps_tab_apps);
+ addTab(newTabSpec(TAG_APPS).setIndicator(label).setContent(contentFactory));
+
+ label = mContext.getString(R.string.all_apps_tab_games);
+ addTab(newTabSpec(TAG_GAMES).setIndicator(label).setContent(contentFactory));
+
+ label = mContext.getString(R.string.all_apps_tab_downloaded);
+ addTab(newTabSpec(TAG_DOWNLOADED).setIndicator(label).setContent(contentFactory));
+
+ // TEMP: just styling the tab widget to be a bit nicer until we get the actual
+ // new assets
+ TabWidget tabWidget = getTabWidget();
+ for (int i = 0; i < tabWidget.getChildCount(); ++i) {
+ RelativeLayout tab = (RelativeLayout) tabWidget.getChildTabViewAt(i);
+ TextView text = (TextView) tab.getChildAt(1);
+ text.setTextSize(20.0f);
+ text.setPadding(20, 0, 20, 0);
+ text.setShadowLayer(1.0f, 0.0f, 1.0f, Color.BLACK);
+ tab.setBackgroundDrawable(null);
+ }
+
+ setOnTabChangedListener(new OnTabChangeListener() {
+ public void onTabChanged(String tabId) {
+ // animate the changing of the tab content by fading pages in and out
+ final int duration = 150;
+ final float alpha = mAllApps.getAlpha();
+ ValueAnimator alphaAnim = new ObjectAnimator(duration, mAllApps, "alpha", alpha, 0.0f);
+ alphaAnim.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ String tag = getCurrentTabTag();
+ if (tag == TAG_ALL) {
+ mAllApps.setAppFilter(AllAppsPagedView.ALL_APPS_FLAG);
+ } else if (tag == TAG_APPS) {
+ mAllApps.setAppFilter(ApplicationInfo.APP_FLAG);
+ } else if (tag == TAG_GAMES) {
+ mAllApps.setAppFilter(ApplicationInfo.GAME_FLAG);
+ } else if (tag == TAG_DOWNLOADED) {
+ mAllApps.setAppFilter(ApplicationInfo.DOWNLOADED_FLAG);
+ }
+
+ final float alpha = mAllApps.getAlpha();
+ ValueAnimator alphaAnim =
+ new ObjectAnimator(duration, mAllApps, "alpha", alpha, 1.0f);
+ alphaAnim.start();
+ }
+ });
+ alphaAnim.start();
+ }
+ });
+
+ setCurrentTab(0);
+
+ // It needs to be INVISIBLE so that it will be measured in the layout.
+ // Otherwise the animations is messed up when we show it for the first time.
+ setVisibility(INVISIBLE);
+ }
+
+ @Override
+ public void setLauncher(Launcher launcher) {
+ mAllApps.setLauncher(launcher);
+ }
+
+ @Override
+ public void setDragController(DragController dragger) {
+ mAllApps.setDragController(dragger);
+ }
+
+ @Override
+ public void zoom(float zoom, boolean animate) {
+ // NOTE: animate parameter is ignored for the TabHost itself
+ setVisibility((zoom == 0.0f) ? View.GONE : View.VISIBLE);
+ mAllApps.zoom(zoom, animate);
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ final boolean isVisible = (visibility == View.VISIBLE);
+ super.setVisibility(visibility);
+ float zoom = (isVisible ? 1.0f : 0.0f);
+ mAllApps.zoom(zoom, false);
+ }
+
+ @Override
+ public boolean isVisible() {
+ return mAllApps.isVisible();
+ }
+
+ @Override
+ public boolean isAnimating() {
+ return (getAnimation() != null);
+ }
+
+ @Override
+ public void setApps(ArrayList<ApplicationInfo> list) {
+ mAllApps.setApps(list);
+ }
+
+ @Override
+ public void addApps(ArrayList<ApplicationInfo> list) {
+ mAllApps.addApps(list);
+ }
+
+ @Override
+ public void removeApps(ArrayList<ApplicationInfo> list) {
+ mAllApps.removeApps(list);
+ }
+
+ @Override
+ public void updateApps(ArrayList<ApplicationInfo> list) {
+ mAllApps.updateApps(list);
+ }
+
+ @Override
+ public void dumpState() {
+ mAllApps.dumpState();
+ }
+
+ @Override
+ public void surrender() {
+ mAllApps.surrender();
+ }
+}
diff --git a/src/com/android/launcher2/AllAppsView.java b/src/com/android/launcher2/AllAppsView.java
index 877c07577..007ecf8d6 100644
--- a/src/com/android/launcher2/AllAppsView.java
+++ b/src/com/android/launcher2/AllAppsView.java
@@ -31,7 +31,7 @@ public interface AllAppsView {
public boolean isVisible();
- public boolean isOpaque();
+ public boolean isAnimating();
public void setApps(ArrayList<ApplicationInfo> list);
diff --git a/src/com/android/launcher2/ApplicationInfo.java b/src/com/android/launcher2/ApplicationInfo.java
index 5bb503780..0851cd350 100644
--- a/src/com/android/launcher2/ApplicationInfo.java
+++ b/src/com/android/launcher2/ApplicationInfo.java
@@ -17,12 +17,11 @@
package com.android.launcher2;
import android.content.ComponentName;
-import android.content.ContentValues;
-import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
import android.util.Log;
import java.util.ArrayList;
@@ -31,6 +30,7 @@ import java.util.ArrayList;
* Represents an app in AllAppsView.
*/
class ApplicationInfo extends ItemInfo {
+ private static final String TAG = "Launcher2.ApplicationInfo";
/**
* The application name.
@@ -54,6 +54,10 @@ class ApplicationInfo extends ItemInfo {
ComponentName componentName;
+ static final int APP_FLAG = 1;
+ static final int GAME_FLAG = 2;
+ static final int DOWNLOADED_FLAG = 4;
+ int flags = 0;
ApplicationInfo() {
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
@@ -62,15 +66,32 @@ class ApplicationInfo extends ItemInfo {
/**
* Must not hold the Context.
*/
- public ApplicationInfo(ResolveInfo info, IconCache iconCache) {
- this.componentName = new ComponentName(
- info.activityInfo.applicationInfo.packageName,
- info.activityInfo.name);
+ public ApplicationInfo(PackageManager pm, ResolveInfo info, IconCache iconCache) {
+ final String packageName = info.activityInfo.applicationInfo.packageName;
+ this.componentName = new ComponentName(packageName, info.activityInfo.name);
this.container = ItemInfo.NO_ID;
this.setActivity(componentName,
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ try {
+ int appFlags = pm.getApplicationInfo(packageName, 0).flags;
+ if ((appFlags & android.content.pm.ApplicationInfo.FLAG_SYSTEM) == 0) {
+ flags |= DOWNLOADED_FLAG;
+ }
+ if ((appFlags & android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ flags |= DOWNLOADED_FLAG;
+ }
+ // TODO: Figure out how to determine what is a game
+
+ // If it's not a game, it's an app
+ if ((flags & GAME_FLAG) == 0) {
+ flags |= APP_FLAG;
+ }
+ } catch (NameNotFoundException e) {
+ Log.d(TAG, "PackageManager.getApplicationInfo failed for " + packageName);
+ }
+
iconCache.getTitleAndIcon(this, info);
}
@@ -79,6 +100,7 @@ class ApplicationInfo extends ItemInfo {
componentName = info.componentName;
title = info.title.toString();
intent = new Intent(info.intent);
+ flags = info.flags;
}
/**
diff --git a/src/com/android/launcher2/ApplicationInfoDropTarget.java b/src/com/android/launcher2/ApplicationInfoDropTarget.java
new file mode 100644
index 000000000..cb45b3aab
--- /dev/null
+++ b/src/com/android/launcher2/ApplicationInfoDropTarget.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+
+/**
+ * Implements a DropTarget which allows applications to be dropped on it,
+ * in order to launch the application info for that app.
+ */
+public class ApplicationInfoDropTarget extends ImageView implements DropTarget, DragController.DragListener {
+ private Launcher mLauncher;
+ private boolean mActive = false;
+
+ /**
+ * If true, this View responsible for managing its own visibility, and that of its handle.
+ * This is generally the case, but it will be set to false when this is part of the
+ * Contextual Action Bar.
+ */
+ private boolean mManageVisibility = true;
+
+ /** The view that this view should appear in the place of. */
+ private View mHandle = null;
+
+ private final Paint mPaint = new Paint();
+
+ public ApplicationInfoDropTarget(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ApplicationInfoDropTarget(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * Set the color that will be used as a filter over objects dragged over this object.
+ */
+ public void setDragColor(int color) {
+ mPaint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
+ }
+
+ public boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset,
+ DragView dragView, Object dragInfo) {
+
+ // acceptDrop is called just before onDrop. We do the work here, rather than
+ // in onDrop, because it allows us to reject the drop (by returning false)
+ // so that the object being dragged isn't removed from the home screen.
+
+ ComponentName componentName = null;
+ if (dragInfo instanceof ApplicationInfo) {
+ componentName = ((ApplicationInfo)dragInfo).componentName;
+ } else if (dragInfo instanceof ShortcutInfo) {
+ componentName = ((ShortcutInfo)dragInfo).intent.getComponent();
+ }
+ mLauncher.startApplicationDetailsActivity(componentName);
+ return false;
+ }
+
+ public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
+ DragView dragView, Object dragInfo) {
+
+ }
+
+ public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
+ DragView dragView, Object dragInfo) {
+ dragView.setPaint(mPaint);
+ }
+
+ public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
+ DragView dragView, Object dragInfo) {
+ }
+
+ public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,
+ DragView dragView, Object dragInfo) {
+ dragView.setPaint(null);
+ }
+
+ public void onDragStart(DragSource source, Object info, int dragAction) {
+ if (info != null) {
+ mActive = true;
+
+ if (mManageVisibility) {
+ // Only show the info icon when an application is selected
+ if (((ItemInfo)info).itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+ setVisibility(VISIBLE);
+ }
+ mHandle.setVisibility(INVISIBLE);
+ }
+ }
+ }
+
+ public boolean isDropEnabled() {
+ return true;
+ }
+
+ public void onDragEnd() {
+ if (mActive) {
+ mActive = false;
+ if (mManageVisibility) {
+ setVisibility(GONE);
+ mHandle.setVisibility(VISIBLE);
+ }
+ }
+ }
+
+ void setLauncher(Launcher launcher) {
+ mLauncher = launcher;
+ }
+
+ void setHandle(View view) {
+ mHandle = view;
+ }
+
+ void setManageVisibility(boolean value) {
+ mManageVisibility = value;
+ }
+
+ @Override
+ public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
+ DragView dragView, Object dragInfo) {
+ return null;
+ }
+}
diff --git a/src/com/android/launcher2/BubbleTextView.java b/src/com/android/launcher2/BubbleTextView.java
index 4a56e1bb0..076f574de 100644
--- a/src/com/android/launcher2/BubbleTextView.java
+++ b/src/com/android/launcher2/BubbleTextView.java
@@ -144,4 +144,10 @@ public class BubbleTextView extends TextView {
super.onDetachedFromWindow();
mBackground.setCallback(null);
}
+
+ @Override
+ protected boolean onSetAlpha(int alpha) {
+ mPaint.setAlpha(alpha);
+ return super.onSetAlpha(alpha);
+ }
}
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 9d39c2ca7..0caaf2e2c 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -16,54 +16,81 @@
package com.android.launcher2;
+import com.android.launcher.R;
+
+import android.app.WallpaperManager;
import android.content.Context;
-import android.content.res.TypedArray;
import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.ContextMenu;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
-import android.app.WallpaperManager;
-
-import java.util.ArrayList;
+import android.view.animation.Animation;
+import android.view.animation.LayoutAnimationController;
-import com.android.launcher.R;
+import java.util.Arrays;
-public class CellLayout extends ViewGroup {
- private boolean mPortrait;
+public class CellLayout extends ViewGroup implements Dimmable {
+ static final String TAG = "CellLayout";
private int mCellWidth;
private int mCellHeight;
-
- private int mLongAxisStartPadding;
- private int mLongAxisEndPadding;
- private int mShortAxisStartPadding;
- private int mShortAxisEndPadding;
+ private int mLeftPadding;
+ private int mRightPadding;
+ private int mTopPadding;
+ private int mBottomPadding;
- private int mShortAxisCells;
- private int mLongAxisCells;
+ private int mCountX;
+ private int mCountY;
private int mWidthGap;
private int mHeightGap;
private final Rect mRect = new Rect();
+ private final RectF mRectF = new RectF();
private final CellInfo mCellInfo = new CellInfo();
-
- int[] mCellXY = new int[2];
+
+ // This is a temporary variable to prevent having to allocate a new object just to
+ // return an (x, y) value from helper functions. Do NOT use it to maintain other state.
+ private final int[] mTmpCellXY = new int[2];
+
boolean[][] mOccupied;
- private RectF mDragRect = new RectF();
+ private OnTouchListener mInterceptTouchListener;
+
+ private float mBackgroundAlpha;
+ private final Rect mBackgroundLayoutRect = new Rect();
- private boolean mDirtyTag;
- private boolean mLastDownOnOccupiedCell = false;
-
- private final WallpaperManager mWallpaperManager;
+ private Drawable mBackground;
+ private Drawable mBackgroundMini;
+ private Drawable mBackgroundMiniHover;
+ // If we're actively dragging something over this screen and it's small,
+ // mHover is true
+ private boolean mHover = false;
+
+ private final RectF mDragRect = new RectF();
+
+ // When dragging, used to indicate a vacant drop location
+ private Drawable mVacantDrawable;
+
+ // When dragging, used to indicate an occupied drop location
+ private Drawable mOccupiedDrawable;
+
+ // Updated to point to mVacantDrawable or mOccupiedDrawable, as appropriate
+ private Drawable mDragRectDrawable;
+
+ // When a drag operation is in progress, holds the nearest cell to the touch point
+ private final int[] mDragCell = new int[2];
+
+ private final WallpaperManager mWallpaperManager;
public CellLayout(Context context) {
this(context, null);
@@ -75,44 +102,99 @@ public class CellLayout extends ViewGroup {
public CellLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+
+ // A ViewGroup usually does not draw, but CellLayout needs to draw a rectangle to show
+ // the user where a dragged item will land when dropped.
+ setWillNotDraw(false);
+ mVacantDrawable = getResources().getDrawable(R.drawable.rounded_rect_green);
+ mOccupiedDrawable = getResources().getDrawable(R.drawable.rounded_rect_red);
+
+ if (LauncherApplication.isScreenXLarge()) {
+ mBackgroundMini = getResources().getDrawable(R.drawable.mini_home_screen_bg);
+ mBackgroundMini.setFilterBitmap(true);
+ mBackground = getResources().getDrawable(R.drawable.home_screen_bg);
+ mBackground.setFilterBitmap(true);
+ mBackgroundMiniHover = getResources().getDrawable(R.drawable.mini_home_screen_bg_hover);
+ mBackgroundMiniHover.setFilterBitmap(true);
+ }
+
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
mCellWidth = a.getDimensionPixelSize(R.styleable.CellLayout_cellWidth, 10);
mCellHeight = a.getDimensionPixelSize(R.styleable.CellLayout_cellHeight, 10);
-
- mLongAxisStartPadding =
- a.getDimensionPixelSize(R.styleable.CellLayout_longAxisStartPadding, 10);
- mLongAxisEndPadding =
- a.getDimensionPixelSize(R.styleable.CellLayout_longAxisEndPadding, 10);
- mShortAxisStartPadding =
- a.getDimensionPixelSize(R.styleable.CellLayout_shortAxisStartPadding, 10);
- mShortAxisEndPadding =
- a.getDimensionPixelSize(R.styleable.CellLayout_shortAxisEndPadding, 10);
-
- mShortAxisCells = a.getInt(R.styleable.CellLayout_shortAxisCells, 4);
- mLongAxisCells = a.getInt(R.styleable.CellLayout_longAxisCells, 4);
+
+ mLeftPadding =
+ a.getDimensionPixelSize(R.styleable.CellLayout_xAxisStartPadding, 10);
+ mRightPadding =
+ a.getDimensionPixelSize(R.styleable.CellLayout_xAxisEndPadding, 10);
+ mTopPadding =
+ a.getDimensionPixelSize(R.styleable.CellLayout_yAxisStartPadding, 10);
+ mBottomPadding =
+ a.getDimensionPixelSize(R.styleable.CellLayout_yAxisEndPadding, 10);
+
+ mCountX = LauncherModel.getCellCountX();
+ mCountY = LauncherModel.getCellCountY();
+ mOccupied = new boolean[mCountX][mCountY];
a.recycle();
setAlwaysDrawnWithCacheEnabled(false);
- if (mOccupied == null) {
- if (mPortrait) {
- mOccupied = new boolean[mShortAxisCells][mLongAxisCells];
- } else {
- mOccupied = new boolean[mLongAxisCells][mShortAxisCells];
- }
- }
-
mWallpaperManager = WallpaperManager.getInstance(getContext());
}
+ public void setHover(boolean value) {
+ if (mHover != value) {
+ invalidate();
+ }
+ mHover = value;
+ }
+
@Override
public void dispatchDraw(Canvas canvas) {
+ if (mBackgroundAlpha > 0.0f) {
+ Drawable bg;
+ if (mHover && getScaleX() < 0.5f) {
+ bg = mBackgroundMiniHover;
+ } else if (getScaleX() < 0.5f) {
+ bg = mBackgroundMini;
+ } else {
+ bg = mBackground;
+ }
+ bg.setAlpha((int) (mBackgroundAlpha * 255));
+ bg.draw(canvas);
+ }
super.dispatchDraw(canvas);
}
@Override
+ protected void onDraw(Canvas canvas) {
+ if (!mDragRect.isEmpty()) {
+ mDragRectDrawable.setBounds(
+ (int)mDragRect.left,
+ (int)mDragRect.top,
+ (int)mDragRect.right,
+ (int)mDragRect.bottom);
+ mDragRectDrawable.draw(canvas);
+ }
+ super.onDraw(canvas);
+ }
+
+ public void setDimmableProgress(float progress) {
+ for (int i = 0; i < getChildCount(); i++) {
+ Dimmable d = (Dimmable) getChildAt(i);
+ d.setDimmableProgress(progress);
+ }
+ }
+
+ public float getDimmableProgress() {
+ if (getChildCount() > 0) {
+ return ((Dimmable) getChildAt(0)).getDimmableProgress();
+ }
+ return 0.0f;
+ }
+
+ @Override
public void cancelLongPress() {
super.cancelLongPress();
@@ -124,22 +206,86 @@ public class CellLayout extends ViewGroup {
}
}
+ public void setOnInterceptTouchListener(View.OnTouchListener listener) {
+ mInterceptTouchListener = listener;
+ }
+
int getCountX() {
- return mPortrait ? mShortAxisCells : mLongAxisCells;
+ return mCountX;
}
int getCountY() {
- return mPortrait ? mLongAxisCells : mShortAxisCells;
+ return mCountY;
}
- @Override
- public void addView(View child, int index, ViewGroup.LayoutParams params) {
+ // Takes canonical layout parameters
+ public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params) {
+ final LayoutParams lp = params;
+
// Generate an id for each view, this assumes we have at most 256x256 cells
// per workspace screen
- final LayoutParams cellParams = (LayoutParams) params;
- cellParams.regenerateId = true;
+ if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {
+ // If the horizontal or vertical span is set to -1, it is taken to
+ // mean that it spans the extent of the CellLayout
+ if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
+ if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
+
+ child.setId(childId);
+
+ // We might be in the middle or end of shrinking/fading to a dimmed view
+ // Make sure this view's alpha is set the same as all the rest of the views
+ child.setAlpha(getAlpha());
+ addView(child, index, lp);
+
+ markCellsAsOccupiedForView(child);
+
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void removeAllViews() {
+ clearOccupiedCells();
+ }
+
+ @Override
+ public void removeAllViewsInLayout() {
+ clearOccupiedCells();
+ }
+
+ @Override
+ public void removeView(View view) {
+ markCellsAsUnoccupiedForView(view);
+ super.removeView(view);
+ }
+
+ @Override
+ public void removeViewAt(int index) {
+ markCellsAsUnoccupiedForView(getChildAt(index));
+ super.removeViewAt(index);
+ }
- super.addView(child, index, params);
+ @Override
+ public void removeViewInLayout(View view) {
+ markCellsAsUnoccupiedForView(view);
+ super.removeViewInLayout(view);
+ }
+
+ @Override
+ public void removeViews(int start, int count) {
+ for (int i = start; i < start + count; i++) {
+ markCellsAsUnoccupiedForView(getChildAt(i));
+ }
+ super.removeViews(start, count);
+ }
+
+ @Override
+ public void removeViewsInLayout(int start, int count) {
+ for (int i = start; i < start + count; i++) {
+ markCellsAsUnoccupiedForView(getChildAt(i));
+ }
+ super.removeViewsInLayout(start, count);
}
@Override
@@ -158,67 +304,59 @@ public class CellLayout extends ViewGroup {
mCellInfo.screen = ((ViewGroup) getParent()).indexOfChild(this);
}
+ public void setTagToCellInfoForPoint(int touchX, int touchY) {
+ final CellInfo cellInfo = mCellInfo;
+ final Rect frame = mRect;
+ final int x = touchX + mScrollX;
+ final int y = touchY + mScrollY;
+ final int count = getChildCount();
+
+ boolean found = false;
+ for (int i = count - 1; i >= 0; i--) {
+ final View child = getChildAt(i);
+
+ if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
+ child.getHitRect(frame);
+ if (frame.contains(x, y)) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ cellInfo.cell = child;
+ cellInfo.cellX = lp.cellX;
+ cellInfo.cellY = lp.cellY;
+ cellInfo.spanX = lp.cellHSpan;
+ cellInfo.spanY = lp.cellVSpan;
+ cellInfo.valid = true;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ final int cellXY[] = mTmpCellXY;
+ pointToCellExact(x, y, cellXY);
+
+ cellInfo.cell = null;
+ cellInfo.cellX = cellXY[0];
+ cellInfo.cellY = cellXY[1];
+ cellInfo.spanX = 1;
+ cellInfo.spanY = 1;
+ cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < mCountX &&
+ cellXY[1] < mCountY && !mOccupied[cellXY[0]][cellXY[1]];
+ }
+ setTag(cellInfo);
+ }
+
+
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) {
+ return true;
+ }
final int action = ev.getAction();
final CellInfo cellInfo = mCellInfo;
if (action == MotionEvent.ACTION_DOWN) {
- final Rect frame = mRect;
- final int x = (int) ev.getX() + mScrollX;
- final int y = (int) ev.getY() + mScrollY;
- final int count = getChildCount();
-
- boolean found = false;
- for (int i = count - 1; i >= 0; i--) {
- final View child = getChildAt(i);
-
- if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
- child.getHitRect(frame);
- if (frame.contains(x, y)) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- cellInfo.cell = child;
- cellInfo.cellX = lp.cellX;
- cellInfo.cellY = lp.cellY;
- cellInfo.spanX = lp.cellHSpan;
- cellInfo.spanY = lp.cellVSpan;
- cellInfo.valid = true;
- found = true;
- mDirtyTag = false;
- break;
- }
- }
- }
-
- mLastDownOnOccupiedCell = found;
-
- if (!found) {
- int cellXY[] = mCellXY;
- pointToCellExact(x, y, cellXY);
-
- final boolean portrait = mPortrait;
- final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
- final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
-
- final boolean[][] occupied = mOccupied;
- findOccupiedCells(xCount, yCount, occupied, null);
-
- cellInfo.cell = null;
- cellInfo.cellX = cellXY[0];
- cellInfo.cellY = cellXY[1];
- cellInfo.spanX = 1;
- cellInfo.spanY = 1;
- cellInfo.valid = cellXY[0] >= 0 && cellXY[1] >= 0 && cellXY[0] < xCount &&
- cellXY[1] < yCount && !occupied[cellXY[0]][cellXY[1]];
-
- // Instead of finding the interesting vacant cells here, wait until a
- // caller invokes getTag() to retrieve the result. Finding the vacant
- // cells is a bit expensive and can generate many new objects, it's
- // therefore better to defer it until we know we actually need it.
-
- mDirtyTag = true;
- }
- setTag(cellInfo);
+ setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY());
} else if (action == MotionEvent.ACTION_UP) {
cellInfo.cell = null;
cellInfo.cellX = -1;
@@ -226,7 +364,6 @@ public class CellLayout extends ViewGroup {
cellInfo.spanX = 0;
cellInfo.spanY = 0;
cellInfo.valid = false;
- mDirtyTag = false;
setTag(cellInfo);
}
@@ -235,104 +372,12 @@ public class CellLayout extends ViewGroup {
@Override
public CellInfo getTag() {
- final CellInfo info = (CellInfo) super.getTag();
- if (mDirtyTag && info.valid) {
- final boolean portrait = mPortrait;
- final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
- final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
-
- final boolean[][] occupied = mOccupied;
- findOccupiedCells(xCount, yCount, occupied, null);
-
- findIntersectingVacantCells(info, info.cellX, info.cellY, xCount, yCount, occupied);
-
- mDirtyTag = false;
- }
- return info;
- }
-
- private static void findIntersectingVacantCells(CellInfo cellInfo, int x, int y,
- int xCount, int yCount, boolean[][] occupied) {
-
- cellInfo.maxVacantSpanX = Integer.MIN_VALUE;
- cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
- cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
- cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
- cellInfo.clearVacantCells();
-
- if (occupied[x][y]) {
- return;
- }
-
- cellInfo.current.set(x, y, x, y);
-
- findVacantCell(cellInfo.current, xCount, yCount, occupied, cellInfo);
- }
-
- private static void findVacantCell(Rect current, int xCount, int yCount, boolean[][] occupied,
- CellInfo cellInfo) {
-
- addVacantCell(current, cellInfo);
-
- if (current.left > 0) {
- if (isColumnEmpty(current.left - 1, current.top, current.bottom, occupied)) {
- current.left--;
- findVacantCell(current, xCount, yCount, occupied, cellInfo);
- current.left++;
- }
- }
-
- if (current.right < xCount - 1) {
- if (isColumnEmpty(current.right + 1, current.top, current.bottom, occupied)) {
- current.right++;
- findVacantCell(current, xCount, yCount, occupied, cellInfo);
- current.right--;
- }
- }
-
- if (current.top > 0) {
- if (isRowEmpty(current.top - 1, current.left, current.right, occupied)) {
- current.top--;
- findVacantCell(current, xCount, yCount, occupied, cellInfo);
- current.top++;
- }
- }
-
- if (current.bottom < yCount - 1) {
- if (isRowEmpty(current.bottom + 1, current.left, current.right, occupied)) {
- current.bottom++;
- findVacantCell(current, xCount, yCount, occupied, cellInfo);
- current.bottom--;
- }
- }
- }
-
- private static void addVacantCell(Rect current, CellInfo cellInfo) {
- CellInfo.VacantCell cell = CellInfo.VacantCell.acquire();
- cell.cellX = current.left;
- cell.cellY = current.top;
- cell.spanX = current.right - current.left + 1;
- cell.spanY = current.bottom - current.top + 1;
- if (cell.spanX > cellInfo.maxVacantSpanX) {
- cellInfo.maxVacantSpanX = cell.spanX;
- cellInfo.maxVacantSpanXSpanY = cell.spanY;
- }
- if (cell.spanY > cellInfo.maxVacantSpanY) {
- cellInfo.maxVacantSpanY = cell.spanY;
- cellInfo.maxVacantSpanYSpanX = cell.spanX;
- }
- cellInfo.vacantCells.add(cell);
- }
-
- private static boolean isColumnEmpty(int x, int top, int bottom, boolean[][] occupied) {
- for (int y = top; y <= bottom; y++) {
- if (occupied[x][y]) {
- return false;
- }
- }
- return true;
+ return (CellInfo) super.getTag();
}
+ /**
+ * Check if the row 'y' is empty from columns 'left' to 'right', inclusive.
+ */
private static boolean isRowEmpty(int y, int left, int right, boolean[][] occupied) {
for (int x = left; x <= right; x++) {
if (occupied[x][y]) {
@@ -342,79 +387,28 @@ public class CellLayout extends ViewGroup {
return true;
}
- CellInfo findAllVacantCells(boolean[] occupiedCells, View ignoreView) {
- final boolean portrait = mPortrait;
- final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
- final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
-
- boolean[][] occupied = mOccupied;
-
- if (occupiedCells != null) {
- for (int y = 0; y < yCount; y++) {
- for (int x = 0; x < xCount; x++) {
- occupied[x][y] = occupiedCells[y * xCount + x];
- }
- }
- } else {
- findOccupiedCells(xCount, yCount, occupied, ignoreView);
- }
-
- CellInfo cellInfo = new CellInfo();
-
- cellInfo.cellX = -1;
- cellInfo.cellY = -1;
- cellInfo.spanY = 0;
- cellInfo.spanX = 0;
- cellInfo.maxVacantSpanX = Integer.MIN_VALUE;
- cellInfo.maxVacantSpanXSpanY = Integer.MIN_VALUE;
- cellInfo.maxVacantSpanY = Integer.MIN_VALUE;
- cellInfo.maxVacantSpanYSpanX = Integer.MIN_VALUE;
- cellInfo.screen = mCellInfo.screen;
-
- Rect current = cellInfo.current;
-
- for (int x = 0; x < xCount; x++) {
- for (int y = 0; y < yCount; y++) {
- if (!occupied[x][y]) {
- current.set(x, y, x, y);
- findVacantCell(current, xCount, yCount, occupied, cellInfo);
- occupied[x][y] = true;
- }
- }
- }
-
- cellInfo.valid = cellInfo.vacantCells.size() > 0;
-
- // Assume the caller will perform their own cell searching, otherwise we
- // risk causing an unnecessary rebuild after findCellForSpan()
-
- return cellInfo;
- }
-
/**
- * Given a point, return the cell that strictly encloses that point
+ * Given a point, return the cell that strictly encloses that point
* @param x X coordinate of the point
* @param y Y coordinate of the point
* @param result Array of 2 ints to hold the x and y coordinate of the cell
*/
void pointToCellExact(int x, int y, int[] result) {
- final boolean portrait = mPortrait;
-
- final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding;
- final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding;
+ final int hStartPadding = getLeftPadding();
+ final int vStartPadding = getTopPadding();
result[0] = (x - hStartPadding) / (mCellWidth + mWidthGap);
result[1] = (y - vStartPadding) / (mCellHeight + mHeightGap);
- final int xAxis = portrait ? mShortAxisCells : mLongAxisCells;
- final int yAxis = portrait ? mLongAxisCells : mShortAxisCells;
+ final int xAxis = mCountX;
+ final int yAxis = mCountY;
if (result[0] < 0) result[0] = 0;
if (result[0] >= xAxis) result[0] = xAxis - 1;
if (result[1] < 0) result[1] = 0;
if (result[1] >= yAxis) result[1] = yAxis - 1;
}
-
+
/**
* Given a point, return the cell that most closely encloses that point
* @param x X coordinate of the point
@@ -427,18 +421,15 @@ public class CellLayout extends ViewGroup {
/**
* Given a cell coordinate, return the point that represents the upper left corner of that cell
- *
- * @param cellX X coordinate of the cell
+ *
+ * @param cellX X coordinate of the cell
* @param cellY Y coordinate of the cell
- *
+ *
* @param result Array of 2 ints to hold the x and y coordinate of the point
*/
void cellToPoint(int cellX, int cellY, int[] result) {
- final boolean portrait = mPortrait;
-
- final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding;
- final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding;
-
+ final int hStartPadding = getLeftPadding();
+ final int vStartPadding = getTopPadding();
result[0] = hStartPadding + cellX * (mCellWidth + mWidthGap);
result[1] = vStartPadding + cellY * (mCellHeight + mHeightGap);
@@ -453,101 +444,74 @@ public class CellLayout extends ViewGroup {
}
int getLeftPadding() {
- return mPortrait ? mShortAxisStartPadding : mLongAxisStartPadding;
+ return mLeftPadding;
}
int getTopPadding() {
- return mPortrait ? mLongAxisStartPadding : mShortAxisStartPadding;
+ return mTopPadding;
}
int getRightPadding() {
- return mPortrait ? mShortAxisEndPadding : mLongAxisEndPadding;
+ return mRightPadding;
}
int getBottomPadding() {
- return mPortrait ? mLongAxisEndPadding : mShortAxisEndPadding;
+ return mBottomPadding;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO: currently ignoring padding
-
+
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
- int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
-
+ int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
+
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
-
+
if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
}
- final int shortAxisCells = mShortAxisCells;
- final int longAxisCells = mLongAxisCells;
- final int longAxisStartPadding = mLongAxisStartPadding;
- final int longAxisEndPadding = mLongAxisEndPadding;
- final int shortAxisStartPadding = mShortAxisStartPadding;
- final int shortAxisEndPadding = mShortAxisEndPadding;
final int cellWidth = mCellWidth;
final int cellHeight = mCellHeight;
- mPortrait = heightSpecSize > widthSpecSize;
+ int numWidthGaps = mCountX - 1;
+ int numHeightGaps = mCountY - 1;
- int numShortGaps = shortAxisCells - 1;
- int numLongGaps = longAxisCells - 1;
+ int vSpaceLeft = heightSpecSize - mTopPadding - mBottomPadding - (cellHeight * mCountY);
+ mHeightGap = vSpaceLeft / numHeightGaps;
- if (mPortrait) {
- int vSpaceLeft = heightSpecSize - longAxisStartPadding - longAxisEndPadding
- - (cellHeight * longAxisCells);
- mHeightGap = vSpaceLeft / numLongGaps;
+ int hSpaceLeft = widthSpecSize - mLeftPadding - mRightPadding - (cellWidth * mCountX);
+ mWidthGap = hSpaceLeft / numWidthGaps;
+
+ // center it around the min gaps
+ int minGap = Math.min(mWidthGap, mHeightGap);
+ mWidthGap = mHeightGap = minGap;
- int hSpaceLeft = widthSpecSize - shortAxisStartPadding - shortAxisEndPadding
- - (cellWidth * shortAxisCells);
- if (numShortGaps > 0) {
- mWidthGap = hSpaceLeft / numShortGaps;
- } else {
- mWidthGap = 0;
- }
- } else {
- int hSpaceLeft = widthSpecSize - longAxisStartPadding - longAxisEndPadding
- - (cellWidth * longAxisCells);
- mWidthGap = hSpaceLeft / numLongGaps;
-
- int vSpaceLeft = heightSpecSize - shortAxisStartPadding - shortAxisEndPadding
- - (cellHeight * shortAxisCells);
- if (numShortGaps > 0) {
- mHeightGap = vSpaceLeft / numShortGaps;
- } else {
- mHeightGap = 0;
- }
- }
-
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- if (mPortrait) {
- lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, shortAxisStartPadding,
- longAxisStartPadding);
- } else {
- lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, longAxisStartPadding,
- shortAxisStartPadding);
- }
-
- if (lp.regenerateId) {
- child.setId(((getId() & 0xFF) << 16) | (lp.cellX & 0xFF) << 8 | (lp.cellY & 0xFF));
- lp.regenerateId = false;
- }
+ lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap,
+ mLeftPadding, mTopPadding);
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
- int childheightMeasureSpec =
- MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
+ int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
+ MeasureSpec.EXACTLY);
+
child.measure(childWidthMeasureSpec, childheightMeasureSpec);
}
-
- setMeasuredDimension(widthSpecSize, heightSpecSize);
+ if (widthSpecMode == MeasureSpec.AT_MOST) {
+ int newWidth = mLeftPadding + mRightPadding + (mCountX * cellWidth) +
+ ((mCountX - 1) * minGap);
+ int newHeight = mTopPadding + mBottomPadding + (mCountY * cellHeight) +
+ ((mCountY - 1) * minGap);
+ setMeasuredDimension(newWidth, newHeight);
+ } else if (widthSpecMode == MeasureSpec.EXACTLY) {
+ setMeasuredDimension(widthSpecSize, heightSpecSize);
+ }
}
@Override
@@ -567,7 +531,7 @@ public class CellLayout extends ViewGroup {
if (lp.dropped) {
lp.dropped = false;
- final int[] cellXY = mCellXY;
+ final int[] cellXY = mTmpCellXY;
getLocationOnScreen(cellXY);
mWallpaperManager.sendWallpaperCommand(getWindowToken(), "android.home.drop",
cellXY[0] + childLeft + lp.width / 2,
@@ -578,6 +542,21 @@ public class CellLayout extends ViewGroup {
}
@Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ mBackgroundLayoutRect.set(0, 0, w, h);
+ if (mBackground != null) {
+ mBackground.setBounds(mBackgroundLayoutRect);
+ }
+ if (mBackgroundMiniHover != null) {
+ mBackgroundMiniHover.setBounds(mBackgroundLayoutRect);
+ }
+ if (mBackgroundMini != null) {
+ mBackgroundMini.setBounds(mBackgroundLayoutRect);
+ }
+ }
+
+ @Override
protected void setChildrenDrawingCacheEnabled(boolean enabled) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
@@ -593,92 +572,383 @@ public class CellLayout extends ViewGroup {
super.setChildrenDrawnWithCacheEnabled(enabled);
}
+ public float getBackgroundAlpha() {
+ return mBackgroundAlpha;
+ }
+
+ public void setBackgroundAlpha(float alpha) {
+ mBackgroundAlpha = alpha;
+ invalidate();
+ }
+
+ // Need to return true to let the view system know we know how to handle alpha-- this is
+ // because when our children have an alpha of 0.0f, they are still rendering their "dimmed"
+ // versions
+ @Override
+ protected boolean onSetAlpha(int alpha) {
+ return true;
+ }
+
+ public void setAlpha(float alpha) {
+ setChildrenAlpha(alpha);
+ super.setAlpha(alpha);
+ }
+
+ private void setChildrenAlpha(float alpha) {
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ getChildAt(i).setAlpha(alpha);
+ }
+ }
+
+ private boolean isVacantIgnoring(
+ int originX, int originY, int spanX, int spanY, View ignoreView) {
+ if (ignoreView != null) {
+ markCellsAsUnoccupiedForView(ignoreView);
+ }
+ boolean isVacant = true;
+ for (int i = 0; i < spanY; i++) {
+ if (!isRowEmpty(originY + i, originX, originX + spanX - 1, mOccupied)) {
+ isVacant = false;
+ break;
+ }
+ }
+ if (ignoreView != null) {
+ markCellsAsOccupiedForView(ignoreView);
+ }
+ return isVacant;
+ }
+
+ private boolean isVacant(int originX, int originY, int spanX, int spanY) {
+ return isVacantIgnoring(originX, originY, spanX, spanY, null);
+ }
+
+ public View getChildAt(int x, int y) {
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if ((lp.cellX <= x) && (x < lp.cellX + lp.cellHSpan) &&
+ (lp.cellY <= y) && (y < lp.cellY + lp.cellHSpan)) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Estimate the size that a child with the given dimensions will take in the layout.
+ */
+ void estimateChildSize(int minWidth, int minHeight, int[] result) {
+ // Assuming it's placed at 0, 0, find where the bottom right cell will land
+ rectToCell(minWidth, minHeight, result);
+
+ // Then figure out the rect it will occupy
+ cellToRect(0, 0, result[0], result[1], mRectF);
+ result[0] = (int)mRectF.width();
+ result[1] = (int)mRectF.height();
+ }
+
+ /**
+ * Estimate where the top left cell of the dragged item will land if it is dropped.
+ *
+ * @param originX The X value of the top left corner of the item
+ * @param originY The Y value of the top left corner of the item
+ * @param spanX The number of horizontal cells that the item spans
+ * @param spanY The number of vertical cells that the item spans
+ * @param result The estimated drop cell X and Y.
+ */
+ void estimateDropCell(int originX, int originY, int spanX, int spanY, int[] result) {
+ final int countX = mCountX;
+ final int countY = mCountY;
+
+ // pointToCellRounded takes the top left of a cell but will pad that with
+ // cellWidth/2 and cellHeight/2 when finding the matching cell
+ pointToCellRounded(originX, originY, result);
+
+ // If the item isn't fully on this screen, snap to the edges
+ int rightOverhang = result[0] + spanX - countX;
+ if (rightOverhang > 0) {
+ result[0] -= rightOverhang; // Snap to right
+ }
+ result[0] = Math.max(0, result[0]); // Snap to left
+ int bottomOverhang = result[1] + spanY - countY;
+ if (bottomOverhang > 0) {
+ result[1] -= bottomOverhang; // Snap to bottom
+ }
+ result[1] = Math.max(0, result[1]); // Snap to top
+ }
+
+ void visualizeDropLocation(
+ View view, int originX, int originY, int spanX, int spanY, View draggedItem) {
+ final int[] originCell = mDragCell;
+ final int[] cellXY = mTmpCellXY;
+ estimateDropCell(originX, originY, spanX, spanY, cellXY);
+
+ // Only recalculate the bounding rect when necessary
+ if (!Arrays.equals(cellXY, originCell)) {
+ originCell[0] = cellXY[0];
+ originCell[1] = cellXY[1];
+
+ // Find the top left corner of the rect the object will occupy
+ final int[] topLeft = mTmpCellXY;
+ cellToPoint(originCell[0], originCell[1], topLeft);
+ final int left = topLeft[0];
+ final int top = topLeft[1];
+
+ // Now find the bottom right
+ final int[] bottomRight = mTmpCellXY;
+ cellToPoint(originCell[0] + spanX - 1, originCell[1] + spanY - 1, bottomRight);
+ bottomRight[0] += mCellWidth;
+ bottomRight[1] += mCellHeight;
+
+ boolean vacant =
+ isVacantIgnoring(originCell[0], originCell[1], spanX, spanY, draggedItem);
+ mDragRectDrawable = vacant ? mVacantDrawable : mOccupiedDrawable;
+
+ // mDragRect will be rendered in onDraw()
+ mDragRect.set(left, top, bottomRight[0], bottomRight[1]);
+ invalidate();
+ }
+ }
+
+ /**
+ * Find a vacant area that will fit the given bounds nearest the requested
+ * cell location. Uses Euclidean distance to score multiple vacant areas.
+ *
+ * @param pixelX The X location at which you want to search for a vacant area.
+ * @param pixelY The Y location at which you want to search for a vacant area.
+ * @param spanX Horizontal span of the object.
+ * @param spanY Vertical span of the object.
+ * @param vacantCells Pre-computed set of vacant cells to search.
+ * @param recycle Previously returned value to possibly recycle.
+ * @return The X, Y cell of a vacant area that can contain this object,
+ * nearest the requested location.
+ */
+ int[] findNearestVacantArea(
+ int pixelX, int pixelY, int spanX, int spanY, int[] recycle) {
+ return findNearestVacantArea(pixelX, pixelY, spanX, spanY, null, recycle);
+ }
+
/**
* Find a vacant area that will fit the given bounds nearest the requested
* cell location. Uses Euclidean distance to score multiple vacant areas.
- *
+ *
* @param pixelX The X location at which you want to search for a vacant area.
* @param pixelY The Y location at which you want to search for a vacant area.
* @param spanX Horizontal span of the object.
* @param spanY Vertical span of the object.
* @param vacantCells Pre-computed set of vacant cells to search.
* @param recycle Previously returned value to possibly recycle.
+ * @param ignoreView Considers space occupied by this view as unoccupied
* @return The X, Y cell of a vacant area that can contain this object,
* nearest the requested location.
*/
- int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY,
- CellInfo vacantCells, int[] recycle) {
-
+ int[] findNearestVacantArea(
+ int pixelX, int pixelY, int spanX, int spanY, View ignoreView, int[] recycle) {
+ if (ignoreView != null) {
+ markCellsAsUnoccupiedForView(ignoreView);
+ }
// Keep track of best-scoring drop area
final int[] bestXY = recycle != null ? recycle : new int[2];
- final int[] cellXY = mCellXY;
double bestDistance = Double.MAX_VALUE;
-
- // Bail early if vacant cells aren't valid
- if (!vacantCells.valid) {
- return null;
- }
- // Look across all vacant cells for best fit
- final int size = vacantCells.vacantCells.size();
- for (int i = 0; i < size; i++) {
- final CellInfo.VacantCell cell = vacantCells.vacantCells.get(i);
-
- // Reject if vacant cell isn't our exact size
- if (cell.spanX != spanX || cell.spanY != spanY) {
- continue;
- }
-
- // Score is center distance from requested pixel
- cellToPoint(cell.cellX, cell.cellY, cellXY);
-
- double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2) +
- Math.pow(cellXY[1] - pixelY, 2));
- if (distance <= bestDistance) {
- bestDistance = distance;
- bestXY[0] = cell.cellX;
- bestXY[1] = cell.cellY;
+ for (int x = 0; x < mCountX - (spanX - 1); x++) {
+ inner:
+ for (int y = 0; y < mCountY - (spanY - 1); y++) {
+ for (int i = 0; i < spanX; i++) {
+ for (int j = 0; j < spanY; j++) {
+ if (mOccupied[x + i][y + j]) {
+ // small optimization: we can skip to below the row we just found
+ // an occupied cell
+ y += j;
+ continue inner;
+ }
+ }
+ }
+ final int[] cellXY = mTmpCellXY;
+ cellToPoint(x, y, cellXY);
+
+ double distance = Math.sqrt(Math.pow(cellXY[0] - pixelX, 2)
+ + Math.pow(cellXY[1] - pixelY, 2));
+ if (distance <= bestDistance) {
+ bestDistance = distance;
+ bestXY[0] = x;
+ bestXY[1] = y;
+ }
}
}
+ if (ignoreView != null) {
+ markCellsAsOccupiedForView(ignoreView);
+ }
- // Return null if no suitable location found
+ // Return null if no suitable location found
if (bestDistance < Double.MAX_VALUE) {
return bestXY;
} else {
return null;
}
}
-
+
+ boolean existsEmptyCell() {
+ return findCellForSpan(null, 1, 1);
+ }
+
+ /**
+ * Finds the upper-left coordinate of the first rectangle in the grid that can
+ * hold a cell of the specified dimensions. If intersectX and intersectY are not -1,
+ * then this method will only return coordinates for rectangles that contain the cell
+ * (intersectX, intersectY)
+ *
+ * @param cellXY The array that will contain the position of a vacant cell if such a cell
+ * can be found.
+ * @param spanX The horizontal span of the cell we want to find.
+ * @param spanY The vertical span of the cell we want to find.
+ *
+ * @return True if a vacant cell of the specified dimension was found, false otherwise.
+ */
+ boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
+ return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, null);
+ }
+
/**
- * Drop a child at the specified position
+ * Like above, but ignores any cells occupied by the item "ignoreView"
+ *
+ * @param cellXY The array that will contain the position of a vacant cell if such a cell
+ * can be found.
+ * @param spanX The horizontal span of the cell we want to find.
+ * @param spanY The vertical span of the cell we want to find.
+ * @param ignoreView The home screen item we should treat as not occupying any space
+ * @return
+ */
+ boolean findCellForSpanIgnoring(int[] cellXY, int spanX, int spanY, View ignoreView) {
+ return findCellForSpanThatIntersectsIgnoring(cellXY, spanX, spanY, -1, -1, ignoreView);
+ }
+
+ /**
+ * Like above, but if intersectX and intersectY are not -1, then this method will try to
+ * return coordinates for rectangles that contain the cell [intersectX, intersectY]
+ *
+ * @param spanX The horizontal span of the cell we want to find.
+ * @param spanY The vertical span of the cell we want to find.
+ * @param ignoreView The home screen item we should treat as not occupying any space
+ * @param intersectX The X coordinate of the cell that we should try to overlap
+ * @param intersectX The Y coordinate of the cell that we should try to overlap
+ *
+ * @return True if a vacant cell of the specified dimension was found, false otherwise.
+ */
+ boolean findCellForSpanThatIntersects(int[] cellXY, int spanX, int spanY,
+ int intersectX, int intersectY) {
+ return findCellForSpanThatIntersectsIgnoring(
+ cellXY, spanX, spanY, intersectX, intersectY, null);
+ }
+
+ /**
+ * The superset of the above two methods
+ */
+ boolean findCellForSpanThatIntersectsIgnoring(int[] cellXY, int spanX, int spanY,
+ int intersectX, int intersectY, View ignoreView) {
+ if (ignoreView != null) {
+ markCellsAsUnoccupiedForView(ignoreView);
+ }
+
+ boolean foundCell = false;
+ while (true) {
+ int startX = 0;
+ if (intersectX >= 0) {
+ startX = Math.max(startX, intersectX - (spanX - 1));
+ }
+ int endX = mCountX - (spanX - 1);
+ if (intersectX >= 0) {
+ endX = Math.min(endX, intersectX + (spanX - 1) + (spanX == 1 ? 1 : 0));
+ }
+ int startY = 0;
+ if (intersectY >= 0) {
+ startY = Math.max(startY, intersectY - (spanY - 1));
+ }
+ int endY = mCountY - (spanY - 1);
+ if (intersectY >= 0) {
+ endY = Math.min(endY, intersectY + (spanY - 1) + (spanY == 1 ? 1 : 0));
+ }
+
+ for (int x = startX; x < endX; x++) {
+ inner:
+ for (int y = startY; y < endY; y++) {
+ for (int i = 0; i < spanX; i++) {
+ for (int j = 0; j < spanY; j++) {
+ if (mOccupied[x + i][y + j]) {
+ // small optimization: we can skip to below the row we just found
+ // an occupied cell
+ y += j;
+ continue inner;
+ }
+ }
+ }
+ if (cellXY != null) {
+ cellXY[0] = x;
+ cellXY[1] = y;
+ }
+ foundCell = true;
+ break;
+ }
+ }
+ if (intersectX == -1 && intersectY == -1) {
+ break;
+ } else {
+ // if we failed to find anything, try again but without any requirements of
+ // intersecting
+ intersectX = -1;
+ intersectY = -1;
+ continue;
+ }
+ }
+
+ if (ignoreView != null) {
+ markCellsAsOccupiedForView(ignoreView);
+ }
+ return foundCell;
+ }
+
+ /**
+ * Called when drag has left this CellLayout or has been completed (successfully or not)
+ */
+ void onDragExit() {
+ // Invalidate the drag data
+ mDragCell[0] = -1;
+ mDragCell[1] = -1;
+
+ setHover(false);
+ mDragRect.setEmpty();
+ invalidate();
+ }
+
+ /**
+ * Mark a child as having been dropped.
*
* @param child The child that is being dropped
- * @param targetXY Destination area to move to
*/
- void onDropChild(View child, int[] targetXY) {
+ void onDropChild(View child) {
if (child != null) {
LayoutParams lp = (LayoutParams) child.getLayoutParams();
- lp.cellX = targetXY[0];
- lp.cellY = targetXY[1];
lp.isDragging = false;
lp.dropped = true;
mDragRect.setEmpty();
child.requestLayout();
- invalidate();
}
+ onDragExit();
}
void onDropAborted(View child) {
if (child != null) {
((LayoutParams) child.getLayoutParams()).isDragging = false;
- invalidate();
}
- mDragRect.setEmpty();
+ onDragExit();
}
/**
* Start dragging the specified child
- *
+ *
* @param child The child that is being dragged
*/
void onDragChild(View child) {
@@ -686,58 +956,43 @@ public class CellLayout extends ViewGroup {
lp.isDragging = true;
mDragRect.setEmpty();
}
-
- /**
- * Drag a child over the specified position
- *
- * @param child The child that is being dropped
- * @param cellX The child's new x cell location
- * @param cellY The child's new y cell location
- */
- void onDragOverChild(View child, int cellX, int cellY) {
- int[] cellXY = mCellXY;
- pointToCellRounded(cellX, cellY, cellXY);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
- cellToRect(cellXY[0], cellXY[1], lp.cellHSpan, lp.cellVSpan, mDragRect);
- invalidate();
- }
-
+
/**
* Computes a bounding rectangle for a range of cells
- *
+ *
* @param cellX X coordinate of upper left corner expressed as a cell position
* @param cellY Y coordinate of upper left corner expressed as a cell position
- * @param cellHSpan Width in cells
+ * @param cellHSpan Width in cells
* @param cellVSpan Height in cells
- * @param dragRect Rectnagle into which to put the results
+ * @param resultRect Rect into which to put the results
*/
- public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF dragRect) {
- final boolean portrait = mPortrait;
+ public void cellToRect(int cellX, int cellY, int cellHSpan, int cellVSpan, RectF resultRect) {
final int cellWidth = mCellWidth;
final int cellHeight = mCellHeight;
final int widthGap = mWidthGap;
final int heightGap = mHeightGap;
-
- final int hStartPadding = portrait ? mShortAxisStartPadding : mLongAxisStartPadding;
- final int vStartPadding = portrait ? mLongAxisStartPadding : mShortAxisStartPadding;
-
+
+ final int hStartPadding = getLeftPadding();
+ final int vStartPadding = getTopPadding();
+
int width = cellHSpan * cellWidth + ((cellHSpan - 1) * widthGap);
int height = cellVSpan * cellHeight + ((cellVSpan - 1) * heightGap);
int x = hStartPadding + cellX * (cellWidth + widthGap);
int y = vStartPadding + cellY * (cellHeight + heightGap);
-
- dragRect.set(x, y, x + width, y + height);
+
+ resultRect.set(x, y, x + width, y + height);
}
-
+
/**
- * Computes the required horizontal and vertical cell spans to always
+ * Computes the required horizontal and vertical cell spans to always
* fit the given rectangle.
- *
+ *
* @param width Width in pixels
* @param height Height in pixels
+ * @param result An array of length 2 in which to store the result (may be null).
*/
- public int[] rectToCell(int width, int height) {
+ public int[] rectToCell(int width, int height, int[] result) {
// Always assume we're working with the smallest span to make sure we
// reserve enough space in both orientations.
final Resources resources = getResources();
@@ -749,7 +1004,12 @@ public class CellLayout extends ViewGroup {
int spanX = (width + smallerSize) / smallerSize;
int spanY = (height + smallerSize) / smallerSize;
- return new int[] { spanX, spanY };
+ if (result == null) {
+ return new int[] { spanX, spanY };
+ }
+ result[0] = spanX;
+ result[1] = spanY;
+ return result;
}
/**
@@ -758,18 +1018,12 @@ public class CellLayout extends ViewGroup {
* @param vacant Holds the x and y coordinate of the vacant cell
* @param spanX Horizontal cell span.
* @param spanY Vertical cell span.
- *
+ *
* @return True if a vacant cell was found
*/
public boolean getVacantCell(int[] vacant, int spanX, int spanY) {
- final boolean portrait = mPortrait;
- final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
- final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
- final boolean[][] occupied = mOccupied;
-
- findOccupiedCells(xCount, yCount, occupied, null);
- return findVacantCell(vacant, spanX, spanY, xCount, yCount, occupied);
+ return findVacantCell(vacant, spanX, spanY, mCountX, mCountY, mOccupied);
}
static boolean findVacantCell(int[] vacant, int spanX, int spanY,
@@ -796,14 +1050,14 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
return false;
}
- boolean[] getOccupiedCells() {
- final boolean portrait = mPortrait;
- final int xCount = portrait ? mShortAxisCells : mLongAxisCells;
- final int yCount = portrait ? mLongAxisCells : mShortAxisCells;
+ /**
+ * Update the array of occupied cells (mOccupied), and return a flattened copy of the array.
+ */
+ boolean[] getOccupiedCellsFlattened() {
+ final int xCount = mCountX;
+ final int yCount = mCountY;
final boolean[][] occupied = mOccupied;
- findOccupiedCells(xCount, yCount, occupied, null);
-
final boolean[] flat = new boolean[xCount * yCount];
for (int y = 0; y < yCount; y++) {
for (int x = 0; x < xCount; x++) {
@@ -814,25 +1068,34 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
return flat;
}
- private void findOccupiedCells(int xCount, int yCount, boolean[][] occupied, View ignoreView) {
- for (int x = 0; x < xCount; x++) {
- for (int y = 0; y < yCount; y++) {
- occupied[x][y] = false;
+ private void clearOccupiedCells() {
+ for (int x = 0; x < mCountX; x++) {
+ for (int y = 0; y < mCountY; y++) {
+ mOccupied[x][y] = false;
}
}
+ }
- int count = getChildCount();
- for (int i = 0; i < count; i++) {
- View child = getChildAt(i);
- if (child instanceof Folder || child.equals(ignoreView)) {
- continue;
- }
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ public void onMove(View view, int newCellX, int newCellY) {
+ LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ markCellsAsUnoccupiedForView(view);
+ markCellsForView(newCellX, newCellY, lp.cellHSpan, lp.cellVSpan, true);
+ }
- for (int x = lp.cellX; x < lp.cellX + lp.cellHSpan && x < xCount; x++) {
- for (int y = lp.cellY; y < lp.cellY + lp.cellVSpan && y < yCount; y++) {
- occupied[x][y] = true;
- }
+ private void markCellsAsOccupiedForView(View view) {
+ LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true);
+ }
+
+ private void markCellsAsUnoccupiedForView(View view) {
+ LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
+ }
+
+ private void markCellsForView(int cellX, int cellY, int spanX, int spanY, boolean value) {
+ for (int x = cellX; x < cellX + spanX && x < mCountX; x++) {
+ for (int y = cellY; y < cellY + spanY && y < mCountY; y++) {
+ mOccupied[x][y] = value;
}
}
}
@@ -852,6 +1115,17 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
return new CellLayout.LayoutParams(p);
}
+ public static class CellLayoutAnimationController extends LayoutAnimationController {
+ public CellLayoutAnimationController(Animation animation, float delay) {
+ super(animation, delay);
+ }
+
+ @Override
+ protected long getDelayForView(View view) {
+ return (int) (Math.random() * 150);
+ }
+ }
+
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
/**
* Horizontal location of the item in the grid.
@@ -876,7 +1150,7 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
*/
@ViewDebug.ExportedProperty
public int cellVSpan;
-
+
/**
* Is this item currently being dragged
*/
@@ -889,8 +1163,6 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
@ViewDebug.ExportedProperty
int y;
- boolean regenerateId;
-
boolean dropped;
public LayoutParams(Context c, AttributeSet attrs) {
@@ -904,7 +1176,15 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
cellHSpan = 1;
cellVSpan = 1;
}
-
+
+ public LayoutParams(LayoutParams source) {
+ super(source);
+ this.cellX = source.cellX;
+ this.cellY = source.cellY;
+ this.cellHSpan = source.cellHSpan;
+ this.cellVSpan = source.cellVSpan;
+ }
+
public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
this.cellX = cellX;
@@ -915,12 +1195,12 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
int hStartPadding, int vStartPadding) {
-
+
final int myCellHSpan = cellHSpan;
final int myCellVSpan = cellVSpan;
final int myCellX = cellX;
final int myCellY = cellY;
-
+
width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
leftMargin - rightMargin;
height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
@@ -929,172 +1209,31 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
}
- }
- static final class CellInfo implements ContextMenu.ContextMenuInfo {
- /**
- * See View.AttachInfo.InvalidateInfo for futher explanations about
- * the recycling mechanism. In this case, we recycle the vacant cells
- * instances because up to several hundreds can be instanciated when
- * the user long presses an empty cell.
- */
- static final class VacantCell {
- int cellX;
- int cellY;
- int spanX;
- int spanY;
-
- // We can create up to 523 vacant cells on a 4x4 grid, 100 seems
- // like a reasonable compromise given the size of a VacantCell and
- // the fact that the user is not likely to touch an empty 4x4 grid
- // very often
- private static final int POOL_LIMIT = 100;
- private static final Object sLock = new Object();
-
- private static int sAcquiredCount = 0;
- private static VacantCell sRoot;
-
- private VacantCell next;
-
- static VacantCell acquire() {
- synchronized (sLock) {
- if (sRoot == null) {
- return new VacantCell();
- }
-
- VacantCell info = sRoot;
- sRoot = info.next;
- sAcquiredCount--;
-
- return info;
- }
- }
-
- void release() {
- synchronized (sLock) {
- if (sAcquiredCount < POOL_LIMIT) {
- sAcquiredCount++;
- next = sRoot;
- sRoot = this;
- }
- }
- }
-
- @Override
- public String toString() {
- return "VacantCell[x=" + cellX + ", y=" + cellY + ", spanX=" + spanX +
- ", spanY=" + spanY + "]";
- }
+ public String toString() {
+ return "(" + this.cellX + ", " + this.cellY + ")";
}
+ }
+ // This class stores info for two purposes:
+ // 1. When dragging items (mDragInfo in Workspace), we store the View, its cellX & cellY,
+ // its spanX, spanY, and the screen it is on
+ // 2. When long clicking on an empty cell in a CellLayout, we save information about the
+ // cellX and cellY coordinates and which page was clicked. We then set this as a tag on
+ // the CellLayout that was long clicked
+ static final class CellInfo implements ContextMenu.ContextMenuInfo {
View cell;
- int cellX;
- int cellY;
+ int cellX = -1;
+ int cellY = -1;
int spanX;
int spanY;
int screen;
boolean valid;
- final ArrayList<VacantCell> vacantCells = new ArrayList<VacantCell>(VacantCell.POOL_LIMIT);
- int maxVacantSpanX;
- int maxVacantSpanXSpanY;
- int maxVacantSpanY;
- int maxVacantSpanYSpanX;
- final Rect current = new Rect();
-
- void clearVacantCells() {
- final ArrayList<VacantCell> list = vacantCells;
- final int count = list.size();
-
- for (int i = 0; i < count; i++) list.get(i).release();
-
- list.clear();
- }
-
- void findVacantCellsFromOccupied(boolean[] occupied, int xCount, int yCount) {
- if (cellX < 0 || cellY < 0) {
- maxVacantSpanX = maxVacantSpanXSpanY = Integer.MIN_VALUE;
- maxVacantSpanY = maxVacantSpanYSpanX = Integer.MIN_VALUE;
- clearVacantCells();
- return;
- }
-
- final boolean[][] unflattened = new boolean[xCount][yCount];
- for (int y = 0; y < yCount; y++) {
- for (int x = 0; x < xCount; x++) {
- unflattened[x][y] = occupied[y * xCount + x];
- }
- }
- CellLayout.findIntersectingVacantCells(this, cellX, cellY, xCount, yCount, unflattened);
- }
-
- /**
- * This method can be called only once! Calling #findVacantCellsFromOccupied will
- * restore the ability to call this method.
- *
- * Finds the upper-left coordinate of the first rectangle in the grid that can
- * hold a cell of the specified dimensions.
- *
- * @param cellXY The array that will contain the position of a vacant cell if such a cell
- * can be found.
- * @param spanX The horizontal span of the cell we want to find.
- * @param spanY The vertical span of the cell we want to find.
- *
- * @return True if a vacant cell of the specified dimension was found, false otherwise.
- */
- boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
- return findCellForSpan(cellXY, spanX, spanY, true);
- }
-
- boolean findCellForSpan(int[] cellXY, int spanX, int spanY, boolean clear) {
- final ArrayList<VacantCell> list = vacantCells;
- final int count = list.size();
-
- boolean found = false;
-
- if (this.spanX >= spanX && this.spanY >= spanY) {
- cellXY[0] = cellX;
- cellXY[1] = cellY;
- found = true;
- }
-
- // Look for an exact match first
- for (int i = 0; i < count; i++) {
- VacantCell cell = list.get(i);
- if (cell.spanX == spanX && cell.spanY == spanY) {
- cellXY[0] = cell.cellX;
- cellXY[1] = cell.cellY;
- found = true;
- break;
- }
- }
-
- // Look for the first cell large enough
- for (int i = 0; i < count; i++) {
- VacantCell cell = list.get(i);
- if (cell.spanX >= spanX && cell.spanY >= spanY) {
- cellXY[0] = cell.cellX;
- cellXY[1] = cell.cellY;
- found = true;
- break;
- }
- }
-
- if (clear) clearVacantCells();
-
- return found;
- }
-
@Override
public String toString() {
- return "Cell[view=" + (cell == null ? "null" : cell.getClass()) + ", x=" + cellX +
- ", y=" + cellY + "]";
+ return "Cell[view=" + (cell == null ? "null" : cell.getClass())
+ + ", x=" + cellX + ", y=" + cellY + "]";
}
}
-
- public boolean lastDownOnOccupiedCell() {
- return mLastDownOnOccupiedCell;
- }
}
-
-
diff --git a/src/com/android/launcher2/CustomizePagedView.java b/src/com/android/launcher2/CustomizePagedView.java
new file mode 100644
index 000000000..03c4bce7e
--- /dev/null
+++ b/src/com/android/launcher2/CustomizePagedView.java
@@ -0,0 +1,852 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import com.android.launcher.R;
+
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.Bitmap.Config;
+import android.graphics.Region.Op;
+import android.graphics.drawable.Drawable;
+import android.provider.LiveFolders;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.ActionMode;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Checkable;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class CustomizePagedView extends PagedView
+ implements View.OnLongClickListener, View.OnClickListener,
+ DragSource, ActionMode.Callback {
+
+ public enum CustomizationType {
+ WidgetCustomization,
+ FolderCustomization,
+ ShortcutCustomization,
+ WallpaperCustomization,
+ ApplicationCustomization
+ }
+
+ /**
+ * The linear layout used strictly for the widget tab of the customization tray
+ */
+ private class WidgetLayout extends LinearLayout {
+ public WidgetLayout(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ // We eat up the touch events here, since the PagedView (which uses the same swiping
+ // touch code as Workspace previously) uses onInterceptTouchEvent() to determine when
+ // the user is scrolling between pages. This means that if the pages themselves don't
+ // handle touch events, it gets forwarded up to PagedView itself, and it's own
+ // onTouchEvent() handling will prevent further intercept touch events from being called
+ // (it's the same view in that case). This is not ideal, but to prevent more changes,
+ // we just always mark the touch event as handled.
+ return super.onTouchEvent(event) || true;
+ }
+ }
+
+ private static final String TAG = "CustomizeWorkspace";
+ private static final boolean DEBUG = false;
+
+ private Launcher mLauncher;
+ private DragController mDragController;
+ private PackageManager mPackageManager;
+
+ private CustomizationType mCustomizationType;
+
+ // The layout used to emulate the workspace in resolve the cell dimensions of a widget
+ private PagedViewCellLayout mWorkspaceWidgetLayout;
+
+ // The mapping between the pages and the widgets that will be laid out on them
+ private ArrayList<ArrayList<AppWidgetProviderInfo>> mWidgetPages;
+
+ // The max dimensions for the ImageView we use for displaying the widget
+ private int mMaxWidgetWidth;
+
+ // The max number of widget cells to take a "page" of widget
+ private int mMaxWidgetsCellHSpan;
+
+ // The raw sources of data for each of the different tabs of the customization page
+ private List<AppWidgetProviderInfo> mWidgetList;
+ private List<ResolveInfo> mFolderList;
+ private List<ResolveInfo> mShortcutList;
+ private List<ResolveInfo> mWallpaperList;
+ private List<ApplicationInfo> mApps;
+
+ private int mCellCountX;
+ private int mCellCountY;
+ private int mPageLayoutPaddingTop;
+ private int mPageLayoutPaddingBottom;
+ private int mPageLayoutPaddingLeft;
+ private int mPageLayoutPaddingRight;
+ private static final int sMinWidgetCellHSpan = 2;
+ private static final int sMaxWidgetCellHSpan = 4;
+
+ private int mChoiceModeTitleText;
+
+ // The scale factor for widget previews inside the widget drawer
+ private static final float sScaleFactor = 0.75f;
+
+ private final Canvas mCanvas = new Canvas();
+ private final LayoutInflater mInflater;
+
+ public CustomizePagedView(Context context) {
+ this(context, null, 0);
+ }
+
+ public CustomizePagedView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CustomizePagedView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a;
+ a = context.obtainStyledAttributes(attrs, R.styleable.CustomizePagedView,
+ defStyle, 0);
+ mMaxWidgetsCellHSpan = a.getInt(R.styleable.CustomizePagedView_widgetCellCountX, 8);
+ a.recycle();
+ a = context.obtainStyledAttributes(attrs, R.styleable.PagedView, defStyle, 0);
+ mCellCountX = a.getInt(R.styleable.PagedView_cellCountX, 7);
+ mCellCountY = a.getInt(R.styleable.PagedView_cellCountY, 4);
+ mPageLayoutPaddingTop = a.getDimensionPixelSize(
+ R.styleable.PagedView_pageLayoutPaddingTop, 10);
+ mPageLayoutPaddingBottom = a.getDimensionPixelSize(
+ R.styleable.PagedView_pageLayoutPaddingBottom, 10);
+ mPageLayoutPaddingLeft = a.getDimensionPixelSize(
+ R.styleable.PagedView_pageLayoutPaddingLeft, 10);
+ mPageLayoutPaddingRight = a.getDimensionPixelSize(
+ R.styleable.PagedView_pageLayoutPaddingRight, 10);
+ a.recycle();
+ mCustomizationType = CustomizationType.WidgetCustomization;
+ mWidgetPages = new ArrayList<ArrayList<AppWidgetProviderInfo>>();
+ mWorkspaceWidgetLayout = new PagedViewCellLayout(context);
+ mInflater = LayoutInflater.from(context);
+
+ setVisibility(View.GONE);
+ setSoundEffectsEnabled(false);
+ setupWorkspaceLayout();
+ }
+
+ public void setLauncher(Launcher launcher) {
+ Context context = getContext();
+ mLauncher = launcher;
+ mPackageManager = context.getPackageManager();
+ }
+
+ /**
+ * Sets the list of applications that launcher has loaded.
+ */
+ public void setApps(ArrayList<ApplicationInfo> list) {
+ mApps = list;
+ Collections.sort(mApps, LauncherModel.APP_NAME_COMPARATOR);
+ mPageViewIconCache.clear();
+ invalidatePageData();
+ }
+
+ /**
+ * Convenience function to add new items to the set of applications that were previously loaded.
+ * Called by both updateApps() and setApps().
+ */
+ private void addAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
+ // we add it in place, in alphabetical order
+ final int count = list.size();
+ for (int i = 0; i < count; ++i) {
+ final ApplicationInfo info = list.get(i);
+ final int index = Collections.binarySearch(mApps, info, LauncherModel.APP_NAME_COMPARATOR);
+ if (index < 0) {
+ mApps.add(-(index + 1), info);
+ }
+ }
+ }
+
+ /**
+ * Adds new applications to the loaded list, and notifies the paged view to update itself.
+ */
+ public void addApps(ArrayList<ApplicationInfo> list) {
+ addAppsWithoutInvalidate(list);
+ invalidatePageData();
+ }
+
+ /**
+ * Convenience function to remove items to the set of applications that were previously loaded.
+ * Called by both updateApps() and removeApps().
+ */
+ private void removeAppsWithoutInvalidate(ArrayList<ApplicationInfo> list) {
+ // loop through all the apps and remove apps that have the same component
+ final int length = list.size();
+ for (int i = 0; i < length; ++i) {
+ final ApplicationInfo info = list.get(i);
+ int removeIndex = findAppByComponent(mApps, info);
+ if (removeIndex > -1) {
+ mApps.remove(removeIndex);
+ mPageViewIconCache.removeOutline(info);
+ }
+ }
+ }
+
+ /**
+ * Removes applications from the loaded list, and notifies the paged view to update itself.
+ */
+ public void removeApps(ArrayList<ApplicationInfo> list) {
+ removeAppsWithoutInvalidate(list);
+ invalidatePageData();
+ }
+
+ /**
+ * Updates a set of applications from the loaded list, and notifies the paged view to update
+ * itself.
+ */
+ public void updateApps(ArrayList<ApplicationInfo> list) {
+ // We remove and re-add the updated applications list because it's properties may have
+ // changed (ie. the title), and this will ensure that the items will be in their proper
+ // place in the list.
+ removeAppsWithoutInvalidate(list);
+ addAppsWithoutInvalidate(list);
+ invalidatePageData();
+ }
+
+ /**
+ * Convenience function to find matching ApplicationInfos for removal.
+ */
+ private int findAppByComponent(List<ApplicationInfo> list, ApplicationInfo item) {
+ ComponentName removeComponent = item.intent.getComponent();
+ final int length = list.size();
+ for (int i = 0; i < length; ++i) {
+ ApplicationInfo info = list.get(i);
+ if (info.intent.getComponent().equals(removeComponent)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public void update() {
+ Context context = getContext();
+
+ // get the list of widgets
+ mWidgetList = AppWidgetManager.getInstance(mLauncher).getInstalledProviders();
+ Collections.sort(mWidgetList, new Comparator<AppWidgetProviderInfo>() {
+ @Override
+ public int compare(AppWidgetProviderInfo object1, AppWidgetProviderInfo object2) {
+ return object1.label.compareTo(object2.label);
+ }
+ });
+
+ Comparator<ResolveInfo> resolveInfoComparator = new Comparator<ResolveInfo>() {
+ @Override
+ public int compare(ResolveInfo object1, ResolveInfo object2) {
+ return object1.loadLabel(mPackageManager).toString().compareTo(
+ object2.loadLabel(mPackageManager).toString());
+ }
+ };
+
+ // get the list of live folder intents
+ Intent liveFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER);
+ mFolderList = mPackageManager.queryIntentActivities(liveFolderIntent, 0);
+
+ // manually create a separate entry for creating a folder in Launcher
+ ResolveInfo folder = new ResolveInfo();
+ folder.icon = R.drawable.ic_launcher_folder;
+ folder.labelRes = R.string.group_folder;
+ folder.resolvePackageName = context.getPackageName();
+ mFolderList.add(0, folder);
+ Collections.sort(mFolderList, resolveInfoComparator);
+
+ // get the list of shortcuts
+ Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
+ mShortcutList = mPackageManager.queryIntentActivities(shortcutsIntent, 0);
+ Collections.sort(mShortcutList, resolveInfoComparator);
+
+ // get the list of wallpapers
+ Intent wallpapersIntent = new Intent(Intent.ACTION_SET_WALLPAPER);
+ mWallpaperList = mPackageManager.queryIntentActivities(wallpapersIntent, 0);
+ Collections.sort(mWallpaperList, resolveInfoComparator);
+
+ // reset the icon cache
+ mPageViewIconCache.clear();
+
+ // Refresh all the tabs
+ invalidatePageData();
+ }
+
+ public void setDragController(DragController dragger) {
+ mDragController = dragger;
+ }
+
+ public void setCustomizationFilter(CustomizationType filterType) {
+ mCustomizationType = filterType;
+ setCurrentPage(0);
+ invalidatePageData();
+
+ // End the current choice mode so that we don't carry selections across tabs
+ endChoiceMode();
+ }
+
+ @Override
+ public void onDropCompleted(View target, boolean success) {
+ // do nothing
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (!v.isInTouchMode()) {
+ return;
+ }
+
+ // On certain pages, we allow single tap to mark items as selected so that they can be
+ // dropped onto the mini workspaces
+ boolean enterChoiceMode = false;
+ switch (mCustomizationType) {
+ case WidgetCustomization:
+ mChoiceModeTitleText = R.string.cab_widget_selection_text;
+ enterChoiceMode = true;
+ break;
+ case ApplicationCustomization:
+ mChoiceModeTitleText = R.string.cab_app_selection_text;
+ enterChoiceMode = true;
+ break;
+ case FolderCustomization:
+ mChoiceModeTitleText = R.string.cab_folder_selection_text;
+ enterChoiceMode = true;
+ break;
+ case ShortcutCustomization:
+ mChoiceModeTitleText = R.string.cab_shortcut_selection_text;
+ enterChoiceMode = true;
+ break;
+ default:
+ break;
+ }
+
+ if (enterChoiceMode) {
+ if (v instanceof Checkable) {
+ final Checkable c = (Checkable) v;
+ final boolean wasChecked = c.isChecked();
+ resetCheckedGrandchildren();
+ c.setChecked(!wasChecked);
+
+ // End the current choice mode when we have no items selected
+ /*if (!c.isChecked()) {
+ endChoiceMode();
+ } else if (isChoiceMode(CHOICE_MODE_NONE)) {
+ endChoiceMode();
+ startChoiceMode(CHOICE_MODE_SINGLE, this);
+ }*/
+ mChoiceMode = CHOICE_MODE_SINGLE;
+
+ Workspace w = mLauncher.getWorkspace();
+ int currentWorkspaceScreen = mLauncher.getCurrentWorkspaceScreen();
+ final CellLayout cl = (CellLayout)w.getChildAt(currentWorkspaceScreen);
+ cl.setHover(true);
+
+ animateClickFeedback(v, new Runnable() {
+ @Override
+ public void run() {
+ cl.setHover(false);
+ mLauncher.onWorkspaceClick(cl);
+ mChoiceMode = CHOICE_MODE_NONE;
+ }
+ });
+ }
+ return;
+ }
+
+ // Otherwise, we just handle the single click here
+ switch (mCustomizationType) {
+ case WallpaperCustomization:
+ // animate some feedback to the long press
+ final View clickView = v;
+ animateClickFeedback(v, new Runnable() {
+ @Override
+ public void run() {
+ // add the shortcut
+ ResolveInfo info = (ResolveInfo) clickView.getTag();
+ Intent createWallpapersIntent = new Intent(Intent.ACTION_SET_WALLPAPER);
+ ComponentName name = new ComponentName(info.activityInfo.packageName,
+ info.activityInfo.name);
+ createWallpapersIntent.setComponent(name);
+ mLauncher.processWallpaper(createWallpapersIntent);
+ }
+ });
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (!v.isInTouchMode()) {
+ return false;
+ }
+
+ // End the current choice mode before we start dragging anything
+ if (isChoiceMode(CHOICE_MODE_SINGLE)) {
+ endChoiceMode();
+ }
+
+ PendingAddItemInfo createItemInfo;
+ switch (mCustomizationType) {
+ case WidgetCustomization:
+ // Get the icon as the drag representation
+ final LinearLayout l = (LinearLayout) v;
+ final Drawable icon = ((ImageView) l.findViewById(R.id.widget_preview)).getDrawable();
+ Bitmap b = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(),
+ Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(b);
+ icon.draw(c);
+
+ createItemInfo = (PendingAddItemInfo) v.getTag();
+ mDragController.startDrag(v, b, this, createItemInfo, DragController.DRAG_ACTION_COPY,
+ null);
+
+ // Cleanup the icon
+ b.recycle();
+ return true;
+ case FolderCustomization:
+ if (v.getTag() instanceof UserFolderInfo) {
+ // The UserFolderInfo tag is only really used for live folders
+ UserFolderInfo folderInfo = (UserFolderInfo) v.getTag();
+ mDragController.startDrag(
+ v, this, folderInfo, DragController.DRAG_ACTION_COPY, null);
+ } else {
+ createItemInfo = (PendingAddItemInfo) v.getTag();
+ mDragController.startDrag(
+ v, this, createItemInfo, DragController.DRAG_ACTION_COPY, null);
+ }
+ return true;
+ case ShortcutCustomization:
+ createItemInfo = (PendingAddItemInfo) v.getTag();
+ mDragController.startDrag(
+ v, this, createItemInfo, DragController.DRAG_ACTION_COPY, null);
+ return true;
+ case ApplicationCustomization:
+ // Pick up the application for dropping
+ ApplicationInfo app = (ApplicationInfo) v.getTag();
+ app = new ApplicationInfo(app);
+
+ mDragController.startDrag(v, this, app, DragController.DRAG_ACTION_COPY);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Pre-processes the layout of the different widget pages.
+ * @return the number of pages of widgets that we have
+ */
+ private int relayoutWidgets() {
+ if (mWidgetList.isEmpty()) return 0;
+
+ // create a new page for the first set of widgets
+ ArrayList<AppWidgetProviderInfo> newPage = new ArrayList<AppWidgetProviderInfo>();
+ mWidgetPages.clear();
+ mWidgetPages.add(newPage);
+
+ // do this until we have no more widgets to lay out
+ final int maxNumCellsPerRow = mMaxWidgetsCellHSpan;
+ final int widgetCount = mWidgetList.size();
+ int numCellsInRow = 0;
+ for (int i = 0; i < widgetCount; ++i) {
+ final AppWidgetProviderInfo info = mWidgetList.get(i);
+
+ // determine the size of the current widget
+ int cellSpanX = Math.max(sMinWidgetCellHSpan, Math.min(sMaxWidgetCellHSpan,
+ mWorkspaceWidgetLayout.estimateCellHSpan(info.minWidth)));
+
+ // create a new page if necessary
+ if ((numCellsInRow + cellSpanX) > maxNumCellsPerRow) {
+ numCellsInRow = 0;
+ newPage = new ArrayList<AppWidgetProviderInfo>();
+ mWidgetPages.add(newPage);
+ }
+
+ // add the item to the current page
+ newPage.add(info);
+ numCellsInRow += cellSpanX;
+ }
+
+ return mWidgetPages.size();
+ }
+
+ /**
+ * This method will extract the preview image specified by the widget developer (if it exists),
+ * otherwise, it will try to generate a default image preview with the widget's package icon.
+ * @return the drawable will be used and sized in the ImageView to represent the widget
+ */
+ private Drawable getWidgetIcon(AppWidgetProviderInfo info) {
+ PackageManager packageManager = mLauncher.getPackageManager();
+ String packageName = info.provider.getPackageName();
+ Drawable drawable = null;
+ if (info.previewImage != 0) {
+ drawable = packageManager.getDrawable(packageName, info.previewImage, null);
+ if (drawable == null) {
+ Log.w(TAG, "Can't load icon drawable 0x" + Integer.toHexString(info.icon)
+ + " for provider: " + info.provider);
+ } else {
+ return drawable;
+ }
+ }
+
+ // If we don't have a preview image, create a default one
+ if (drawable == null) {
+ Resources resources = mLauncher.getResources();
+
+ // Create a new bitmap to hold the widget preview
+ final int minDim = mWorkspaceWidgetLayout.estimateCellWidth(1);
+ final int maxDim = mWorkspaceWidgetLayout.estimateCellWidth(3);
+ int width = (int) (Math.max(minDim, Math.min(maxDim, info.minWidth)) * sScaleFactor);
+ int height = (int) (Math.max(minDim, Math.min(maxDim, info.minHeight)) * sScaleFactor);
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
+ mCanvas.setBitmap(bitmap);
+ // For some reason, we must re-set the clip rect here, otherwise it will be wrong
+ mCanvas.clipRect(0, 0, width, height, Op.REPLACE);
+
+ Drawable background = resources.getDrawable(R.drawable.default_widget_preview);
+ background.setBounds(0, 0, width, height);
+ background.draw(mCanvas);
+
+ // Draw the icon vertically centered, flush left
+ try {
+ Rect tmpRect = new Rect();
+ Drawable icon = null;
+ if (info.icon > 0) {
+ icon = packageManager.getDrawable(packageName, info.icon, null);
+ } else {
+ icon = resources.getDrawable(R.drawable.ic_launcher_application);
+ }
+ background.getPadding(tmpRect);
+
+ final int iconSize = minDim / 2;
+ final int offset = iconSize / 4;
+ icon.setBounds(new Rect(offset, offset, offset + iconSize, offset + iconSize));
+ icon.draw(mCanvas);
+ } catch (Resources.NotFoundException e) {
+ // if we can't find the icon, then just don't draw it
+ }
+
+ drawable = new FastBitmapDrawable(bitmap);
+ }
+ drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ return drawable;
+ }
+
+ private void setupPage(PagedViewCellLayout layout) {
+ layout.setCellCount(mCellCountX, mCellCountY);
+ layout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop, mPageLayoutPaddingRight,
+ mPageLayoutPaddingBottom);
+ }
+
+ private void setupWorkspaceLayout() {
+ mWorkspaceWidgetLayout.setCellCount(mCellCountX, mCellCountY);
+ mWorkspaceWidgetLayout.setPadding(20, 10, 20, 0);
+
+ mMaxWidgetWidth = mWorkspaceWidgetLayout.estimateCellWidth(sMaxWidgetCellHSpan);
+ }
+
+ private void syncWidgetPages() {
+ if (mWidgetList == null) return;
+
+ // we need to repopulate with the LinearLayout layout for the widget pages
+ removeAllViews();
+ int numPages = relayoutWidgets();
+ for (int i = 0; i < numPages; ++i) {
+ LinearLayout layout = new WidgetLayout(getContext());
+ layout.setGravity(Gravity.CENTER_HORIZONTAL);
+
+ // Temporary change to prevent the last page from being too small (and items bleeding
+ // onto it). We can remove this once we properly fix the fading algorithm
+ if (i < numPages - 1) {
+ addView(layout, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.MATCH_PARENT));
+ } else {
+ addView(layout, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.MATCH_PARENT));
+ }
+ }
+ }
+
+ private void syncWidgetPageItems(int page) {
+ // ensure that we have the right number of items on the pages
+ LinearLayout layout = (LinearLayout) getChildAt(page);
+ final ArrayList<AppWidgetProviderInfo> list = mWidgetPages.get(page);
+ final int count = list.size();
+ layout.removeAllViews();
+ for (int i = 0; i < count; ++i) {
+ AppWidgetProviderInfo info = (AppWidgetProviderInfo) list.get(i);
+ PendingAddItemInfo createItemInfo = new PendingAddItemInfo();
+ createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+ createItemInfo.componentName = info.provider;
+
+ LinearLayout l = (LinearLayout) mInflater.inflate(
+ R.layout.customize_paged_view_widget, layout, false);
+ l.setTag(createItemInfo);
+ l.setOnClickListener(this);
+ l.setOnLongClickListener(this);
+
+ final Drawable icon = getWidgetIcon(info);
+ final int hSpan = mWorkspaceWidgetLayout.estimateCellHSpan(info.minWidth);
+ final int vSpan = mWorkspaceWidgetLayout.estimateCellHSpan(info.minHeight);
+
+ ImageView image = (ImageView) l.findViewById(R.id.widget_preview);
+ image.setMaxWidth(mMaxWidgetWidth);
+ image.setImageDrawable(icon);
+ TextView name = (TextView) l.findViewById(R.id.widget_name);
+ name.setText(info.label);
+ TextView dims = (TextView) l.findViewById(R.id.widget_dims);
+ dims.setText(mContext.getString(R.string.widget_dims_format, hSpan, vSpan));
+
+ layout.addView(l);
+ }
+ }
+
+ private void syncListPages(List<ResolveInfo> list) {
+ // we need to repopulate with PagedViewCellLayouts
+ removeAllViews();
+
+ // ensure that we have the right number of pages
+ int numPages = (int) Math.ceil((float) list.size() / (mCellCountX * mCellCountY));
+ for (int i = 0; i < numPages; ++i) {
+ PagedViewCellLayout layout = new PagedViewCellLayout(getContext());
+ setupPage(layout);
+ addView(layout);
+ }
+ }
+
+ private void syncListPageItems(int page, List<ResolveInfo> list) {
+ // ensure that we have the right number of items on the pages
+ int numCells = mCellCountX * mCellCountY;
+ int startIndex = page * numCells;
+ int endIndex = Math.min(startIndex + numCells, list.size());
+ PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page);
+ // TODO: we can optimize by just re-applying to existing views
+ layout.removeAllViews();
+ for (int i = startIndex; i < endIndex; ++i) {
+ ResolveInfo info = list.get(i);
+ PendingAddItemInfo createItemInfo = new PendingAddItemInfo();
+
+ PagedViewIcon icon = (PagedViewIcon) mInflater.inflate(
+ R.layout.customize_paged_view_item, layout, false);
+ icon.applyFromResolveInfo(info, mPackageManager, mPageViewIconCache);
+ switch (mCustomizationType) {
+ case WallpaperCustomization:
+ icon.setOnClickListener(this);
+ break;
+ case FolderCustomization:
+ if (info.labelRes != R.string.group_folder) {
+ createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER;
+ createItemInfo.componentName = new ComponentName(info.activityInfo.packageName,
+ info.activityInfo.name);
+ icon.setTag(createItemInfo);
+ } else {
+ UserFolderInfo folderInfo = new UserFolderInfo();
+ folderInfo.title = getResources().getText(R.string.folder_name);
+ icon.setTag(folderInfo);
+ }
+ icon.setOnClickListener(this);
+ icon.setOnLongClickListener(this);
+ break;
+ case ShortcutCustomization:
+ createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+ createItemInfo.componentName = new ComponentName(info.activityInfo.packageName,
+ info.activityInfo.name);
+ icon.setTag(createItemInfo);
+ icon.setOnClickListener(this);
+ icon.setOnLongClickListener(this);
+ break;
+ default:
+ break;
+ }
+
+ final int index = i - startIndex;
+ final int x = index % mCellCountX;
+ final int y = index / mCellCountX;
+ setupPage(layout);
+ layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1));
+ }
+ }
+
+ private void syncAppPages() {
+ if (mApps == null) return;
+
+ // We need to repopulate with PagedViewCellLayouts
+ removeAllViews();
+
+ // Ensure that we have the right number of pages
+ int numPages = (int) Math.ceil((float) mApps.size() / (mCellCountX * mCellCountY));
+ for (int i = 0; i < numPages; ++i) {
+ PagedViewCellLayout layout = new PagedViewCellLayout(getContext());
+ setupPage(layout);
+ addView(layout);
+ }
+ }
+
+ private void syncAppPageItems(int page) {
+ if (mApps == null) return;
+
+ // ensure that we have the right number of items on the pages
+ int numCells = mCellCountX * mCellCountY;
+ int startIndex = page * numCells;
+ int endIndex = Math.min(startIndex + numCells, mApps.size());
+ PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(page);
+ // TODO: we can optimize by just re-applying to existing views
+ layout.removeAllViews();
+ for (int i = startIndex; i < endIndex; ++i) {
+ final ApplicationInfo info = mApps.get(i);
+ PagedViewIcon icon = (PagedViewIcon) mInflater.inflate(
+ R.layout.all_apps_paged_view_application, layout, false);
+ icon.applyFromApplicationInfo(info, mPageViewIconCache);
+ icon.setOnClickListener(this);
+ icon.setOnLongClickListener(this);
+
+ final int index = i - startIndex;
+ final int x = index % mCellCountX;
+ final int y = index / mCellCountX;
+ setupPage(layout);
+ layout.addViewToCellLayout(icon, -1, i, new PagedViewCellLayout.LayoutParams(x,y, 1,1));
+ }
+ }
+
+ @Override
+ public void syncPages() {
+ boolean centerPagedViewCellLayouts = false;
+ switch (mCustomizationType) {
+ case WidgetCustomization:
+ syncWidgetPages();
+ break;
+ case FolderCustomization:
+ syncListPages(mFolderList);
+ centerPagedViewCellLayouts = true;
+ break;
+ case ShortcutCustomization:
+ syncListPages(mShortcutList);
+ centerPagedViewCellLayouts = true;
+ break;
+ case WallpaperCustomization:
+ syncListPages(mWallpaperList);
+ centerPagedViewCellLayouts = true;
+ break;
+ case ApplicationCustomization:
+ syncAppPages();
+ centerPagedViewCellLayouts = false;
+ break;
+ default:
+ removeAllViews();
+ setCurrentPage(0);
+ break;
+ }
+
+ // only try and center the page if there is one page
+ final int childCount = getChildCount();
+ if (centerPagedViewCellLayouts) {
+ if (childCount == 1) {
+ PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(0);
+ layout.enableCenteredContent(true);
+ } else {
+ for (int i = 0; i < childCount; ++i) {
+ PagedViewCellLayout layout = (PagedViewCellLayout) getChildAt(i);
+ layout.enableCenteredContent(false);
+ }
+ }
+ }
+
+ // bound the current page
+ setCurrentPage(Math.max(0, Math.min(childCount - 1, getCurrentPage())));
+ }
+
+ @Override
+ public void syncPageItems(int page) {
+ switch (mCustomizationType) {
+ case WidgetCustomization:
+ syncWidgetPageItems(page);
+ break;
+ case FolderCustomization:
+ syncListPageItems(page, mFolderList);
+ break;
+ case ShortcutCustomization:
+ syncListPageItems(page, mShortcutList);
+ break;
+ case WallpaperCustomization:
+ syncListPageItems(page, mWallpaperList);
+ break;
+ case ApplicationCustomization:
+ syncAppPageItems(page);
+ break;
+ }
+ }
+
+ @Override
+ protected int getAssociatedLowerPageBound(int page) {
+ return 0;
+ }
+ @Override
+ protected int getAssociatedUpperPageBound(int page) {
+ return getChildCount();
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ mode.setTitle(mChoiceModeTitleText);
+ return true;
+ }
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ return true;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ endChoiceMode();
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return false;
+ }
+
+}
diff --git a/src/com/android/launcher2/DeferredHandler.java b/src/com/android/launcher2/DeferredHandler.java
index 7801642d2..0323c7f4d 100644
--- a/src/com/android/launcher2/DeferredHandler.java
+++ b/src/com/android/launcher2/DeferredHandler.java
@@ -16,13 +16,12 @@
package com.android.launcher2;
+import java.util.LinkedList;
+
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
-import android.util.Log;
-
-import java.util.LinkedList;
/**
* Queue of things to run on a looper thread. Items posted with {@link #post} will not
diff --git a/src/com/android/launcher2/DeleteZone.java b/src/com/android/launcher2/DeleteZone.java
index 4e8b204fb..1f54b366b 100644
--- a/src/com/android/launcher2/DeleteZone.java
+++ b/src/com/android/launcher2/DeleteZone.java
@@ -16,24 +16,23 @@
package com.android.launcher2;
-import android.widget.ImageView;
+import com.android.launcher.R;
+
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.TransitionDrawable;
import android.util.AttributeSet;
import android.view.View;
-import android.view.animation.TranslateAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
-import android.graphics.RectF;
-import android.graphics.drawable.TransitionDrawable;
-
-import com.android.launcher.R;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.TranslateAnimation;
+import android.widget.ImageView;
public class DeleteZone extends ImageView implements DropTarget, DragController.DragListener {
private static final int ORIENTATION_HORIZONTAL = 1;
@@ -45,6 +44,13 @@ public class DeleteZone extends ImageView implements DropTarget, DragController.
private Launcher mLauncher;
private boolean mTrashMode;
+ /**
+ * If true, this View responsible for managing its own visibility, and that of its handle.
+ * This is generally the case, but it will be set to false when this is part of the
+ * Contextual Action Bar.
+ */
+ private boolean mManageVisibility = true;
+
private AnimationSet mInAnimation;
private AnimationSet mOutAnimation;
private Animation mHandleInAnimation;
@@ -55,9 +61,11 @@ public class DeleteZone extends ImageView implements DropTarget, DragController.
private final RectF mRegion = new RectF();
private TransitionDrawable mTransition;
- private View mHandle;
private final Paint mTrashPaint = new Paint();
+ /** The View that this view will replace. */
+ private View mHandle = null;
+
public DeleteZone(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@@ -71,6 +79,7 @@ public class DeleteZone extends ImageView implements DropTarget, DragController.
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DeleteZone, defStyle, 0);
mOrientation = a.getInt(R.styleable.DeleteZone_direction, ORIENTATION_HORIZONTAL);
a.recycle();
+
}
@Override
@@ -83,31 +92,30 @@ public class DeleteZone extends ImageView implements DropTarget, DragController.
DragView dragView, Object dragInfo) {
return true;
}
-
- public Rect estimateDropLocation(DragSource source, int x, int y, int xOffset, int yOffset,
- DragView dragView, Object dragInfo, Rect recycle) {
- return null;
- }
public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
DragView dragView, Object dragInfo) {
final ItemInfo item = (ItemInfo) dragInfo;
+ // On x-large screens, you can uninstall an app by dragging from all apps
+ if (item instanceof ApplicationInfo && LauncherApplication.isScreenXLarge()) {
+ mLauncher.startApplicationUninstallActivity((ApplicationInfo) item);
+ }
+
if (item.container == -1) return;
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
if (item instanceof LauncherAppWidgetInfo) {
mLauncher.removeAppWidget((LauncherAppWidgetInfo) item);
}
- } else {
- if (source instanceof UserFolder) {
- final UserFolder userFolder = (UserFolder) source;
- final UserFolderInfo userFolderInfo = (UserFolderInfo) userFolder.getInfo();
- // Item must be a ShortcutInfo otherwise it couldn't have been in the folder
- // in the first place.
- userFolderInfo.remove((ShortcutInfo)item);
- }
+ } else if (source instanceof UserFolder) {
+ final UserFolder userFolder = (UserFolder) source;
+ final UserFolderInfo userFolderInfo = (UserFolderInfo) userFolder.getInfo();
+ // Item must be a ShortcutInfo otherwise it couldn't have been in the folder
+ // in the first place.
+ userFolderInfo.remove((ShortcutInfo)item);
}
+
if (item instanceof UserFolderInfo) {
final UserFolderInfo userFolderInfo = (UserFolderInfo)item;
LauncherModel.deleteUserFolderContentsFromDatabase(mLauncher, userFolderInfo);
@@ -126,6 +134,7 @@ public class DeleteZone extends ImageView implements DropTarget, DragController.
}.start();
}
}
+
LauncherModel.deleteItemFromDatabase(mLauncher, item);
}
@@ -149,16 +158,21 @@ public class DeleteZone extends ImageView implements DropTarget, DragController.
final ItemInfo item = (ItemInfo) info;
if (item != null) {
mTrashMode = true;
- createAnimations();
final int[] location = mLocation;
getLocationOnScreen(location);
mRegion.set(location[0], location[1], location[0] + mRight - mLeft,
location[1] + mBottom - mTop);
mDragController.setDeleteRegion(mRegion);
+
+ // Make sure the icon is set to the default drawable, not the hover drawable
mTransition.resetTransition();
- startAnimation(mInAnimation);
- mHandle.startAnimation(mHandleOutAnimation);
- setVisibility(VISIBLE);
+
+ if (mManageVisibility) {
+ createAnimations();
+ startAnimation(mInAnimation);
+ mHandle.startAnimation(mHandleOutAnimation);
+ setVisibility(VISIBLE);
+ }
}
}
@@ -166,12 +180,20 @@ public class DeleteZone extends ImageView implements DropTarget, DragController.
if (mTrashMode) {
mTrashMode = false;
mDragController.setDeleteRegion(null);
- startAnimation(mOutAnimation);
- mHandle.startAnimation(mHandleInAnimation);
- setVisibility(GONE);
+
+ if (mOutAnimation != null) startAnimation(mOutAnimation);
+ if (mHandleInAnimation != null) mHandle.startAnimation(mHandleInAnimation);
+
+ if (mManageVisibility) {
+ setVisibility(GONE);
+ }
}
}
+ public boolean isDropEnabled() {
+ return true;
+ }
+
private void createAnimations() {
if (mInAnimation == null) {
mInAnimation = new FastAnimationSet();
@@ -228,6 +250,10 @@ public class DeleteZone extends ImageView implements DropTarget, DragController.
mHandle = view;
}
+ void setManageVisibility(boolean value) {
+ mManageVisibility = value;
+ }
+
private static class FastTranslateAnimation extends TranslateAnimation {
public FastTranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
int fromYType, float fromYValue, int toYType, float toYValue) {
@@ -261,4 +287,10 @@ public class DeleteZone extends ImageView implements DropTarget, DragController.
return false;
}
}
+
+ @Override
+ public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
+ DragView dragView, Object dragInfo) {
+ return null;
+ }
}
diff --git a/src/com/android/launcher2/Dimmable.java b/src/com/android/launcher2/Dimmable.java
new file mode 100644
index 000000000..df43b3c8e
--- /dev/null
+++ b/src/com/android/launcher2/Dimmable.java
@@ -0,0 +1,6 @@
+package com.android.launcher2;
+
+public interface Dimmable {
+ public void setDimmableProgress(float progress);
+ public float getDimmableProgress();
+}
diff --git a/src/com/android/launcher2/DimmableAppWidgetHostView.java b/src/com/android/launcher2/DimmableAppWidgetHostView.java
new file mode 100644
index 000000000..1f512a3f9
--- /dev/null
+++ b/src/com/android/launcher2/DimmableAppWidgetHostView.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import com.android.launcher.R;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.view.View;
+
+public class DimmableAppWidgetHostView extends LauncherAppWidgetHostView implements Dimmable {
+ public DimmableAppWidgetHostView(Context context) {
+ super(context);
+ mPaint.setFilterBitmap(true);
+ }
+
+ private final Paint mPaint = new Paint();
+ private Bitmap mDimmedView;
+ private Canvas mDimmedViewCanvas;
+ private boolean isDimmedViewUpdatePass;
+ private float mDimmableProgress;
+
+ private void setChildAlpha(float alpha) {
+ if (getChildCount() > 0) {
+ final View child = getChildAt(0);
+ if (child.getAlpha() != alpha) {
+ getChildAt(0).setAlpha(alpha);
+ }
+ }
+ }
+
+ private void updateChildAlpha() {
+ // hacky, but sometimes widgets get their alpha set back to 1.0f, so we call
+ // this to force them back
+ setChildAlpha(getAlpha());
+ }
+
+ //@Override
+ public boolean onSetAlpha(int alpha) {
+ super.onSetAlpha(alpha);
+ return true;
+ }
+
+ public void setDimmableProgress(float progress) {
+ mDimmableProgress = progress;
+ }
+
+ public float getDimmableProgress() {
+ return mDimmableProgress;
+ }
+
+ private void updateDimmedView() {
+ if (mDimmedView == null) {
+ mDimmedView = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
+ Bitmap.Config.ARGB_8888);
+ mDimmedViewCanvas = new Canvas(mDimmedView);
+ }
+ mDimmedViewCanvas.drawColor(0x00000000);
+ mDimmedViewCanvas.concat(getMatrix());
+ isDimmedViewUpdatePass = true;
+ draw(mDimmedViewCanvas);
+ // make the bitmap look "dimmed"
+ int dimmedColor = getContext().getResources().getColor(R.color.dimmed_view_color);
+ mDimmedViewCanvas.drawColor(dimmedColor, PorterDuff.Mode.SRC_IN);
+ isDimmedViewUpdatePass = false;
+ invalidate();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ if (mDimmedView == null && mDimmableProgress > 0.0f) {
+ updateDimmedView();
+ }
+ }
+
+ @Override
+ public void dispatchDraw(Canvas canvas) {
+ if (isDimmedViewUpdatePass) {
+ final float alpha = getAlpha();
+ canvas.save();
+ setAlpha(1.0f);
+ super.dispatchDraw(canvas);
+ canvas.restore();
+ setAlpha(alpha);
+ } else {
+ if (mDimmedView != null && mDimmableProgress > 0) {
+ // draw the dimmed version of this widget
+ mPaint.setAlpha((int) (mDimmableProgress * 255));
+ canvas.drawBitmap(mDimmedView, 0, 0, mPaint);
+ }
+
+ updateChildAlpha();
+ super.dispatchDraw(canvas);
+ }
+ }
+}
diff --git a/src/com/android/launcher2/DimmableBubbleTextView.java b/src/com/android/launcher2/DimmableBubbleTextView.java
new file mode 100644
index 000000000..cb3b8efd6
--- /dev/null
+++ b/src/com/android/launcher2/DimmableBubbleTextView.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import com.android.launcher.R;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.util.AttributeSet;
+
+public class DimmableBubbleTextView extends BubbleTextView implements Dimmable {
+ private Paint mDimmedPaint = new Paint();
+ private int mAlpha;
+ private Bitmap mDimmedView;
+ private Canvas mDimmedViewCanvas;
+ private boolean isDimmedViewUpdatePass;
+ private float mDimmableProgress;
+
+ public DimmableBubbleTextView(Context context) {
+ super(context);
+ mDimmedPaint.setFilterBitmap(true);
+ }
+
+ public DimmableBubbleTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mDimmedPaint.setFilterBitmap(true);
+ }
+
+ public DimmableBubbleTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mDimmedPaint.setFilterBitmap(true);
+ }
+
+ public void setDimmableProgress(float progress) {
+ mDimmableProgress = progress;
+ }
+
+ public float getDimmableProgress() {
+ return mDimmableProgress;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ if (mDimmedView == null) {
+ isDimmedViewUpdatePass = true;
+ mDimmedView = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
+ Bitmap.Config.ARGB_8888);
+ mDimmedViewCanvas = new Canvas(mDimmedView);
+ mDimmedViewCanvas.concat(getMatrix());
+
+ draw(mDimmedViewCanvas);
+
+ // MAKE THE DIMMED VERSION
+ int dimmedColor = getContext().getResources().getColor(R.color.dimmed_view_color);
+ mDimmedViewCanvas.drawColor(dimmedColor, PorterDuff.Mode.SRC_IN);
+
+ isDimmedViewUpdatePass = false;
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (isDimmedViewUpdatePass) {
+ canvas.save();
+ final float alpha = getAlpha();
+ super.setAlpha(1.0f);
+ super.onDraw(canvas);
+ super.setAlpha(alpha);
+ canvas.restore();
+ } else {
+ super.onDraw(canvas);
+ }
+
+ if (mDimmedView != null && mDimmableProgress > 0) {
+ mDimmedPaint.setAlpha((int) (mDimmableProgress * 255));
+ canvas.drawBitmap(mDimmedView, mScrollX, mScrollY, mDimmedPaint);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index b453061df..87b3473a1 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -18,23 +18,22 @@ package com.android.launcher2;
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.os.IBinder;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Vibrator;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.view.View;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import java.util.ArrayList;
-import com.android.launcher.R;
-
/**
* Class for initiating a drag within a view or across multiple views.
*/
@@ -49,6 +48,7 @@ public class DragController {
public static int DRAG_ACTION_COPY = 1;
private static final int SCROLL_DELAY = 600;
+ private static final int SCROLL_ZONE = 20;
private static final int VIBRATE_DURATION = 35;
private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
@@ -88,11 +88,6 @@ public class DragController {
/** Y offset from the upper-left corner of the cell to where we touched. */
private float mTouchOffsetY;
- /** the area at the edge of the screen that makes the workspace go left
- * or right while you're dragging.
- */
- private int mScrollZone;
-
/** Where the drag originated */
private DragSource mDragSource;
@@ -105,7 +100,7 @@ public class DragController {
/** Who can receive drop events */
private ArrayList<DropTarget> mDropTargets = new ArrayList<DropTarget>();
- private DragListener mListener;
+ private ArrayList<DragListener> mListeners = new ArrayList<DragListener>();
/** The window token used as the parent for the DragView. */
private IBinder mWindowToken;
@@ -140,7 +135,7 @@ public class DragController {
void onDragStart(DragSource source, Object info, int dragAction);
/**
- * The drag has eneded
+ * The drag has ended
*/
void onDragEnd();
}
@@ -153,12 +148,11 @@ public class DragController {
public DragController(Context context) {
mContext = context;
mHandler = new Handler();
- mScrollZone = context.getResources().getDimensionPixelSize(R.dimen.scroll_zone);
}
/**
* Starts a drag.
- *
+ *
* @param v The view that is being dragged
* @param source An object representing where the drag originated
* @param dragInfo The data associated with the object that is being dragged
@@ -166,6 +160,22 @@ public class DragController {
* {@link #DRAG_ACTION_COPY}
*/
public void startDrag(View v, DragSource source, Object dragInfo, int dragAction) {
+ startDrag(v, source, dragInfo, dragAction, null);
+ }
+
+ /**
+ * Starts a drag.
+ *
+ * @param v The view that is being dragged
+ * @param source An object representing where the drag originated
+ * @param dragInfo The data associated with the object that is being dragged
+ * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
+ * {@link #DRAG_ACTION_COPY}
+ * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
+ * Makes dragging feel more precise, e.g. you can clip out a transparent border
+ */
+ public void startDrag(View v, DragSource source, Object dragInfo, int dragAction,
+ Rect dragRegion) {
mOriginator = v;
Bitmap b = getViewBitmap(v);
@@ -181,7 +191,7 @@ public class DragController {
int screenY = loc[1];
startDrag(b, screenX, screenY, 0, 0, b.getWidth(), b.getHeight(),
- source, dragInfo, dragAction);
+ source, dragInfo, dragAction, dragRegion);
b.recycle();
@@ -192,7 +202,36 @@ public class DragController {
/**
* Starts a drag.
- *
+ *
+ * @param v The view that is being dragged
+ * @param bmp The bitmap that represents the view being dragged
+ * @param source An object representing where the drag originated
+ * @param dragInfo The data associated with the object that is being dragged
+ * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
+ * {@link #DRAG_ACTION_COPY}
+ * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
+ * Makes dragging feel more precise, e.g. you can clip out a transparent border
+ */
+ public void startDrag(View v, Bitmap bmp, DragSource source, Object dragInfo, int dragAction,
+ Rect dragRegion) {
+ mOriginator = v;
+
+ int[] loc = mCoordinatesTemp;
+ v.getLocationOnScreen(loc);
+ int screenX = loc[0];
+ int screenY = loc[1];
+
+ startDrag(bmp, screenX, screenY, 0, 0, bmp.getWidth(), bmp.getHeight(),
+ source, dragInfo, dragAction, dragRegion);
+
+ if (dragAction == DRAG_ACTION_MOVE) {
+ v.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * Starts a drag.
+ *
* @param b The bitmap to display as the drag image. It will be re-scaled to the
* enlarged size.
* @param screenX The x position on screen of the left-top of the bitmap.
@@ -209,6 +248,31 @@ public class DragController {
public void startDrag(Bitmap b, int screenX, int screenY,
int textureLeft, int textureTop, int textureWidth, int textureHeight,
DragSource source, Object dragInfo, int dragAction) {
+ startDrag(b, screenX, screenY, textureLeft, textureTop, textureWidth, textureHeight,
+ source, dragInfo, dragAction, null);
+ }
+
+ /**
+ * Starts a drag.
+ *
+ * @param b The bitmap to display as the drag image. It will be re-scaled to the
+ * enlarged size.
+ * @param screenX The x position on screen of the left-top of the bitmap.
+ * @param screenY The y position on screen of the left-top of the bitmap.
+ * @param textureLeft The left edge of the region inside b to use.
+ * @param textureTop The top edge of the region inside b to use.
+ * @param textureWidth The width of the region inside b to use.
+ * @param textureHeight The height of the region inside b to use.
+ * @param source An object representing where the drag originated
+ * @param dragInfo The data associated with the object that is being dragged
+ * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
+ * {@link #DRAG_ACTION_COPY}
+ * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
+ * Makes dragging feel more precise, e.g. you can clip out a transparent border
+ */
+ public void startDrag(Bitmap b, int screenX, int screenY,
+ int textureLeft, int textureTop, int textureWidth, int textureHeight,
+ DragSource source, Object dragInfo, int dragAction, Rect dragRegion) {
if (PROFILE_DRAWING_DURING_DRAG) {
android.os.Debug.startMethodTracing("Launcher");
}
@@ -220,15 +284,17 @@ public class DragController {
}
mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);
- if (mListener != null) {
- mListener.onDragStart(source, dragInfo, dragAction);
+ for (DragListener listener : mListeners) {
+ listener.onDragStart(source, dragInfo, dragAction);
}
int registrationX = ((int)mMotionDownX) - screenX;
int registrationY = ((int)mMotionDownY) - screenY;
- mTouchOffsetX = mMotionDownX - screenX;
- mTouchOffsetY = mMotionDownY - screenY;
+ final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
+ final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
+ mTouchOffsetX = mMotionDownX - screenX - dragRegionLeft;
+ mTouchOffsetY = mMotionDownY - screenY - dragRegionTop;
mDragging = true;
mDragSource = source;
@@ -238,6 +304,12 @@ public class DragController {
DragView dragView = mDragView = new DragView(mContext, b, registrationX, registrationY,
textureLeft, textureTop, textureWidth, textureHeight);
+
+ if (dragRegion != null) {
+ dragView.setDragRegion(dragRegionLeft, dragRegion.top,
+ dragRegion.right - dragRegionLeft, dragRegion.bottom - dragRegionTop);
+ }
+
dragView.show(mWindowToken, (int)mMotionDownX, (int)mMotionDownY);
}
@@ -304,8 +376,8 @@ public class DragController {
if (mOriginator != null) {
mOriginator.setVisibility(View.VISIBLE);
}
- if (mListener != null) {
- mListener.onDragEnd();
+ for (DragListener listener : mListeners) {
+ listener.onDragEnd();
}
if (mDragView != null) {
mDragView.remove();
@@ -385,7 +457,7 @@ public class DragController {
mMotionDownX = screenX;
mMotionDownY = screenY;
- if ((screenX < mScrollZone) || (screenX > scrollView.getWidth() - mScrollZone)) {
+ if ((screenX < SCROLL_ZONE) || (screenX > scrollView.getWidth() - SCROLL_ZONE)) {
mScrollState = SCROLL_WAITING_IN_ZONE;
mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
} else {
@@ -402,6 +474,13 @@ public class DragController {
final int[] coordinates = mCoordinatesTemp;
DropTarget dropTarget = findDropTarget(screenX, screenY, coordinates);
if (dropTarget != null) {
+ DropTarget delegate = dropTarget.getDropTargetDelegate(
+ mDragSource, coordinates[0], coordinates[1],
+ (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
+ if (delegate != null) {
+ dropTarget = delegate;
+ }
+
if (mLastDropTarget == dropTarget) {
dropTarget.onDragOver(mDragSource, coordinates[0], coordinates[1],
(int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
@@ -426,15 +505,13 @@ public class DragController {
if (mDeleteRegion != null) {
inDeleteRegion = mDeleteRegion.contains(screenX, screenY);
}
- //Log.d(Launcher.TAG, "inDeleteRegion=" + inDeleteRegion + " screenX=" + screenX
- // + " mScrollZone=" + mScrollZone);
- if (!inDeleteRegion && screenX < mScrollZone) {
+ if (!inDeleteRegion && screenX < SCROLL_ZONE) {
if (mScrollState == SCROLL_OUTSIDE_ZONE) {
mScrollState = SCROLL_WAITING_IN_ZONE;
mScrollRunnable.setDirection(SCROLL_LEFT);
mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
}
- } else if (!inDeleteRegion && screenX > scrollView.getWidth() - mScrollZone) {
+ } else if (!inDeleteRegion && screenX > scrollView.getWidth() - SCROLL_ZONE) {
if (mScrollState == SCROLL_OUTSIDE_ZONE) {
mScrollState = SCROLL_WAITING_IN_ZONE;
mScrollRunnable.setDirection(SCROLL_RIGHT);
@@ -491,13 +568,28 @@ public class DragController {
final ArrayList<DropTarget> dropTargets = mDropTargets;
final int count = dropTargets.size();
for (int i=count-1; i>=0; i--) {
- final DropTarget target = dropTargets.get(i);
+ DropTarget target = dropTargets.get(i);
+ if (!target.isDropEnabled())
+ continue;
+
target.getHitRect(r);
+
+ // Convert the hit rect to screen coordinates
target.getLocationOnScreen(dropCoordinates);
r.offset(dropCoordinates[0] - target.getLeft(), dropCoordinates[1] - target.getTop());
+
if (r.contains(x, y)) {
+ DropTarget delegate = target.getDropTargetDelegate(mDragSource,
+ x, y, (int)mTouchOffsetX, (int)mTouchOffsetY, mDragView, mDragInfo);
+ if (delegate != null) {
+ target = delegate;
+ target.getLocationOnScreen(dropCoordinates);
+ }
+
+ // Make dropCoordinates relative to the DropTarget
dropCoordinates[0] = x - dropCoordinates[0];
dropCoordinates[1] = y - dropCoordinates[1];
+
return target;
}
}
@@ -537,15 +629,15 @@ public class DragController {
/**
* Sets the drag listner which will be notified when a drag starts or ends.
*/
- public void setDragListener(DragListener l) {
- mListener = l;
+ public void addDragListener(DragListener l) {
+ mListeners.add(l);
}
/**
* Remove a previously installed drag listener.
*/
public void removeDragListener(DragListener l) {
- mListener = null;
+ mListeners.remove(l);
}
/**
diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java
index c68320764..ab71670a2 100644
--- a/src/com/android/launcher2/DragLayer.java
+++ b/src/com/android/launcher2/DragLayer.java
@@ -18,13 +18,13 @@ package com.android.launcher2;
import android.content.Context;
import android.util.AttributeSet;
-import android.view.MotionEvent;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
/**
- * A ViewGroup that coordinated dragging across its dscendants
+ * A ViewGroup that coordinates dragging across its descendants
*/
public class DragLayer extends FrameLayout {
DragController mDragController;
@@ -33,7 +33,7 @@ public class DragLayer extends FrameLayout {
* Used to create a new DragLayer from XML.
*
* @param context The application's context.
- * @param attrs The attribtues set containing the Workspace's customization values.
+ * @param attrs The attributes set containing the Workspace's customization values.
*/
public DragLayer(Context context, AttributeSet attrs) {
super(context, attrs);
diff --git a/src/com/android/launcher2/DragView.java b/src/com/android/launcher2/DragView.java
index 248712ed3..d14f5f756 100644
--- a/src/com/android/launcher2/DragView.java
+++ b/src/com/android/launcher2/DragView.java
@@ -18,20 +18,15 @@
package com.android.launcher2;
import android.content.Context;
-import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
-import android.graphics.Point;
import android.os.IBinder;
-import android.util.AttributeSet;
-import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
-import android.view.KeyEvent;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
@@ -44,6 +39,11 @@ public class DragView extends View implements TweenCallback {
private int mRegistrationX;
private int mRegistrationY;
+ private int mDragRegionLeft = 0;
+ private int mDragRegionTop = 0;
+ private int mDragRegionWidth;
+ private int mDragRegionHeight;
+
SymmetricalLinearTween mTween;
private float mScale;
private float mAnimationScale = 1.0f;
@@ -76,12 +76,52 @@ public class DragView extends View implements TweenCallback {
scale.setScale(scaleFactor, scaleFactor);
mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height, scale, true);
+ setDragRegion(0, 0, width, height);
// The point in our scaled bitmap that the touch events are located
mRegistrationX = registrationX + (DRAG_SCALE / 2);
mRegistrationY = registrationY + (DRAG_SCALE / 2);
}
+ public void setDragRegion(int left, int top, int width, int height) {
+ mDragRegionLeft = left;
+ mDragRegionTop = top;
+ mDragRegionWidth = width;
+ mDragRegionHeight = height;
+ }
+
+ public int getScaledDragRegionXOffset() {
+ return -(int)((mScale - 1.0f) * mDragRegionWidth / 2);
+ }
+
+ public int getScaledDragRegionWidth() {
+ return (int)(mScale * mDragRegionWidth);
+ }
+
+ public int getScaledDragRegionYOffset() {
+ return -(int)((mScale - 1.0f) * mDragRegionHeight / 2);
+ }
+
+ public int getScaledDragRegionHeight() {
+ return (int)(mScale * mDragRegionWidth);
+ }
+
+ public int getDragRegionLeft() {
+ return mDragRegionLeft;
+ }
+
+ public int getDragRegionTop() {
+ return mDragRegionTop;
+ }
+
+ public int getDragRegionWidth() {
+ return mDragRegionWidth;
+ }
+
+ public int getDragRegionHeight() {
+ return mDragRegionHeight;
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
diff --git a/src/com/android/launcher2/DropTarget.java b/src/com/android/launcher2/DropTarget.java
index 72eb33094..308dbbee3 100644
--- a/src/com/android/launcher2/DropTarget.java
+++ b/src/com/android/launcher2/DropTarget.java
@@ -23,6 +23,12 @@ import android.graphics.Rect;
*
*/
public interface DropTarget {
+ /**
+ * Used to temporarily disable certain drop targets
+ *
+ * @return boolean specifying whether this drop target is currently enabled
+ */
+ boolean isDropEnabled();
/**
* Handle an object being dropped on the DropTarget
@@ -51,27 +57,28 @@ public interface DropTarget {
DragView dragView, Object dragInfo);
/**
- * Check if a drop action can occur at, or near, the requested location.
- * This may be called repeatedly during a drag, so any calls should return
- * quickly.
- *
+ * Allows a DropTarget to delegate drag and drop events to another object.
+ *
+ * Most subclasses will should just return null from this method.
+ *
* @param source DragSource where the drag started
* @param x X coordinate of the drop location
* @param y Y coordinate of the drop location
- * @param xOffset Horizontal offset with the object being dragged where the
- * original touch happened
- * @param yOffset Vertical offset with the object being dragged where the
- * original touch happened
+ * @param xOffset Horizontal offset with the object being dragged where the original
+ * touch happened
+ * @param yOffset Vertical offset with the object being dragged where the original
+ * touch happened
* @param dragView The DragView that's being dragged around on screen.
* @param dragInfo Data associated with the object being dragged
- * @return True if the drop will be accepted, false otherwise.
+ *
+ * @return The DropTarget to delegate to, or null to not delegate to another object.
*/
- boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset,
+ DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
DragView dragView, Object dragInfo);
/**
- * Estimate the surface area where this object would land if dropped at the
- * given location.
+ * Check if a drop action can occur at, or near, the requested location.
+ * This will be called just before onDrop.
*
* @param source DragSource where the drag started
* @param x X coordinate of the drop location
@@ -82,13 +89,10 @@ public interface DropTarget {
* original touch happened
* @param dragView The DragView that's being dragged around on screen.
* @param dragInfo Data associated with the object being dragged
- * @param recycle {@link Rect} object to be possibly recycled.
- * @return Estimated area that would be occupied if object was dropped at
- * the given location. Should return null if no estimate is found,
- * or if this target doesn't provide estimations.
+ * @return True if the drop will be accepted, false otherwise.
*/
- Rect estimateDropLocation(DragSource source, int x, int y, int xOffset, int yOffset,
- DragView dragView, Object dragInfo, Rect recycle);
+ boolean acceptDrop(DragSource source, int x, int y, int xOffset, int yOffset,
+ DragView dragView, Object dragInfo);
// These methods are implemented in Views
void getHitRect(Rect outRect);
diff --git a/src/com/android/launcher2/FastBitmapDrawable.java b/src/com/android/launcher2/FastBitmapDrawable.java
index 226d6d8d8..1cafa09a7 100644
--- a/src/com/android/launcher2/FastBitmapDrawable.java
+++ b/src/com/android/launcher2/FastBitmapDrawable.java
@@ -17,6 +17,7 @@
package com.android.launcher2;
import android.graphics.drawable.Drawable;
+import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -26,6 +27,7 @@ class FastBitmapDrawable extends Drawable {
private Bitmap mBitmap;
private int mWidth;
private int mHeight;
+ private final Paint mPaint = new Paint();
FastBitmapDrawable(Bitmap b) {
mBitmap = b;
@@ -39,7 +41,7 @@ class FastBitmapDrawable extends Drawable {
@Override
public void draw(Canvas canvas) {
- canvas.drawBitmap(mBitmap, 0.0f, 0.0f, null);
+ canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
}
@Override
@@ -49,6 +51,7 @@ class FastBitmapDrawable extends Drawable {
@Override
public void setAlpha(int alpha) {
+ mPaint.setAlpha(alpha);
}
@Override
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index 7ff83284e..018b28456 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -21,11 +21,11 @@ import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
+import android.widget.AbsListView;
import android.widget.AdapterView;
+import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
-import android.widget.AbsListView;
-import android.widget.BaseAdapter;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
@@ -148,7 +148,7 @@ public class Folder extends LinearLayout implements DragSource, OnItemLongClickL
void onClose() {
final Workspace workspace = mLauncher.getWorkspace();
- workspace.getChildAt(workspace.getCurrentScreen()).requestFocus();
+ workspace.getChildAt(workspace.getCurrentPage()).requestFocus();
}
void bind(FolderInfo info) {
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index 0013644ab..e692d2058 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -29,7 +29,7 @@ import com.android.launcher.R;
/**
* An icon that can appear on in the workspace representing an {@link UserFolder}.
*/
-public class FolderIcon extends BubbleTextView implements DropTarget {
+public class FolderIcon extends DimmableBubbleTextView implements DropTarget {
private UserFolderInfo mInfo;
private Launcher mLauncher;
private Drawable mCloseIcon;
@@ -43,6 +43,10 @@ public class FolderIcon extends BubbleTextView implements DropTarget {
super(context);
}
+ public boolean isDropEnabled() {
+ return !((Workspace)getParent().getParent()).isSmall();
+ }
+
static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
UserFolderInfo folderInfo) {
@@ -71,11 +75,6 @@ public class FolderIcon extends BubbleTextView implements DropTarget {
&& item.container != mInfo.id;
}
- public Rect estimateDropLocation(DragSource source, int x, int y, int xOffset, int yOffset,
- DragView dragView, Object dragInfo, Rect recycle) {
- return null;
- }
-
public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
DragView dragView, Object dragInfo) {
ShortcutInfo item;
@@ -91,7 +90,9 @@ public class FolderIcon extends BubbleTextView implements DropTarget {
public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
DragView dragView, Object dragInfo) {
- setCompoundDrawablesWithIntrinsicBounds(null, mOpenIcon, null, null);
+ if (acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) {
+ setCompoundDrawablesWithIntrinsicBounds(null, mOpenIcon, null, null);
+ }
}
public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
@@ -102,4 +103,10 @@ public class FolderIcon extends BubbleTextView implements DropTarget {
DragView dragView, Object dragInfo) {
setCompoundDrawablesWithIntrinsicBounds(null, mCloseIcon, null, null);
}
+
+ @Override
+ public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
+ DragView dragView, Object dragInfo) {
+ return null;
+ }
}
diff --git a/src/com/android/launcher2/HolographicOutlineHelper.java b/src/com/android/launcher2/HolographicOutlineHelper.java
new file mode 100644
index 000000000..9e61b79d0
--- /dev/null
+++ b/src/com/android/launcher2/HolographicOutlineHelper.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import android.graphics.Bitmap;
+import android.graphics.BlurMaskFilter;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+
+public class HolographicOutlineHelper {
+ private final Paint mHolographicPaint = new Paint();
+ private final Paint mExpensiveBlurPaint = new Paint();
+ private final Paint mErasePaint = new Paint();
+
+ private static final BlurMaskFilter mThickOuterBlurMaskFilter = new BlurMaskFilter(6.0f,
+ BlurMaskFilter.Blur.OUTER);
+ private static final BlurMaskFilter mThinOuterBlurMaskFilter = new BlurMaskFilter(1.0f,
+ BlurMaskFilter.Blur.OUTER);
+ private static final BlurMaskFilter mThickInnerBlurMaskFilter = new BlurMaskFilter(4.0f,
+ BlurMaskFilter.Blur.NORMAL);
+
+ HolographicOutlineHelper() {
+ mHolographicPaint.setFilterBitmap(true);
+ mHolographicPaint.setAntiAlias(true);
+ mExpensiveBlurPaint.setFilterBitmap(true);
+ mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
+ mErasePaint.setFilterBitmap(true);
+ mErasePaint.setAntiAlias(true);
+ }
+
+ /**
+ * Returns the interpolated holographic highlight alpha for the effect we want when scrolling
+ * pages.
+ */
+ public float highlightAlphaInterpolator(float r) {
+ float maxAlpha = 0.8f;
+ return (float) Math.pow(maxAlpha * (1.0f - r), 1.5f);
+ }
+
+ /**
+ * Returns the interpolated view alpha for the effect we want when scrolling pages.
+ */
+ public float viewAlphaInterpolator(float r) {
+ final float pivot = 0.95f;
+ if (r < pivot) {
+ return (float) Math.pow(r / pivot, 1.5f);
+ } else {
+ return 1.0f;
+ }
+ }
+
+ /**
+ * Applies a more expensive and accurate outline to whatever is currently drawn in a specified
+ * bitmap.
+ */
+ void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
+ int outlineColor) {
+ // calculate the outer blur first
+ mExpensiveBlurPaint.setMaskFilter(mThickOuterBlurMaskFilter);
+ int[] outerBlurOffset = new int[2];
+ Bitmap thickOuterBlur = srcDst.extractAlpha(mExpensiveBlurPaint, outerBlurOffset);
+ mExpensiveBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter);
+ int[] thinOuterBlurOffset = new int[2];
+ Bitmap thinOuterBlur = srcDst.extractAlpha(mExpensiveBlurPaint, thinOuterBlurOffset);
+
+ // calculate the inner blur
+ srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
+ mExpensiveBlurPaint.setMaskFilter(mThickInnerBlurMaskFilter);
+ int[] thickInnerBlurOffset = new int[2];
+ Bitmap thickInnerBlur = srcDst.extractAlpha(mExpensiveBlurPaint, thickInnerBlurOffset);
+
+ // mask out the inner blur
+ srcDstCanvas.setBitmap(thickInnerBlur);
+ srcDstCanvas.drawBitmap(srcDst, -thickInnerBlurOffset[0],
+ -thickInnerBlurOffset[1], mErasePaint);
+ srcDstCanvas.drawRect(0, 0, -thickInnerBlurOffset[0], thickInnerBlur.getHeight(),
+ mErasePaint);
+ srcDstCanvas.drawRect(0, 0, thickInnerBlur.getWidth(), -thickInnerBlurOffset[1],
+ mErasePaint);
+
+ // draw the inner and outer blur
+ srcDstCanvas.setBitmap(srcDst);
+ srcDstCanvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
+ mHolographicPaint.setColor(color);
+ srcDstCanvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1],
+ mHolographicPaint);
+ srcDstCanvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1],
+ mHolographicPaint);
+
+ // draw the bright outline
+ mHolographicPaint.setColor(outlineColor);
+ srcDstCanvas.drawBitmap(thinOuterBlur, thinOuterBlurOffset[0], thinOuterBlurOffset[1],
+ mHolographicPaint);
+
+ // cleanup
+ thinOuterBlur.recycle();
+ thickOuterBlur.recycle();
+ thickInnerBlur.recycle();
+ }
+} \ No newline at end of file
diff --git a/src/com/android/launcher2/InstallShortcutReceiver.java b/src/com/android/launcher2/InstallShortcutReceiver.java
index 3fc568b9f..caeb12b4a 100644
--- a/src/com/android/launcher2/InstallShortcutReceiver.java
+++ b/src/com/android/launcher2/InstallShortcutReceiver.java
@@ -16,11 +16,11 @@
package com.android.launcher2;
+import java.util.ArrayList;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.ContentResolver;
-import android.database.Cursor;
import android.widget.Toast;
import com.android.launcher.R;
@@ -50,11 +50,6 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
if (findEmptyCell(context, mCoordinates, screen)) {
- CellLayout.CellInfo cell = new CellLayout.CellInfo();
- cell.cellX = mCoordinates[0];
- cell.cellY = mCoordinates[1];
- cell.screen = screen;
-
Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
if (intent.getAction() == null) {
@@ -66,7 +61,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
boolean duplicate = data.getBooleanExtra(Launcher.EXTRA_SHORTCUT_DUPLICATE, true);
if (duplicate || !LauncherModel.shortcutExists(context, name, intent)) {
((LauncherApplication)context.getApplicationContext()).getModel()
- .addShortcut(context, data, cell, true);
+ .addShortcut(context, data, screen, mCoordinates[0], mCoordinates[1], true);
Toast.makeText(context, context.getString(R.string.shortcut_installed, name),
Toast.LENGTH_SHORT).show();
} else {
@@ -84,40 +79,26 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
}
private static boolean findEmptyCell(Context context, int[] xy, int screen) {
- final int xCount = Launcher.NUMBER_CELLS_X;
- final int yCount = Launcher.NUMBER_CELLS_Y;
-
+ final int xCount = LauncherModel.getCellCountX();
+ final int yCount = LauncherModel.getCellCountY();
boolean[][] occupied = new boolean[xCount][yCount];
- final ContentResolver cr = context.getContentResolver();
- Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
- new String[] { LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
- LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY },
- LauncherSettings.Favorites.SCREEN + "=?",
- new String[] { String.valueOf(screen) }, null);
-
- final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
- final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
- final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
- final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
-
- try {
- while (c.moveToNext()) {
- int cellX = c.getInt(cellXIndex);
- int cellY = c.getInt(cellYIndex);
- int spanX = c.getInt(spanXIndex);
- int spanY = c.getInt(spanYIndex);
-
+ ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context);
+ ItemInfo item = null;
+ int cellX, cellY, spanX, spanY;
+ for (int i = 0; i < items.size(); ++i) {
+ item = items.get(i);
+ if (item.screen == screen) {
+ cellX = item.cellX;
+ cellY = item.cellY;
+ spanX = item.spanX;
+ spanY = item.spanY;
for (int x = cellX; x < cellX + spanX && x < xCount; x++) {
for (int y = cellY; y < cellY + spanY && y < yCount; y++) {
occupied[x][y] = true;
}
}
}
- } catch (Exception e) {
- return false;
- } finally {
- c.close();
}
return CellLayout.findVacantCell(xy, 1, 1, xCount, yCount, occupied);
diff --git a/src/com/android/launcher2/ItemInfo.java b/src/com/android/launcher2/ItemInfo.java
index a96d9aeb2..dc4575062 100644
--- a/src/com/android/launcher2/ItemInfo.java
+++ b/src/com/android/launcher2/ItemInfo.java
@@ -112,6 +112,11 @@ class ItemInfo {
}
}
+ void updateValuesWithCoordinates(ContentValues values, int cellX, int cellY) {
+ values.put(LauncherSettings.Favorites.CELLX, cellX);
+ values.put(LauncherSettings.Favorites.CELLY, cellY);
+ }
+
static byte[] flattenBitmap(Bitmap bitmap) {
// Try go guesstimate how much space the icon will take when serialized
// to avoid unnecessary allocations/copies during the write.
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 8c64af7de..da1393c5d 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -1,3 +1,4 @@
+
/*
* Copyright (C) 2008 The Android Open Source Project
*
@@ -16,14 +17,23 @@
package com.android.launcher2;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import com.android.common.Search;
+import com.android.launcher.R;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.AnimatorSet;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.SearchManager;
import android.app.StatusBarManager;
import android.app.WallpaperManager;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -35,16 +45,18 @@ import android.content.Intent.ShortcutIconResource;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.Bitmap;
-import android.graphics.Rect;
import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
+import android.graphics.Color;
+import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -53,6 +65,7 @@ import android.os.Parcelable;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.LiveFolders;
+import android.provider.Settings;
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
@@ -64,34 +77,40 @@ import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
-import android.widget.TextView;
-import android.widget.Toast;
import android.widget.ImageView;
-import android.widget.PopupWindow;
import android.widget.LinearLayout;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
+import android.widget.PopupWindow;
+import android.widget.RelativeLayout;
+import android.widget.TabHost;
+import android.widget.TabHost.OnTabChangeListener;
+import android.widget.TabHost.TabContentFactory;
+import android.widget.TabWidget;
+import android.widget.TextView;
+import android.widget.Toast;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.HashMap;
+import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.io.DataInputStream;
-
-import com.android.launcher.R;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
/**
* Default launcher application.
*/
public final class Launcher extends Activity
- implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher {
+ implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
+ AllAppsView.Watcher, View.OnTouchListener {
static final String TAG = "Launcher";
static final boolean LOGD = false;
@@ -99,8 +118,6 @@ public final class Launcher extends Activity
static final boolean DEBUG_WIDGETS = false;
static final boolean DEBUG_USER_INTERFACE = false;
- private static final int WALLPAPER_SCREENS_SPAN = 2;
-
private static final int MENU_GROUP_ADD = 1;
private static final int MENU_GROUP_WALLPAPER = MENU_GROUP_ADD + 1;
@@ -124,8 +141,6 @@ public final class Launcher extends Activity
static final int SCREEN_COUNT = 5;
static final int DEFAULT_SCREEN = 2;
- static final int NUMBER_CELLS_X = 4;
- static final int NUMBER_CELLS_Y = 4;
static final int DIALOG_CREATE_SHORTCUT = 1;
static final int DIALOG_RENAME_FOLDER = 2;
@@ -144,21 +159,23 @@ public final class Launcher extends Activity
private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cellX";
// Type: int
private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cellY";
- // Type: int
- private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_spanX";
- // Type: int
- private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_spanY";
- // Type: int
- private static final String RUNTIME_STATE_PENDING_ADD_COUNT_X = "launcher.add_countX";
- // Type: int
- private static final String RUNTIME_STATE_PENDING_ADD_COUNT_Y = "launcher.add_countY";
- // Type: int[]
- private static final String RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS = "launcher.add_occupied_cells";
// Type: boolean
private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder";
// Type: long
private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
+ // tags for the customization tabs
+ private static final String WIDGETS_TAG = "widgets";
+ private static final String APPLICATIONS_TAG = "applications";
+ private static final String FOLDERS_TAG = "folders";
+ private static final String SHORTCUTS_TAG = "shortcuts";
+ private static final String WALLPAPERS_TAG = "wallpapers";
+
+ private static final String TOOLBAR_ICON_METADATA_NAME = "com.android.launcher.toolbar_icon";
+
+ /** The different states that Launcher can be in. */
+ private enum State { WORKSPACE, ALL_APPS, CUSTOMIZE, OVERVIEW };
+
static final int APPWIDGET_HOST_ID = 1024;
private static final Object sLock = new Object();
@@ -176,14 +193,21 @@ public final class Launcher extends Activity
private AppWidgetManager mAppWidgetManager;
private LauncherAppWidgetHost mAppWidgetHost;
- private CellLayout.CellInfo mAddItemCellInfo;
- private CellLayout.CellInfo mMenuAddInfo;
- private final int[] mCellCoordinates = new int[2];
+ private int mAddScreen = -1;
+ private int mAddIntersectCellX = -1;
+ private int mAddIntersectCellY = -1;
+ private int[] mAddDropPosition;
+ private int[] mTmpAddItemCellCoordinates = new int[2];
+
private FolderInfo mFolderInfo;
private DeleteZone mDeleteZone;
private HandleView mHandleView;
private AllAppsView mAllAppsGrid;
+ private TabHost mHomeCustomizationDrawer;
+
+ private PagedView mAllAppsPagedView = null;
+ private CustomizePagedView mCustomizePagedView = null;
private Bundle mSavedState;
@@ -209,16 +233,23 @@ public final class Launcher extends Activity
private ImageView mNextView;
// Hotseats (quick-launch icons next to AllApps)
- private static final int NUM_HOTSEATS = 2;
private String[] mHotseatConfig = null;
private Intent[] mHotseats = null;
private Drawable[] mHotseatIcons = null;
private CharSequence[] mHotseatLabels = null;
+ private Intent mAppMarketIntent = null;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (LauncherApplication.isInPlaceRotationEnabled()) {
+ // hide the status bar (temporary until we get the status bar design figured out)
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
+ }
+
LauncherApplication app = ((LauncherApplication)getApplication());
mModel = app.setLauncher(this);
mIconCache = app.getIconCache();
@@ -236,8 +267,87 @@ public final class Launcher extends Activity
loadHotseats();
checkForLocaleChange();
setWallpaperDimension();
-
setContentView(R.layout.launcher);
+ mHomeCustomizationDrawer = (TabHost) findViewById(com.android.internal.R.id.tabhost);
+ if (mHomeCustomizationDrawer != null) {
+ mHomeCustomizationDrawer.setup();
+
+ // share the same customization workspace across all the tabs
+ mCustomizePagedView = (CustomizePagedView) mInflater.inflate(
+ R.layout.customization_drawer, mHomeCustomizationDrawer, false);
+ TabContentFactory contentFactory = new TabContentFactory() {
+ public View createTabContent(String tag) {
+ return mCustomizePagedView;
+ }
+ };
+
+ String widgetsLabel = getString(R.string.widgets_tab_label);
+ mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(WIDGETS_TAG)
+ .setIndicator(widgetsLabel).setContent(contentFactory));
+ String applicationsLabel = getString(R.string.applications_tab_label);
+ mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(APPLICATIONS_TAG)
+ .setIndicator(applicationsLabel).setContent(contentFactory));
+ String foldersLabel = getString(R.string.folders_tab_label);
+ mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(FOLDERS_TAG)
+ .setIndicator(foldersLabel).setContent(contentFactory));
+ String wallpapersLabel = getString(R.string.wallpapers_tab_label);
+ mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(WALLPAPERS_TAG)
+ .setIndicator(wallpapersLabel).setContent(contentFactory));
+ String shortcutsLabel = getString(R.string.shortcuts_tab_label);
+ mHomeCustomizationDrawer.addTab(mHomeCustomizationDrawer.newTabSpec(SHORTCUTS_TAG)
+ .setIndicator(shortcutsLabel).setContent(contentFactory));
+
+ // TEMP: just styling the tab widget to be a bit nicer until we get the actual
+ // new assets
+ TabWidget tabWidget = mHomeCustomizationDrawer.getTabWidget();
+ for (int i = 0; i < tabWidget.getChildCount(); ++i) {
+ RelativeLayout tab = (RelativeLayout) tabWidget.getChildTabViewAt(i);
+ TextView text = (TextView) tab.getChildAt(1);
+ text.setTextSize(20.0f);
+ text.setPadding(20, 0, 20, 0);
+ text.setShadowLayer(1.0f, 0.0f, 1.0f, Color.BLACK);
+ tab.setBackgroundDrawable(null);
+ }
+
+ mHomeCustomizationDrawer.setOnTabChangedListener(new OnTabChangeListener() {
+ public void onTabChanged(String tabId) {
+ // animate the changing of the tab content by fading pages in and out
+ final int duration = 150;
+ final float alpha = mCustomizePagedView.getAlpha();
+ ValueAnimator alphaAnim = new ObjectAnimator(duration, mCustomizePagedView,
+ "alpha", alpha, 0.0f);
+ alphaAnim.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ String tag = mHomeCustomizationDrawer.getCurrentTabTag();
+ if (tag == WIDGETS_TAG) {
+ mCustomizePagedView.setCustomizationFilter(
+ CustomizePagedView.CustomizationType.WidgetCustomization);
+ } else if (tag == APPLICATIONS_TAG) {
+ mCustomizePagedView.setCustomizationFilter(
+ CustomizePagedView.CustomizationType.ApplicationCustomization);
+ } else if (tag == FOLDERS_TAG) {
+ mCustomizePagedView.setCustomizationFilter(
+ CustomizePagedView.CustomizationType.FolderCustomization);
+ } else if (tag == WALLPAPERS_TAG) {
+ mCustomizePagedView.setCustomizationFilter(
+ CustomizePagedView.CustomizationType.WallpaperCustomization);
+ } else if (tag == SHORTCUTS_TAG) {
+ mCustomizePagedView.setCustomizationFilter(
+ CustomizePagedView.CustomizationType.ShortcutCustomization);
+ }
+
+ final float alpha = mCustomizePagedView.getAlpha();
+ ValueAnimator alphaAnim = new ObjectAnimator(duration,
+ mCustomizePagedView, "alpha", alpha, 1.0f);
+ alphaAnim.start();
+ }
+ });
+ alphaAnim.start();
+ }
+ });
+
+ mHomeCustomizationDrawer.setCurrentTab(0);
+ }
setupViews();
registerContentObservers();
@@ -380,11 +490,12 @@ public final class Launcher extends Activity
WallpaperManager wpm = (WallpaperManager)getSystemService(WALLPAPER_SERVICE);
Display display = getWindowManager().getDefaultDisplay();
- boolean isPortrait = display.getWidth() < display.getHeight();
-
- final int width = isPortrait ? display.getWidth() : display.getHeight();
- final int height = isPortrait ? display.getHeight() : display.getWidth();
- wpm.suggestDesiredDimensions(width * WALLPAPER_SCREENS_SPAN, height);
+ // TODO: Put back when we decide about scrolling the wallpaper
+ // boolean isPortrait = display.getWidth() < display.getHeight();
+ // final int width = isPortrait ? display.getWidth() : display.getHeight();
+ // final int height = isPortrait ? display.getHeight() : display.getWidth();
+ wpm.suggestDesiredDimensions(Math.max(display.getWidth(), display.getHeight()),
+ Math.max(display.getWidth(), display.getHeight()));
}
// Note: This doesn't do all the client-id magic that BrowserProvider does
@@ -443,7 +554,7 @@ public final class Launcher extends Activity
// note: if the user launches this without a default set, she
// will always be taken to the default URL above; this is
// unavoidable as we must specify a valid URL in order for the
- // chooser to appear, and once the user selects something, that
+ // chooser to appear, and once the user selects something, that
// URL is unavoidably sent to the chosen app.
} else {
try {
@@ -453,7 +564,7 @@ public final class Launcher extends Activity
// bogus; leave intent=null
}
}
-
+
if (intent == null) {
mHotseats[i] = null;
mHotseatLabels[i] = getText(R.string.activity_not_found);
@@ -461,15 +572,15 @@ public final class Launcher extends Activity
}
if (LOGD) {
- Log.d(TAG, "loadHotseats: hotseat " + i
- + " initial intent=["
+ Log.d(TAG, "loadHotseats: hotseat " + i
+ + " initial intent=["
+ intent.toUri(Intent.URI_INTENT_SCHEME)
+ "]");
}
ResolveInfo bestMatch = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
List<ResolveInfo> allMatches = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
- if (LOGD) {
+ if (LOGD) {
Log.d(TAG, "Best match for intent: " + bestMatch);
Log.d(TAG, "All matches: ");
for (ResolveInfo ri : allMatches) {
@@ -478,8 +589,8 @@ public final class Launcher extends Activity
}
// did this resolve to a single app, or the resolver?
if (allMatches.size() == 0 || bestMatch == null) {
- // can't find any activity to handle this. let's leave the
- // intent as-is and let Launcher show a toast when it fails
+ // can't find any activity to handle this. let's leave the
+ // intent as-is and let Launcher show a toast when it fails
// to launch.
mHotseats[i] = intent;
@@ -495,7 +606,7 @@ public final class Launcher extends Activity
break;
}
}
-
+
if (!found) {
if (LOGD) Log.d(TAG, "Multiple options, no default yet");
// the bestMatch is probably the ResolveActivity, meaning the
@@ -520,8 +631,8 @@ public final class Launcher extends Activity
}
if (LOGD) {
- Log.d(TAG, "loadHotseats: hotseat " + i
- + " final intent=["
+ Log.d(TAG, "loadHotseats: hotseat " + i
+ + " final intent=["
+ ((mHotseats[i] == null)
? "null"
: mHotseats[i].toUri(Intent.URI_INTENT_SCHEME))
@@ -542,28 +653,30 @@ public final class Launcher extends Activity
// For example, the user would PICK_SHORTCUT for "Music playlist", and we
// launch over to the Music app to actually CREATE_SHORTCUT.
- if (resultCode == RESULT_OK && mAddItemCellInfo != null) {
+ if (resultCode == RESULT_OK && mAddScreen != -1) {
switch (requestCode) {
case REQUEST_PICK_APPLICATION:
- completeAddApplication(this, data, mAddItemCellInfo);
+ completeAddApplication(
+ this, data, mAddScreen, mAddIntersectCellX, mAddIntersectCellY);
break;
case REQUEST_PICK_SHORTCUT:
processShortcut(data);
break;
case REQUEST_CREATE_SHORTCUT:
- completeAddShortcut(data, mAddItemCellInfo);
+ completeAddShortcut(data, mAddScreen, mAddIntersectCellX, mAddIntersectCellY);
break;
case REQUEST_PICK_LIVE_FOLDER:
addLiveFolder(data);
break;
case REQUEST_CREATE_LIVE_FOLDER:
- completeAddLiveFolder(data, mAddItemCellInfo);
+ completeAddLiveFolder(data, mAddScreen, mAddIntersectCellX, mAddIntersectCellY);
break;
case REQUEST_PICK_APPWIDGET:
- addAppWidget(data);
+ addAppWidgetFromPick(data);
break;
case REQUEST_CREATE_APPWIDGET:
- completeAddAppWidget(data, mAddItemCellInfo);
+ int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
+ completeAddAppWidget(appWidgetId, mAddScreen);
break;
case REQUEST_PICK_WALLPAPER:
// We just wanted the activity result here so we can clear mWaitingForResult
@@ -591,13 +704,21 @@ public final class Launcher extends Activity
mModel.startLoader(this, true);
mRestoring = false;
}
+ // When we resume Launcher, a different Activity might be responsible for the app
+ // market intent, so refresh the icon
+ updateAppMarketIcon();
}
@Override
protected void onPause() {
super.onPause();
- dismissPreview(mPreviousView);
- dismissPreview(mNextView);
+ // Some launcher layouts don't have a previous and next view
+ if (mPreviousView != null) {
+ dismissPreview(mPreviousView);
+ }
+ if (mNextView != null) {
+ dismissPreview(mNextView);
+ }
mDragController.cancelDrag();
}
@@ -688,23 +809,15 @@ public final class Launcher extends Activity
final int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1);
if (currentScreen > -1) {
- mWorkspace.setCurrentScreen(currentScreen);
+ mWorkspace.setCurrentPage(currentScreen);
}
final int addScreen = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
+
if (addScreen > -1) {
- mAddItemCellInfo = new CellLayout.CellInfo();
- final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo;
- addItemCellInfo.valid = true;
- addItemCellInfo.screen = addScreen;
- addItemCellInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
- addItemCellInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
- addItemCellInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
- addItemCellInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
- addItemCellInfo.findVacantCellsFromOccupied(
- savedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS),
- savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_X),
- savedState.getInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y));
+ mAddScreen = addScreen;
+ mAddIntersectCellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
+ mAddIntersectCellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
mRestoring = true;
}
@@ -720,7 +833,7 @@ public final class Launcher extends Activity
* Finds all the views we need and configure them properly.
*/
private void setupViews() {
- DragController dragController = mDragController;
+ final DragController dragController = mDragController;
DragLayer dragLayer = (DragLayer) findViewById(R.id.drag_layer);
dragLayer.setDragController(dragController);
@@ -729,8 +842,15 @@ public final class Launcher extends Activity
mAllAppsGrid.setLauncher(this);
mAllAppsGrid.setDragController(dragController);
((View) mAllAppsGrid).setWillNotDraw(false); // We don't want a hole punched in our window.
- // Manage focusability manually since this thing is always visible
- ((View) mAllAppsGrid).setFocusable(false);
+ // Manage focusability manually since this thing is always visible (in non-xlarge)
+ ((View) mAllAppsGrid).setFocusable(false);
+
+ if (LauncherApplication.isScreenXLarge()) {
+ // They need to be INVISIBLE initially so that they will be measured in the layout.
+ // Otherwise the animations are messed up when we show them for the first time.
+ ((View) mAllAppsGrid).setVisibility(View.INVISIBLE);
+ mHomeCustomizationDrawer.setVisibility(View.INVISIBLE);
+ }
mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace);
final Workspace workspace = mWorkspace;
@@ -739,29 +859,39 @@ public final class Launcher extends Activity
DeleteZone deleteZone = (DeleteZone) dragLayer.findViewById(R.id.delete_zone);
mDeleteZone = deleteZone;
- mHandleView = (HandleView) findViewById(R.id.all_apps_button);
- mHandleView.setLauncher(this);
- mHandleView.setOnClickListener(this);
- mHandleView.setOnLongClickListener(this);
+ View handleView = findViewById(R.id.all_apps_button);
+ if (handleView != null && handleView instanceof HandleView) {
+ // we don't use handle view in xlarge mode
+ mHandleView = (HandleView)handleView;
+ mHandleView.setLauncher(this);
+ mHandleView.setOnClickListener(this);
+ mHandleView.setOnLongClickListener(this);
+ }
- ImageView hotseatLeft = (ImageView) findViewById(R.id.hotseat_left);
- hotseatLeft.setContentDescription(mHotseatLabels[0]);
- hotseatLeft.setImageDrawable(mHotseatIcons[0]);
- ImageView hotseatRight = (ImageView) findViewById(R.id.hotseat_right);
- hotseatRight.setContentDescription(mHotseatLabels[1]);
- hotseatRight.setImageDrawable(mHotseatIcons[1]);
+ if (mCustomizePagedView != null) {
+ mCustomizePagedView.setLauncher(this);
+ mCustomizePagedView.setDragController(dragController);
+ mCustomizePagedView.update();
+ } else {
+ ImageView hotseatLeft = (ImageView) findViewById(R.id.hotseat_left);
+ hotseatLeft.setContentDescription(mHotseatLabels[0]);
+ hotseatLeft.setImageDrawable(mHotseatIcons[0]);
+ ImageView hotseatRight = (ImageView) findViewById(R.id.hotseat_right);
+ hotseatRight.setContentDescription(mHotseatLabels[1]);
+ hotseatRight.setImageDrawable(mHotseatIcons[1]);
- mPreviousView = (ImageView) dragLayer.findViewById(R.id.previous_screen);
- mNextView = (ImageView) dragLayer.findViewById(R.id.next_screen);
+ mPreviousView = (ImageView) dragLayer.findViewById(R.id.previous_screen);
+ mNextView = (ImageView) dragLayer.findViewById(R.id.next_screen);
- Drawable previous = mPreviousView.getDrawable();
- Drawable next = mNextView.getDrawable();
- mWorkspace.setIndicators(previous, next);
+ Drawable previous = mPreviousView.getDrawable();
+ Drawable next = mNextView.getDrawable();
+ mWorkspace.setIndicators(previous, next);
- mPreviousView.setHapticFeedbackEnabled(false);
- mPreviousView.setOnLongClickListener(this);
- mNextView.setHapticFeedbackEnabled(false);
- mNextView.setOnLongClickListener(this);
+ mPreviousView.setHapticFeedbackEnabled(false);
+ mPreviousView.setOnLongClickListener(this);
+ mNextView.setHapticFeedbackEnabled(false);
+ mNextView.setOnLongClickListener(this);
+ }
workspace.setOnLongClickListener(this);
workspace.setDragController(dragController);
@@ -769,16 +899,33 @@ public final class Launcher extends Activity
deleteZone.setLauncher(this);
deleteZone.setDragController(dragController);
- deleteZone.setHandle(findViewById(R.id.all_apps_button_cluster));
+ int deleteZoneHandleId;
+ if (LauncherApplication.isScreenXLarge()) {
+ deleteZoneHandleId = R.id.all_apps_button;
+ } else {
+ deleteZoneHandleId = R.id.all_apps_button_cluster;
+ }
+ deleteZone.setHandle(findViewById(deleteZoneHandleId));
+ dragController.addDragListener(deleteZone);
+
+ ApplicationInfoDropTarget infoButton = (ApplicationInfoDropTarget)findViewById(R.id.info_button);
+ if (infoButton != null) {
+ infoButton.setLauncher(this);
+ infoButton.setHandle(findViewById(R.id.configure_button));
+ infoButton.setDragColor(getResources().getColor(R.color.app_info_filter));
+ dragController.addDragListener(infoButton);
+ }
dragController.setDragScoller(workspace);
- dragController.setDragListener(deleteZone);
dragController.setScrollView(dragLayer);
dragController.setMoveTarget(workspace);
// The order here is bottom to top.
dragController.addDropTarget(workspace);
dragController.addDropTarget(deleteZone);
+ if (infoButton != null) {
+ dragController.addDropTarget(infoButton);
+ }
}
@SuppressWarnings({"UnusedDeclaration"})
@@ -816,7 +963,7 @@ public final class Launcher extends Activity
);
}
}
-
+
/**
* Creates a view representing a shortcut.
*
@@ -826,7 +973,7 @@ public final class Launcher extends Activity
*/
View createShortcut(ShortcutInfo info) {
return createShortcut(R.layout.application,
- (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
+ (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
}
/**
@@ -857,9 +1004,15 @@ public final class Launcher extends Activity
* @param data The intent describing the application.
* @param cellInfo The position on screen where to create the shortcut.
*/
- void completeAddApplication(Context context, Intent data, CellLayout.CellInfo cellInfo) {
- cellInfo.screen = mWorkspace.getCurrentScreen();
- if (!findSingleSlot(cellInfo)) return;
+ void completeAddApplication(Context context, Intent data, int screen,
+ int intersectCellX, int intersectCellY) {
+ final int[] cellXY = mTmpAddItemCellCoordinates;
+ final CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen);
+
+ if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) {
+ showOutOfSpaceMessage();
+ return;
+ }
final ShortcutInfo info = mModel.getShortcutInfo(context.getPackageManager(),
data, context);
@@ -868,7 +1021,8 @@ public final class Launcher extends Activity
info.setActivity(data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
info.container = ItemInfo.NO_ID;
- mWorkspace.addApplicationShortcut(info, cellInfo, isWorkspaceLocked());
+ mWorkspace.addApplicationShortcut(info, screen, cellXY[0], cellXY[1],
+ isWorkspaceLocked(), mAddIntersectCellX, mAddIntersectCellY);
} else {
Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data);
}
@@ -880,16 +1034,22 @@ public final class Launcher extends Activity
* @param data The intent describing the shortcut.
* @param cellInfo The position on screen where to create the shortcut.
*/
- private void completeAddShortcut(Intent data, CellLayout.CellInfo cellInfo) {
- cellInfo.screen = mWorkspace.getCurrentScreen();
- if (!findSingleSlot(cellInfo)) return;
+ private void completeAddShortcut(Intent data, int screen,
+ int intersectCellX, int intersectCellY) {
+ final int[] cellXY = mTmpAddItemCellCoordinates;
+ final CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen);
- final ShortcutInfo info = mModel.addShortcut(this, data, cellInfo, false);
+ if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) {
+ showOutOfSpaceMessage();
+ return;
+ }
+
+ final ShortcutInfo info = mModel.addShortcut(
+ this, data, screen, cellXY[0], cellXY[1], false);
if (!mRestoring) {
final View view = createShortcut(info);
- mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1,
- isWorkspaceLocked());
+ mWorkspace.addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, isWorkspaceLocked());
}
}
@@ -897,36 +1057,61 @@ public final class Launcher extends Activity
/**
* Add a widget to the workspace.
*
- * @param data The intent describing the appWidgetId.
+ * @param appWidgetId The app widget id
* @param cellInfo The position on screen where to create the widget.
*/
- private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo) {
- Bundle extras = data.getExtras();
- int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
-
- if (LOGD) Log.d(TAG, "dumping extras content=" + extras.toString());
-
+ private void completeAddAppWidget(int appWidgetId, int screen) {
AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
// Calculate the grid spans needed to fit this widget
- CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen);
- int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight);
+ CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen);
+ int[] spanXY = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight, null);
// Try finding open space on Launcher screen
- final int[] xy = mCellCoordinates;
- if (!findSlot(cellInfo, xy, spans[0], spans[1])) {
+ // We have saved the position to which the widget was dragged-- this really only matters
+ // if we are placing widgets on a "spring-loaded" screen
+ final int[] cellXY = mTmpAddItemCellCoordinates;
+
+ // For now, we don't save the coordinate where we dropped the icon because we're not
+ // supporting spring-loaded mini-screens; however, leaving the ability to directly place
+ // a widget on the home screen in case we want to add it in the future
+ final int[] touchXY = null;
+ //final int[] touchXY = mAddDropPosition;
+ boolean findNearestVacantAreaFailed = false;
+ boolean foundCellSpan = false;
+ if (touchXY != null) {
+ // when dragging and dropping, just find the closest free spot
+ CellLayout screenLayout = (CellLayout) mWorkspace.getChildAt(screen);
+ int[] result = screenLayout.findNearestVacantArea(
+ touchXY[0], touchXY[1], spanXY[0], spanXY[1], cellXY);
+ findNearestVacantAreaFailed = (result == null);
+ foundCellSpan = !findNearestVacantAreaFailed;
+ } else {
+ if (mAddIntersectCellX != -1 && mAddIntersectCellY != -1) {
+ // if we long pressed on an empty cell to bring up a menu,
+ // make sure we intersect the empty cell
+ foundCellSpan = layout.findCellForSpanThatIntersects(cellXY, spanXY[0], spanXY[1],
+ mAddIntersectCellX, mAddIntersectCellY);
+ } else {
+ // if we went through the menu -> add, just find any spot
+ foundCellSpan = layout.findCellForSpan(cellXY, spanXY[0], spanXY[1]);
+ }
+ }
+
+ if (!foundCellSpan) {
if (appWidgetId != -1) mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+ showOutOfSpaceMessage();
return;
}
// Build Launcher-specific widget info and save to database
LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId);
- launcherInfo.spanX = spans[0];
- launcherInfo.spanY = spans[1];
+ launcherInfo.spanX = spanXY[0];
+ launcherInfo.spanY = spanXY[1];
LauncherModel.addItemToDatabase(this, launcherInfo,
LauncherSettings.Favorites.CONTAINER_DESKTOP,
- mWorkspace.getCurrentScreen(), xy[0], xy[1], false);
+ screen, cellXY[0], cellXY[1], false);
if (!mRestoring) {
mDesktopItems.add(launcherInfo);
@@ -937,11 +1122,15 @@ public final class Launcher extends Activity
launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
launcherInfo.hostView.setTag(launcherInfo);
- mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1],
+ mWorkspace.addInScreen(launcherInfo.hostView, screen, cellXY[0], cellXY[1],
launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
}
}
+ void showOutOfSpaceMessage() {
+ Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
+ }
+
public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) {
mDesktopItems.remove(launcherInfo);
launcherInfo.hostView = null;
@@ -984,10 +1173,19 @@ public final class Launcher extends Activity
boolean alreadyOnHome = ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
!= Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
boolean allAppsVisible = isAllAppsVisible();
- if (!mWorkspace.isDefaultScreenShowing()) {
- mWorkspace.moveToDefaultScreen(alreadyOnHome && !allAppsVisible);
+ boolean customizationDrawerVisible = isCustomizationDrawerVisible();
+
+ // in all these cases, only animate if we're already on home
+ if (LauncherApplication.isScreenXLarge()) {
+ mWorkspace.unshrink(alreadyOnHome);
+ }
+ if (!mWorkspace.isDefaultPageShowing()) {
+ // on the phone, we don't animate the change to the workspace if all apps is visible
+ mWorkspace.moveToDefaultScreen(
+ alreadyOnHome && (LauncherApplication.isScreenXLarge() || !allAppsVisible));
}
closeAllApps(alreadyOnHome && allAppsVisible);
+ hideCustomizationDrawer(alreadyOnHome);
final View v = getWindow().peekDecorView();
if (v != null && v.getWindowToken() != null) {
@@ -1002,11 +1200,18 @@ public final class Launcher extends Activity
protected void onRestoreInstanceState(Bundle savedInstanceState) {
// Do not call super here
mSavedInstanceState = savedInstanceState;
+
+ if (mHomeCustomizationDrawer != null) {
+ String cur = savedInstanceState.getString("currentTab");
+ if (cur != null) {
+ mHomeCustomizationDrawer.setCurrentTabByTag(cur);
+ }
+ }
}
@Override
protected void onSaveInstanceState(Bundle outState) {
- outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentScreen());
+ outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentPage());
final ArrayList<Folder> folders = mWorkspace.getOpenFolders();
if (folders.size() > 0) {
@@ -1026,25 +1231,23 @@ public final class Launcher extends Activity
outState.putBoolean(RUNTIME_STATE_ALL_APPS_FOLDER, true);
}
- if (mAddItemCellInfo != null && mAddItemCellInfo.valid && mWaitingForResult) {
- final CellLayout.CellInfo addItemCellInfo = mAddItemCellInfo;
- final CellLayout layout = (CellLayout) mWorkspace.getChildAt(addItemCellInfo.screen);
-
- outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, addItemCellInfo.screen);
- outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, addItemCellInfo.cellX);
- outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, addItemCellInfo.cellY);
- outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, addItemCellInfo.spanX);
- outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, addItemCellInfo.spanY);
- outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_X, layout.getCountX());
- outState.putInt(RUNTIME_STATE_PENDING_ADD_COUNT_Y, layout.getCountY());
- outState.putBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS,
- layout.getOccupiedCells());
+ if (mAddScreen > -1 && mWaitingForResult) {
+ outState.putInt(RUNTIME_STATE_PENDING_ADD_SCREEN, mAddScreen);
+ outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, mAddIntersectCellX);
+ outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, mAddIntersectCellY);
}
if (mFolderInfo != null && mWaitingForResult) {
outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true);
outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
}
+
+ if (mHomeCustomizationDrawer != null) {
+ String currentTabTag = mHomeCustomizationDrawer.getCurrentTabTag();
+ if (currentTabTag != null) {
+ outState.putString("currentTab", currentTabTag);
+ }
+ }
}
@Override
@@ -1064,9 +1267,14 @@ public final class Launcher extends Activity
unbindDesktopItems();
getContentResolver().unregisterContentObserver(mWidgetObserver);
-
- dismissPreview(mPreviousView);
- dismissPreview(mNextView);
+
+ // Some launcher layouts don't have a previous and next view
+ if (mPreviousView != null) {
+ dismissPreview(mPreviousView);
+ }
+ if (mNextView != null) {
+ dismissPreview(mNextView);
+ }
unregisterReceiver(mCloseSystemDialogsReceiver);
}
@@ -1140,19 +1348,19 @@ public final class Launcher extends Activity
// If all apps is animating, don't show the menu, because we don't know
// which one to show.
- if (mAllAppsGrid.isVisible() && !mAllAppsGrid.isOpaque()) {
+ if (mAllAppsGrid.isAnimating()) {
return false;
}
// Only show the add and wallpaper options when we're not in all apps.
- boolean visible = !mAllAppsGrid.isOpaque();
+ boolean visible = !mAllAppsGrid.isVisible();
menu.setGroupVisible(MENU_GROUP_ADD, visible);
menu.setGroupVisible(MENU_GROUP_WALLPAPER, visible);
// Disable add if the workspace is full.
if (visible) {
- mMenuAddInfo = mWorkspace.findAllVacantCells(null);
- menu.setGroupEnabled(MENU_GROUP_ADD, mMenuAddInfo != null && mMenuAddInfo.valid);
+ CellLayout layout = (CellLayout) mWorkspace.getChildAt(mWorkspace.getCurrentPage());
+ menu.setGroupEnabled(MENU_GROUP_ADD, layout.existsEmptyCell());
}
return true;
@@ -1197,17 +1405,49 @@ public final class Launcher extends Activity
}
private void addItems() {
- closeAllApps(true);
- showAddDialog(mMenuAddInfo);
+ if (LauncherApplication.isScreenXLarge()) {
+ // Animate the widget chooser up from the bottom of the screen
+ if (!isCustomizationDrawerVisible()) {
+ showCustomizationDrawer(true);
+ }
+ } else {
+ closeAllApps(true);
+ showAddDialog(-1, -1);
+ }
+ }
+
+ private void resetAddInfo() {
+ mAddScreen = -1;
+ mAddIntersectCellX = -1;
+ mAddIntersectCellY = -1;
+ mAddDropPosition = null;
+ }
+
+ void addAppWidgetFromDrop(ComponentName appWidgetProvider, int screen, int[] position) {
+ resetAddInfo();
+ mAddScreen = screen;
+
+ // only set mAddDropPosition if we dropped on home screen in "spring-loaded" manner
+ mAddDropPosition = position;
+
+ int appWidgetId = getAppWidgetHost().allocateAppWidgetId();
+ AppWidgetManager.getInstance(this).bindAppWidgetId(appWidgetId, appWidgetProvider);
+ addAppWidgetImpl(appWidgetId);
}
private void manageApps() {
startActivity(new Intent(android.provider.Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS));
}
- void addAppWidget(Intent data) {
+ void addAppWidgetFromPick(Intent data) {
// TODO: catch bad widget exception when sent
int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
+ // TODO: Is this log message meaningful?
+ if (LOGD) Log.d(TAG, "dumping extras content=" + data.getExtras());
+ addAppWidgetImpl(appWidgetId);
+ }
+
+ void addAppWidgetImpl(int appWidgetId) {
AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
if (appWidget.configure != null) {
@@ -1219,10 +1459,20 @@ public final class Launcher extends Activity
startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET);
} else {
// Otherwise just add it
- onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data);
+ completeAddAppWidget(appWidgetId, mAddScreen);
}
}
+ void processShortcutFromDrop(ComponentName componentName, int screen, int[] position) {
+ resetAddInfo();
+ mAddScreen = screen;
+ mAddDropPosition = position;
+
+ Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
+ createShortcutIntent.setComponent(componentName);
+ processShortcut(createShortcutIntent);
+ }
+
void processShortcut(Intent intent) {
// Handle case where user selected "Applications"
String applicationName = getResources().getString(R.string.group_applications);
@@ -1240,59 +1490,80 @@ public final class Launcher extends Activity
}
}
- void addLiveFolder(Intent intent) {
+ void processWallpaper(Intent intent) {
+ startActivityForResult(intent, REQUEST_PICK_WALLPAPER);
+ }
+
+ void addLiveFolderFromDrop(ComponentName componentName, int screen, int[] position) {
+ resetAddInfo();
+ mAddScreen = screen;
+ mAddDropPosition = position;
+
+ Intent createFolderIntent = new Intent(LiveFolders.ACTION_CREATE_LIVE_FOLDER);
+ createFolderIntent.setComponent(componentName);
+
+ addLiveFolder(createFolderIntent);
+ }
+
+ void addLiveFolder(Intent intent) { // YYY add screen intersect etc. parameters here
// Handle case where user selected "Folder"
String folderName = getResources().getString(R.string.group_folder);
String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
if (folderName != null && folderName.equals(shortcutName)) {
- addFolder();
+ addFolder(mAddScreen, mAddIntersectCellX, mAddIntersectCellY);
} else {
startActivityForResultSafely(intent, REQUEST_CREATE_LIVE_FOLDER);
}
}
- void addFolder() {
+ void addFolder(int screen, int intersectCellX, int intersectCellY) {
UserFolderInfo folderInfo = new UserFolderInfo();
folderInfo.title = getText(R.string.folder_name);
- CellLayout.CellInfo cellInfo = mAddItemCellInfo;
- cellInfo.screen = mWorkspace.getCurrentScreen();
- if (!findSingleSlot(cellInfo)) return;
+ final CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen);
+ final int[] cellXY = mTmpAddItemCellCoordinates;
+ if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) {
+ showOutOfSpaceMessage();
+ return;
+ }
// Update the model
LauncherModel.addItemToDatabase(this, folderInfo,
LauncherSettings.Favorites.CONTAINER_DESKTOP,
- mWorkspace.getCurrentScreen(), cellInfo.cellX, cellInfo.cellY, false);
+ screen, cellXY[0], cellXY[1], false);
sFolders.put(folderInfo.id, folderInfo);
// Create the view
FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
- (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), folderInfo);
- mWorkspace.addInCurrentScreen(newFolder,
- cellInfo.cellX, cellInfo.cellY, 1, 1, isWorkspaceLocked());
+ (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), folderInfo);
+ mWorkspace.addInScreen(newFolder, screen, cellXY[0], cellXY[1], 1, 1, isWorkspaceLocked());
}
void removeFolder(FolderInfo folder) {
sFolders.remove(folder.id);
}
- private void completeAddLiveFolder(Intent data, CellLayout.CellInfo cellInfo) {
- cellInfo.screen = mWorkspace.getCurrentScreen();
- if (!findSingleSlot(cellInfo)) return;
+ private void completeAddLiveFolder(
+ Intent data, int screen, int intersectCellX, int intersectCellY) {
+ final CellLayout layout = (CellLayout) mWorkspace.getChildAt(screen);
+ final int[] cellXY = mTmpAddItemCellCoordinates;
+ if (!layout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectCellX, intersectCellY)) {
+ showOutOfSpaceMessage();
+ return;
+ }
- final LiveFolderInfo info = addLiveFolder(this, data, cellInfo, false);
+ final LiveFolderInfo info = addLiveFolder(this, data, screen, cellXY[0], cellXY[1], false);
if (!mRestoring) {
final View view = LiveFolderIcon.fromXml(R.layout.live_folder_icon, this,
- (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentScreen()), info);
- mWorkspace.addInCurrentScreen(view, cellInfo.cellX, cellInfo.cellY, 1, 1,
- isWorkspaceLocked());
+ (ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
+ mWorkspace.addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, isWorkspaceLocked());
}
}
static LiveFolderInfo addLiveFolder(Context context, Intent data,
- CellLayout.CellInfo cellInfo, boolean notify) {
+ int screen, int cellX, int cellY, boolean notify) {
Intent baseIntent = data.getParcelableExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT);
String name = data.getStringExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME);
@@ -1328,35 +1599,12 @@ public final class Launcher extends Activity
LiveFolders.DISPLAY_MODE_GRID);
LauncherModel.addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
- cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
+ screen, cellX, cellY, notify);
sFolders.put(info.id, info);
return info;
}
- private boolean findSingleSlot(CellLayout.CellInfo cellInfo) {
- final int[] xy = new int[2];
- if (findSlot(cellInfo, xy, 1, 1)) {
- cellInfo.cellX = xy[0];
- cellInfo.cellY = xy[1];
- return true;
- }
- return false;
- }
-
- private boolean findSlot(CellLayout.CellInfo cellInfo, int[] xy, int spanX, int spanY) {
- if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
- boolean[] occupied = mSavedState != null ?
- mSavedState.getBooleanArray(RUNTIME_STATE_PENDING_ADD_OCCUPIED_CELLS) : null;
- cellInfo = mWorkspace.findAllVacantCells(occupied);
- if (!cellInfo.findCellForSpan(xy, spanX, spanY)) {
- Toast.makeText(this, getString(R.string.out_of_space), Toast.LENGTH_SHORT).show();
- return false;
- }
- }
- return true;
- }
-
private void showNotifications() {
final StatusBarManager statusBar = (StatusBarManager) getSystemService(STATUS_BAR_SERVICE);
if (statusBar != null) {
@@ -1420,11 +1668,16 @@ public final class Launcher extends Activity
public void onBackPressed() {
if (isAllAppsVisible()) {
closeAllApps(true);
+ } else if (isCustomizationDrawerVisible()) {
+ hideCustomizationDrawer(true);
} else {
closeFolder();
}
- dismissPreview(mPreviousView);
- dismissPreview(mNextView);
+ // Some launcher layouts don't have a previous and next view
+ if (mPreviousView != null) {
+ dismissPreview(mPreviousView);
+ dismissPreview(mNextView);
+ }
}
private void closeFolder() {
@@ -1490,6 +1743,74 @@ public final class Launcher extends Activity
}
}
+ public boolean onTouch(View v, MotionEvent event) {
+ // this is an intercepted event being forwarded from mWorkspace;
+ // clicking anywhere on the workspace causes the drawer to slide down
+ hideCustomizationDrawer(true);
+ return false;
+ }
+
+ /**
+ * Event handler for the search button
+ *
+ * @param v The view that was clicked.
+ */
+ public void onClickSearchButton(View v) {
+ Intent i = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
+ View button = findViewById(R.id.search_button);
+ i.setSourceBounds(
+ new Rect(button.getLeft(), button.getTop(), button.getRight(), button.getBottom()));
+ startActivity(i);
+ }
+
+ /**
+ * Event handler for the "gear" button that appears on the home screen, which
+ * enters home screen customization mode.
+ *
+ * @param v The view that was clicked.
+ */
+ public void onClickConfigureButton(View v) {
+ addItems();
+ }
+
+ /**
+ * Event handler for the "grid" button that appears on the home screen, which
+ * enters all apps mode.
+ *
+ * @param v The view that was clicked.
+ */
+ public void onClickAllAppsButton(View v) {
+ showAllApps(true);
+ }
+
+ public void onClickAppMarketButton(View v) {
+ if (mAppMarketIntent != null) {
+ startActivitySafely(mAppMarketIntent, "app market");
+ }
+ }
+
+ void startApplicationDetailsActivity(ComponentName componentName) {
+ String packageName = componentName.getPackageName();
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+ Uri.fromParts("package", packageName, null));
+ startActivity(intent);
+ }
+
+ void startApplicationUninstallActivity(ApplicationInfo appInfo) {
+ if ((appInfo.flags & ApplicationInfo.DOWNLOADED_FLAG) == 0) {
+ // System applications cannot be installed. For now, show a toast explaining that.
+ // We may give them the option of disabling apps this way.
+ int messageId = R.string.uninstall_system_app_text;
+ Toast.makeText(this, messageId, Toast.LENGTH_SHORT).show();
+ } else {
+ String packageName = appInfo.componentName.getPackageName();
+ String className = appInfo.componentName.getClassName();
+ Intent intent = new Intent(
+ Intent.ACTION_DELETE, Uri.fromParts("package", packageName, className));
+ startActivity(intent);
+ }
+ }
+
void startActivitySafely(Intent intent, Object tag) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
@@ -1505,7 +1826,7 @@ public final class Launcher extends Activity
+ "tag="+ tag + " intent=" + intent, e);
}
}
-
+
void startActivityForResultSafely(Intent intent, int requestCode) {
try {
startActivityForResult(intent, requestCode);
@@ -1530,10 +1851,10 @@ public final class Launcher extends Activity
Folder openFolder = mWorkspace.getFolderForTag(folderInfo);
int folderScreen;
if (openFolder != null) {
- folderScreen = mWorkspace.getScreenForView(openFolder);
+ folderScreen = mWorkspace.getPageForView(openFolder);
// .. and close it
closeFolder(openFolder);
- if (folderScreen != mWorkspace.getCurrentScreen()) {
+ if (folderScreen != mWorkspace.getCurrentPage()) {
// Close any folder open on the current screen
closeFolder();
// Pull the folder onto this screen
@@ -1550,7 +1871,7 @@ public final class Launcher extends Activity
*
* @param folderInfo The FolderInfo describing the folder to open.
*/
- private void openFolder(FolderInfo folderInfo) {
+ public void openFolder(FolderInfo folderInfo) {
Folder openFolder;
if (folderInfo instanceof UserFolderInfo) {
@@ -1567,7 +1888,8 @@ public final class Launcher extends Activity
openFolder.bind(folderInfo);
folderInfo.opened = true;
- mWorkspace.addInScreen(openFolder, folderInfo.screen, 0, 0, 4, 4);
+ mWorkspace.addInFullScreen(openFolder, folderInfo.screen);
+
openFolder.onOpen();
}
@@ -1604,28 +1926,38 @@ public final class Launcher extends Activity
v = (View) v.getParent();
}
- CellLayout.CellInfo cellInfo = (CellLayout.CellInfo) v.getTag();
+ resetAddInfo();
+ CellLayout.CellInfo longClickCellInfo = (CellLayout.CellInfo) v.getTag();
// This happens when long clicking an item with the dpad/trackball
- if (cellInfo == null) {
+ if (longClickCellInfo == null || !longClickCellInfo.valid) {
return true;
}
+ final View itemUnderLongClick = longClickCellInfo.cell;
+
if (mWorkspace.allowLongPress()) {
- if (cellInfo.cell == null) {
- if (cellInfo.valid) {
- // User long pressed on empty space
- mWorkspace.setAllowLongPress(false);
- mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
- showAddDialog(cellInfo);
+ if (itemUnderLongClick == null) {
+ // User long pressed on empty space
+ mWorkspace.setAllowLongPress(false);
+ mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ if (LauncherApplication.isScreenXLarge()) {
+ // Animate the widget chooser up from the bottom of the screen
+ if (!isCustomizationDrawerVisible()) {
+ showCustomizationDrawer(true);
+ }
+ } else {
+ showAddDialog(longClickCellInfo.cellX, longClickCellInfo.cellY);
}
} else {
- if (!(cellInfo.cell instanceof Folder)) {
+ if (!(itemUnderLongClick instanceof Folder)) {
// User long pressed on an item
mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
- mWorkspace.startDrag(cellInfo);
+ mAddIntersectCellX = longClickCellInfo.cellX;
+ mAddIntersectCellY = longClickCellInfo.cellY;
+ mWorkspace.startDrag(longClickCellInfo);
}
}
}
@@ -1665,9 +1997,9 @@ public final class Launcher extends Activity
final Workspace workspace = mWorkspace;
CellLayout cell = ((CellLayout) workspace.getChildAt(start));
-
+
float max = workspace.getChildCount();
-
+
final Rect r = new Rect();
resources.getDrawable(R.drawable.preview_background).getPadding(r);
int extraW = (int) ((r.left + r.right) * max);
@@ -1713,12 +2045,12 @@ public final class Launcher extends Activity
image.setOnClickListener(handler);
image.setOnFocusChangeListener(handler);
image.setFocusable(true);
- if (i == mWorkspace.getCurrentScreen()) image.requestFocus();
+ if (i == mWorkspace.getCurrentPage()) image.requestFocus();
preview.addView(image,
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
- bitmaps.add(bitmap);
+ bitmaps.add(bitmap);
}
final PopupWindow p = new PopupWindow(this);
@@ -1739,7 +2071,7 @@ public final class Launcher extends Activity
anchor.setTag(p);
anchor.setTag(R.id.workspace, preview);
- anchor.setTag(R.id.icon, bitmaps);
+ anchor.setTag(R.id.icon, bitmaps);
}
class PreviewTouchHandler implements View.OnClickListener, Runnable, View.OnFocusChangeListener {
@@ -1750,17 +2082,17 @@ public final class Launcher extends Activity
}
public void onClick(View v) {
- mWorkspace.snapToScreen((Integer) v.getTag());
+ mWorkspace.snapToPage((Integer) v.getTag());
v.post(this);
}
public void run() {
- dismissPreview(mAnchor);
+ dismissPreview(mAnchor);
}
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
- mWorkspace.snapToScreen((Integer) v.getTag());
+ mWorkspace.snapToPage((Integer) v.getTag());
}
}
}
@@ -1803,13 +2135,17 @@ public final class Launcher extends Activity
showDialog(DIALOG_RENAME_FOLDER);
}
- private void showAddDialog(CellLayout.CellInfo cellInfo) {
- mAddItemCellInfo = cellInfo;
+ private void showAddDialog(int intersectX, int intersectY) {
+ resetAddInfo();
+ mAddIntersectCellX = intersectX;
+ mAddIntersectCellY = intersectY;
+ mAddScreen = mWorkspace.getCurrentPage();
mWaitingForResult = true;
showDialog(DIALOG_CREATE_SHORTCUT);
}
private void pickShortcut() {
+ // Insert extra item to handle picking application
Bundle bundle = new Bundle();
ArrayList<String> shortcutNames = new ArrayList<String>();
@@ -1911,22 +2247,319 @@ public final class Launcher extends Activity
// Now a part of LauncherModel.Callbacks. Used to reorder loading steps.
public boolean isAllAppsVisible() {
- return (mAllAppsGrid != null) ? mAllAppsGrid.isVisible() : false;
+ return mAllAppsGrid != null && mAllAppsGrid.isVisible();
}
// AllAppsView.Watcher
public void zoomed(float zoom) {
- if (zoom == 1.0f) {
+ // In XLarge view, we zoom down the workspace below all apps so it's still visible
+ if (zoom == 1.0f && !LauncherApplication.isScreenXLarge()) {
mWorkspace.setVisibility(View.GONE);
}
}
+
+ private void showToolbarButton(View button) {
+ button.setAlpha(1.0f);
+ button.setVisibility(View.VISIBLE);
+ button.setFocusable(true);
+ button.setClickable(true);
+ }
+
+ private void hideToolbarButton(View button) {
+ button.setAlpha(0.0f);
+ // We can't set it to GONE, otherwise the RelativeLayout gets screwed up
+ button.setVisibility(View.INVISIBLE);
+ button.setFocusable(false);
+ button.setClickable(false);
+ }
+
+ /**
+ * Helper function for showing or hiding a toolbar button, possibly animated.
+ *
+ * @param show If true, create an animation to the show the item. Otherwise, hide it.
+ * @param view The toolbar button to be animated
+ * @param seq A AnimatorSet that will be used to animate the transition. If null, the
+ * transition will not be animated.
+ */
+ private void hideOrShowToolbarButton(boolean show, final View view, AnimatorSet seq) {
+ final boolean showing = show;
+ final boolean hiding = !show;
+
+ final int duration = show ?
+ getResources().getInteger(R.integer.config_toolbarButtonFadeInTime) :
+ getResources().getInteger(R.integer.config_toolbarButtonFadeOutTime);
+
+ if (seq != null) {
+ Animator anim = new ObjectAnimator<Float>(duration, view, "alpha", show ? 1.0f : 0.0f);
+ anim.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationStart(Animator animation) {
+ if (showing) showToolbarButton(view);
+ }
+ public void onAnimationEnd(Animator animation) {
+ if (hiding) hideToolbarButton(view);
+ }
+ });
+ seq.play(anim);
+ } else {
+ if (showing) {
+ showToolbarButton(view);
+ } else {
+ hideToolbarButton(view);
+ }
+ }
+ }
+
+ /**
+ * Show/hide the appropriate toolbar buttons for newState.
+ * If showSeq or hideSeq is null, the transition will be done immediately (not animated).
+ *
+ * @param newState The state that is being switched to
+ * @param showSeq AnimatorSet in which to put "show" animations, or null.
+ * @param hideSeq AnimatorSet in which to put "hide" animations, or null.
+ */
+ private void hideAndShowToolbarButtons(State newState, AnimatorSet showSeq, AnimatorSet hideSeq) {
+ final View searchButton = findViewById(R.id.search_button);
+ final View allAppsButton = findViewById(R.id.all_apps_button);
+ final View marketButton = findViewById(R.id.market_button);
+ final View configureButton = findViewById(R.id.configure_button);
+
+ switch (newState) {
+ case WORKSPACE:
+ hideOrShowToolbarButton(true, searchButton, showSeq);
+ hideOrShowToolbarButton(true, allAppsButton, showSeq);
+ hideOrShowToolbarButton(true, configureButton, showSeq);
+ hideOrShowToolbarButton(false, marketButton, hideSeq);
+ mDeleteZone.setHandle(allAppsButton);
+ break;
+ case ALL_APPS:
+ hideOrShowToolbarButton(true, configureButton, showSeq);
+ hideOrShowToolbarButton(true, marketButton, showSeq);
+ hideOrShowToolbarButton(false, searchButton, hideSeq);
+ hideOrShowToolbarButton(false, allAppsButton, hideSeq);
+ mDeleteZone.setHandle(marketButton);
+ break;
+ case CUSTOMIZE:
+ hideOrShowToolbarButton(true, allAppsButton, showSeq);
+ hideOrShowToolbarButton(false, searchButton, hideSeq);
+ hideOrShowToolbarButton(false, marketButton, hideSeq);
+ hideOrShowToolbarButton(false, configureButton, hideSeq);
+ mDeleteZone.setHandle(allAppsButton);
+ break;
+ }
+ }
+
+ /**
+ * Helper method for the cameraZoomIn/cameraZoomOut animations
+ * @param view The view being animated
+ * @param state The state that we are moving in or out of -- either ALL_APPS or CUSTOMIZE
+ * @param scaleFactor The scale factor used for the zoom
+ */
+ private void setPivotsForZoom(View view, State state, float scaleFactor) {
+ final int height = view.getHeight();
+ view.setPivotX(view.getWidth() / 2.0f);
+ // Set pivotY so that at the starting zoom factor, the view is off-screen by a small margin
+ // Assumes that the view is normally anchored to either the top or bottom of the screen
+ final int margin = getResources().getInteger(R.integer.config_allAppsVerticalOffset);
+ if (state == State.ALL_APPS) {
+ view.setPivotY(height + ((view.getTop() + height) / scaleFactor) + margin);
+ } else {
+ view.setPivotY(0.0f - (view.getTop() / scaleFactor) - margin);
+ }
+ }
+
+ /**
+ * Zoom the camera out from the workspace to reveal 'toView'.
+ * Assumes that the view to show is anchored at either the very top or very bottom
+ * of the screen.
+ * @param toState The state to zoom out to. Must be ALL_APPS or CUSTOMIZE.
+ */
+ private void cameraZoomOut(State toState, boolean animated) {
+ final Resources res = getResources();
+ final int duration = res.getInteger(R.integer.config_allAppsZoomInTime);
+ final float scale = (float) res.getInteger(R.integer.config_allAppsZoomScaleFactor);
+ final boolean toAllApps = (toState == State.ALL_APPS);
+ final View toView = toAllApps ? (View) mAllAppsGrid : mHomeCustomizationDrawer;
+
+ setPivotsForZoom(toView, toState, scale);
+
+ if (toState == State.ALL_APPS) {
+ mWorkspace.shrinkToBottom(animated);
+ } else {
+ mWorkspace.shrinkToTop(animated);
+ }
+
+ if (animated) {
+ ValueAnimator scaleAnim = new ObjectAnimator(duration, toView,
+ new PropertyValuesHolder<Float>("scaleX", scale, 1.0f),
+ new PropertyValuesHolder<Float>("scaleY", scale, 1.0f));
+ scaleAnim.setInterpolator(new DecelerateInterpolator());
+ scaleAnim.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationStart(Animator animation) {
+ // Prepare the position
+ toView.setTranslationX(0.0f);
+ toView.setTranslationY(0.0f);
+ toView.setVisibility(View.VISIBLE);
+ }
+ });
+
+ AnimatorSet toolbarHideAnim = new AnimatorSet();
+ AnimatorSet toolbarShowAnim = new AnimatorSet();
+ hideAndShowToolbarButtons(toState, toolbarShowAnim, toolbarHideAnim);
+
+ // toView should appear right at the end of the workspace shrink animation
+ final int startDelay = res.getInteger(R.integer.config_workspaceShrinkTime) - duration;
+
+ AnimatorSet s = new AnimatorSet();
+ s.playTogether(scaleAnim, toolbarHideAnim);
+ s.play(scaleAnim).after(startDelay);
+
+ // Show the new toolbar buttons just as the main animation is ending
+ final int fadeInTime = res.getInteger(R.integer.config_toolbarButtonFadeInTime);
+ s.play(toolbarShowAnim).after(duration + startDelay - fadeInTime);
+ s.start();
+ } else {
+ toView.setTranslationX(0.0f);
+ toView.setTranslationY(0.0f);
+ toView.setScaleX(1.0f);
+ toView.setScaleY(1.0f);
+ toView.setVisibility(View.VISIBLE);
+ hideAndShowToolbarButtons(toState, null, null);
+ }
+ }
+
+ /**
+ * Zoom the camera back into the workspace, hiding 'fromView'.
+ * This is the opposite of cameraZoomOut.
+ * @param fromState The current state (must be ALL_APPS or CUSTOMIZE).
+ * @param animated If true, the transition will be animated.
+ */
+ private void cameraZoomIn(State fromState, boolean animated) {
+ Resources res = getResources();
+ int duration = res.getInteger(R.integer.config_allAppsZoomOutTime);
+ float scaleFactor = (float) res.getInteger(R.integer.config_allAppsZoomScaleFactor);
+ final View fromView =
+ (fromState == State.ALL_APPS) ? (View) mAllAppsGrid : mHomeCustomizationDrawer;
+
+ mCustomizePagedView.endChoiceMode();
+ mAllAppsPagedView.endChoiceMode();
+
+ setPivotsForZoom(fromView, fromState, scaleFactor);
+
+ mWorkspace.unshrink(animated);
+
+ if (animated) {
+ AnimatorSet s = new AnimatorSet();
+ ValueAnimator scaleAnim = new ObjectAnimator(duration, fromView,
+ new PropertyValuesHolder<Float>("scaleX", scaleFactor),
+ new PropertyValuesHolder<Float>("scaleY", scaleFactor));
+ scaleAnim.setInterpolator(new AccelerateInterpolator());
+ s.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ fromView.setVisibility(View.GONE);
+ fromView.setScaleX(1.0f);
+ fromView.setScaleY(1.0f);
+ }
+ });
+
+ AnimatorSet toolbarHideAnim = new AnimatorSet();
+ AnimatorSet toolbarShowAnim = new AnimatorSet();
+ hideAndShowToolbarButtons(State.WORKSPACE, toolbarShowAnim, toolbarHideAnim);
+
+ s.playTogether(scaleAnim, toolbarHideAnim);
+
+ // Show the new toolbar buttons at the very end of the whole animation
+ final int fadeInTime = res.getInteger(R.integer.config_toolbarButtonFadeInTime);
+ final int unshrinkTime = res.getInteger(R.integer.config_workspaceUnshrinkTime);
+ s.play(toolbarShowAnim).after(unshrinkTime - fadeInTime);
+ s.start();
+ } else {
+ fromView.setVisibility(View.GONE);
+ hideAndShowToolbarButtons(State.WORKSPACE, null, null);
+ }
+ }
+
+ /**
+ * Pan the camera in the vertical plane between 'fromView' and 'toView'.
+ * This is the transition used on xlarge screens to go between all apps and
+ * the home customization drawer.
+ * @param fromState The view to pan away from. Must be ALL_APPS or CUSTOMIZE.
+ * @param toState The view to pan into the frame. Must be ALL_APPS or CUSTOMIZE.
+ * @param animated If true, the transition will be animated.
+ */
+ private void cameraPan(State fromState, State toState, boolean animated) {
+ final Resources res = getResources();
+ final int duration = res.getInteger(R.integer.config_allAppsCameraPanTime);
+ final int workspaceHeight = mWorkspace.getHeight();
+
+ final boolean fromAllApps = (fromState == State.ALL_APPS);
+ final View fromView = fromAllApps ? (View) mAllAppsGrid : mHomeCustomizationDrawer;
+ final View toView = fromAllApps ? mHomeCustomizationDrawer : (View) mAllAppsGrid;
+
+ final float fromViewStartY = fromAllApps ? 0.0f : fromView.getY();
+ final float fromViewEndY = fromAllApps ? -fromView.getHeight() * 2 : workspaceHeight * 2;
+ final float toViewStartY = fromAllApps ? workspaceHeight * 2 : -toView.getHeight() * 2;
+ final float toViewEndY = fromAllApps ? workspaceHeight - toView.getHeight() : 0.0f;
+
+ mCustomizePagedView.endChoiceMode();
+ mAllAppsPagedView.endChoiceMode();
+
+ if (toState == State.ALL_APPS) {
+ mWorkspace.shrinkToBottom(animated);
+ } else {
+ mWorkspace.shrinkToTop(animated);
+ }
+
+ if (animated) {
+ AnimatorSet s = new AnimatorSet();
+ s.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationStart(Animator animation) {
+ toView.setVisibility(View.VISIBLE);
+ toView.setY(toViewStartY);
+ }
+ public void onAnimationEnd(Animator animation) {
+ fromView.setVisibility(View.GONE);
+ }
+ });
+
+ AnimatorSet toolbarHideAnim = new AnimatorSet();
+ AnimatorSet toolbarShowAnim = new AnimatorSet();
+ hideAndShowToolbarButtons(toState, toolbarShowAnim, toolbarHideAnim);
+
+ s.playTogether(
+ toolbarHideAnim,
+ new ObjectAnimator(duration, fromView, "y", fromViewStartY, fromViewEndY),
+ new ObjectAnimator(duration, toView, "y", toViewStartY, toViewEndY));
+
+ // Show the new toolbar buttons just as the main animation is ending
+ final int fadeInTime = res.getInteger(R.integer.config_toolbarButtonFadeInTime);
+ s.play(toolbarShowAnim).after(duration - fadeInTime);
+ s.start();
+ } else {
+ fromView.setY(fromViewEndY);
+ fromView.setVisibility(View.GONE);
+ toView.setY(toViewEndY);
+ toView.setVisibility(View.VISIBLE);
+ hideAndShowToolbarButtons(toState, null, null);
+ }
+ }
void showAllApps(boolean animated) {
- mAllAppsGrid.zoom(1.0f, animated);
+ if (mAllAppsGrid.isVisible())
+ return;
+
+ if (LauncherApplication.isScreenXLarge()) {
+ if (isCustomizationDrawerVisible()) {
+ cameraPan(State.CUSTOMIZE, State.ALL_APPS, animated);
+ } else {
+ cameraZoomOut(State.ALL_APPS, animated);
+ }
+ } else {
+ mAllAppsGrid.zoom(1.0f, animated);
+ }
((View) mAllAppsGrid).setFocusable(true);
((View) mAllAppsGrid).requestFocus();
-
+
// TODO: fade these two too
mDeleteZone.setVisibility(View.GONE);
}
@@ -1973,9 +2606,13 @@ public final class Launcher extends Activity
void closeAllApps(boolean animated) {
if (mAllAppsGrid.isVisible()) {
mWorkspace.setVisibility(View.VISIBLE);
- mAllAppsGrid.zoom(0.0f, animated);
+ if (LauncherApplication.isScreenXLarge()) {
+ cameraZoomIn(State.ALL_APPS, animated);
+ } else {
+ mAllAppsGrid.zoom(0.0f, animated);
+ }
((View)mAllAppsGrid).setFocusable(false);
- mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
+ mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
}
}
@@ -1987,6 +2624,92 @@ public final class Launcher extends Activity
// TODO
}
+ private boolean isCustomizationDrawerVisible() {
+ return mHomeCustomizationDrawer != null &&
+ mHomeCustomizationDrawer.getVisibility() == View.VISIBLE;
+ }
+
+ // Show the customization drawer (only exists in x-large configuration)
+ private void showCustomizationDrawer(boolean animated) {
+ if (isAllAppsVisible()) {
+ cameraPan(State.ALL_APPS, State.CUSTOMIZE, animated);
+ } else {
+ cameraZoomOut(State.CUSTOMIZE, animated);
+ }
+ }
+
+ // Hide the customization drawer (only exists in x-large configuration)
+ void hideCustomizationDrawer(boolean animated) {
+ if (isCustomizationDrawerVisible()) {
+ cameraZoomIn(State.CUSTOMIZE, animated);
+ }
+ }
+
+ void onWorkspaceClick(CellLayout layout) {
+ Object itemInfo = mAllAppsPagedView.getChosenItem();
+ if (itemInfo == null) {
+ itemInfo = mCustomizePagedView.getChosenItem();
+ }
+
+ if (itemInfo == null) {
+ // No items are chosen in All Apps or Customize, so just zoom into the workspace
+ mWorkspace.unshrink(layout);
+ if (isAllAppsVisible()) {
+ closeAllApps(true);
+ } else if (isCustomizationDrawerVisible()) {
+ hideCustomizationDrawer(true);
+ }
+ } else {
+ // Act as if the chosen item was dropped on the given CellLayout
+ if (mWorkspace.addExternalItemToScreen(itemInfo, layout)) {
+ mAllAppsPagedView.endChoiceMode();
+ mCustomizePagedView.endChoiceMode();
+ } else {
+ showOutOfSpaceMessage();
+ }
+ }
+ }
+
+ /**
+ * Sets the app market icon (shown when all apps is visible on x-large screens)
+ */
+ private void updateAppMarketIcon() {
+ if (LauncherApplication.isScreenXLarge()) {
+ // Find the app market activity by resolving an intent.
+ // (If multiple app markets are installed, it will return the ResolverActivity.)
+ PackageManager packageManager = getPackageManager();
+ Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_APP_MARKET);
+ ComponentName activityName = intent.resolveActivity(getPackageManager());
+ if (activityName != null) {
+ mAppMarketIntent = intent;
+ ImageView marketButton = (ImageView) findViewById(R.id.market_button);
+ Drawable toolbarIcon = null;
+ try {
+ // Look for the toolbar icon specified in the activity meta-data
+ Bundle metaData = packageManager.getActivityInfo(
+ activityName, PackageManager.GET_META_DATA).metaData;
+ if (metaData != null) {
+ int iconResId = metaData.getInt(TOOLBAR_ICON_METADATA_NAME);
+ if (iconResId != 0) {
+ Resources res = packageManager.getResourcesForActivity(activityName);
+ toolbarIcon = res.getDrawable(iconResId);
+ }
+ }
+ } catch (NameNotFoundException e) {
+ // Do nothing
+ }
+ // If we were unable to find the icon via the meta-data, use a generic one
+ if (toolbarIcon == null) {
+ marketButton.setImageResource(R.drawable.app_market_generic);
+ } else {
+ marketButton.setImageDrawable(toolbarIcon);
+ }
+ } else {
+ mAppMarketIntent = null;
+ }
+ }
+ }
+
/**
* Displays the shortcut creation dialog and launches, if necessary, the
* appropriate activity.
@@ -2039,7 +2762,6 @@ public final class Launcher extends Activity
switch (which) {
case AddAdapter.ITEM_SHORTCUT: {
- // Insert extra item to handle picking application
pickShortcut();
break;
}
@@ -2087,7 +2809,7 @@ public final class Launcher extends Activity
}
public void onShow(DialogInterface dialog) {
- mWaitingForResult = true;
+ mWaitingForResult = true;
}
}
@@ -2105,6 +2827,7 @@ public final class Launcher extends Activity
animate = false;
}
closeAllApps(animate);
+ hideCustomizationDrawer(animate);
}
}
}
@@ -2128,12 +2851,16 @@ public final class Launcher extends Activity
*/
public int getCurrentWorkspaceScreen() {
if (mWorkspace != null) {
- return mWorkspace.getCurrentScreen();
+ return mWorkspace.getCurrentPage();
} else {
return SCREEN_COUNT / 2;
}
}
+ void setAllAppsPagedView(PagedView view) {
+ mAllAppsPagedView = view;
+ }
+
/**
* Refreshes the shortcuts shown on the workspace.
*
@@ -2181,7 +2908,7 @@ public final class Launcher extends Activity
break;
case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
final FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
- (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
+ (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
(UserFolderInfo) item);
workspace.addInScreen(newFolder, item.screen, item.cellX, item.cellY, 1, 1,
false);
@@ -2189,7 +2916,7 @@ public final class Launcher extends Activity
case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
final FolderIcon newLiveFolder = LiveFolderIcon.fromXml(
R.layout.live_folder_icon, this,
- (ViewGroup) workspace.getChildAt(workspace.getCurrentScreen()),
+ (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
(LiveFolderInfo) item);
workspace.addInScreen(newLiveFolder, item.screen, item.cellX, item.cellY, 1, 1,
false);
@@ -2252,7 +2979,7 @@ public final class Launcher extends Activity
public void finishBindingItems() {
if (mSavedState != null) {
if (!mWorkspace.hasFocus()) {
- mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
+ mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
}
final long[] userFolders = mSavedState.getLongArray(RUNTIME_STATE_USER_FOLDERS);
@@ -2287,6 +3014,10 @@ public final class Launcher extends Activity
*/
public void bindAllApplications(ArrayList<ApplicationInfo> apps) {
mAllAppsGrid.setApps(apps);
+ if (mCustomizePagedView != null) {
+ mCustomizePagedView.setApps(apps);
+ }
+ updateAppMarketIcon();
}
/**
@@ -2297,6 +3028,10 @@ public final class Launcher extends Activity
public void bindAppsAdded(ArrayList<ApplicationInfo> apps) {
removeDialog(DIALOG_CREATE_SHORTCUT);
mAllAppsGrid.addApps(apps);
+ if (mCustomizePagedView != null) {
+ mCustomizePagedView.addApps(apps);
+ }
+ updateAppMarketIcon();
}
/**
@@ -2308,6 +3043,10 @@ public final class Launcher extends Activity
removeDialog(DIALOG_CREATE_SHORTCUT);
mWorkspace.updateShortcuts(apps);
mAllAppsGrid.updateApps(apps);
+ if (mCustomizePagedView != null) {
+ mCustomizePagedView.updateApps(apps);
+ }
+ updateAppMarketIcon();
}
/**
@@ -2321,6 +3060,19 @@ public final class Launcher extends Activity
mWorkspace.removeItems(apps);
}
mAllAppsGrid.removeApps(apps);
+ if (mCustomizePagedView != null) {
+ mCustomizePagedView.removeApps(apps);
+ }
+ updateAppMarketIcon();
+ }
+
+ /**
+ * A number of packages were updated.
+ */
+ public void bindPackagesUpdated() {
+ // update the customization drawer contents
+ if (mCustomizePagedView != null)
+ mCustomizePagedView.update();
}
/**
diff --git a/src/com/android/launcher2/LauncherAppWidgetHost.java b/src/com/android/launcher2/LauncherAppWidgetHost.java
index a5761ecc3..9352ec218 100644
--- a/src/com/android/launcher2/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher2/LauncherAppWidgetHost.java
@@ -30,10 +30,14 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
public LauncherAppWidgetHost(Context context, int hostId) {
super(context, hostId);
}
-
+
@Override
protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
AppWidgetProviderInfo appWidget) {
- return new LauncherAppWidgetHostView(context);
+ if (LauncherApplication.isScreenXLarge()) {
+ return new DimmableAppWidgetHostView(context);
+ } else {
+ return new LauncherAppWidgetHostView(context);
+ }
}
}
diff --git a/src/com/android/launcher2/LauncherAppWidgetInfo.java b/src/com/android/launcher2/LauncherAppWidgetInfo.java
index 8499ebb7c..32c92aabd 100644
--- a/src/com/android/launcher2/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher2/LauncherAppWidgetInfo.java
@@ -17,25 +17,50 @@
package com.android.launcher2;
import android.appwidget.AppWidgetHostView;
+import android.content.ComponentName;
import android.content.ContentValues;
/**
- * Represents a widget, which just contains an identifier.
+ * Represents a widget (either instantiated or about to be) in the Launcher.
*/
class LauncherAppWidgetInfo extends ItemInfo {
/**
+ * Indicates that the widget hasn't been instantiated yet.
+ */
+ static final int NO_ID = -1;
+
+ /**
* Identifier for this widget when talking with
* {@link android.appwidget.AppWidgetManager} for updates.
*/
- int appWidgetId;
+ int appWidgetId = NO_ID;
+
+ ComponentName providerName;
+ // TODO: Are these necessary here?
+ int minWidth = -1;
+ int minHeight = -1;
+
/**
* View that holds this widget after it's been created. This view isn't created
* until Launcher knows it's needed.
*/
AppWidgetHostView hostView = null;
+ /**
+ * Constructor for use with AppWidgets that haven't been instantiated yet.
+ */
+ LauncherAppWidgetInfo(ComponentName providerName) {
+ itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+ this.providerName = providerName;
+
+ // Since the widget isn't instantiated yet, we don't know these values. Set them to -1
+ // to indicate that they should be calculated based on the layout and minWidth/minHeight
+ spanX = -1;
+ spanY = -1;
+ }
+
LauncherAppWidgetInfo(int appWidgetId) {
itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
this.appWidgetId = appWidgetId;
@@ -52,7 +77,6 @@ class LauncherAppWidgetInfo extends ItemInfo {
return "AppWidget(id=" + Integer.toString(appWidgetId) + ")";
}
-
@Override
void unbind() {
super.unbind();
diff --git a/src/com/android/launcher2/LauncherApplication.java b/src/com/android/launcher2/LauncherApplication.java
index eda92d999..ca08378d8 100644
--- a/src/com/android/launcher2/LauncherApplication.java
+++ b/src/com/android/launcher2/LauncherApplication.java
@@ -20,6 +20,7 @@ import android.app.Application;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Configuration;
import android.database.ContentObserver;
import android.os.Handler;
import dalvik.system.VMRuntime;
@@ -27,6 +28,8 @@ import dalvik.system.VMRuntime;
public class LauncherApplication extends Application {
public LauncherModel mModel;
public IconCache mIconCache;
+ private static boolean sIsScreenXLarge;
+ private static final boolean ENABLE_ROTATION = false;
@Override
public void onCreate() {
@@ -36,6 +39,7 @@ public class LauncherApplication extends Application {
mIconCache = new IconCache(this);
mModel = new LauncherModel(this, mIconCache);
+ sIsScreenXLarge = (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE;
// Register intent receivers
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
@@ -89,4 +93,12 @@ public class LauncherApplication extends Application {
LauncherModel getModel() {
return mModel;
}
+
+ public static boolean isInPlaceRotationEnabled() {
+ return sIsScreenXLarge && ENABLE_ROTATION;
+ }
+
+ public static boolean isScreenXLarge() {
+ return sIsScreenXLarge;
+ }
}
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index 06fcfb319..7c1fa2114 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -16,6 +16,15 @@
package com.android.launcher2;
+import java.lang.ref.WeakReference;
+import java.net.URISyntaxException;
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
@@ -23,9 +32,9 @@ import android.content.ComponentName;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
+import android.content.Context;
import android.content.Intent;
import android.content.Intent.ShortcutIconResource;
-import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
@@ -38,20 +47,10 @@ import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Parcelable;
-import android.os.RemoteException;
-import android.util.Log;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
-
-import java.lang.ref.WeakReference;
-import java.net.URISyntaxException;
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
+import android.util.Log;
import com.android.launcher.R;
@@ -91,6 +90,9 @@ public class LauncherModel extends BroadcastReceiver {
private Bitmap mDefaultIcon;
+ private static int mCellCountX;
+ private static int mCellCountY;
+
public interface Callbacks {
public int getCurrentWorkspaceScreen();
public void startBinding();
@@ -102,6 +104,7 @@ public class LauncherModel extends BroadcastReceiver {
public void bindAppsAdded(ArrayList<ApplicationInfo> apps);
public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);
public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent);
+ public void bindPackagesUpdated();
public boolean isAllAppsVisible();
}
@@ -146,6 +149,7 @@ public class LauncherModel extends BroadcastReceiver {
*/
static void moveItemInDatabase(Context context, ItemInfo item, long container, int screen,
int cellX, int cellY) {
+
item.container = container;
item.screen = screen;
item.cellX = cellX;
@@ -155,8 +159,8 @@ public class LauncherModel extends BroadcastReceiver {
final ContentResolver cr = context.getContentResolver();
values.put(LauncherSettings.Favorites.CONTAINER, item.container);
- values.put(LauncherSettings.Favorites.CELLX, item.cellX);
- values.put(LauncherSettings.Favorites.CELLY, item.cellY);
+ values.put(LauncherSettings.Favorites.CELLX, cellX);
+ values.put(LauncherSettings.Favorites.CELLY, cellY);
values.put(LauncherSettings.Favorites.SCREEN, item.screen);
cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
@@ -181,6 +185,48 @@ public class LauncherModel extends BroadcastReceiver {
}
/**
+ * Returns an ItemInfo array containing all the items in the LauncherModel.
+ * The ItemInfo.id is not set through this function.
+ */
+ static ArrayList<ItemInfo> getItemsInLocalCoordinates(Context context) {
+ ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
+ final ContentResolver cr = context.getContentResolver();
+ Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] {
+ LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER,
+ LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
+ LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY }, null, null, null);
+
+ final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
+ final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
+ final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
+ final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
+ final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
+ final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
+ final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
+
+ try {
+ while (c.moveToNext()) {
+ ItemInfo item = new ItemInfo();
+ item.cellX = c.getInt(cellXIndex);
+ item.cellY = c.getInt(cellYIndex);
+ item.spanX = c.getInt(spanXIndex);
+ item.spanY = c.getInt(spanYIndex);
+ item.container = c.getInt(containerIndex);
+ item.itemType = c.getInt(itemTypeIndex);
+ item.screen = c.getInt(screenIndex);
+
+ items.add(item);
+ }
+ } catch (Exception e) {
+ items.clear();
+ } finally {
+ c.close();
+ }
+
+ return items;
+ }
+
+ /**
* Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
*/
FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
@@ -239,9 +285,10 @@ public class LauncherModel extends BroadcastReceiver {
final ContentValues values = new ContentValues();
final ContentResolver cr = context.getContentResolver();
-
item.onAddToDatabase(values);
+ item.updateValuesWithCoordinates(values, cellX, cellY);
+
Uri result = cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
@@ -251,6 +298,30 @@ public class LauncherModel extends BroadcastReceiver {
}
/**
+ * Creates a new unique child id, for a given cell span across all layouts.
+ */
+ static int getCellLayoutChildId(int cellId, int screen, int localCellX, int localCellY, int spanX, int spanY) {
+ return ((cellId & 0xFF) << 16) | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
+ }
+
+ static int getCellCountX() {
+ return mCellCountX;
+ }
+
+ static int getCellCountY() {
+ return mCellCountY;
+ }
+
+ /**
+ * Updates the model orientation helper to take into account the current layout dimensions
+ * when performing local/canonical coordinate transformations.
+ */
+ static void updateWorkspaceLayoutCells(int shortAxisCellCount, int longAxisCellCount) {
+ mCellCountX = shortAxisCellCount;
+ mCellCountY = longAxisCellCount;
+ }
+
+ /**
* Update an item to the database in a specified container.
*/
static void updateItemInDatabase(Context context, ItemInfo item) {
@@ -258,6 +329,7 @@ public class LauncherModel extends BroadcastReceiver {
final ContentResolver cr = context.getContentResolver();
item.onAddToDatabase(values);
+ item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
cr.update(LauncherSettings.Favorites.getContentUri(item.id, false), values, null, null);
}
@@ -303,7 +375,7 @@ public class LauncherModel extends BroadcastReceiver {
*/
public void onReceive(Context context, Intent intent) {
if (DEBUG_LOADERS) Log.d(TAG, "onReceive intent=" + intent);
-
+
final String action = intent.getAction();
if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
@@ -347,7 +419,6 @@ public class LauncherModel extends BroadcastReceiver {
String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
enqueuePackageUpdated(new PackageUpdatedTask(
PackageUpdatedTask.OP_UNAVAILABLE, packages));
-
}
}
@@ -453,7 +524,7 @@ public class LauncherModel extends BroadcastReceiver {
}
if (DEBUG_LOADERS) {
Log.d(TAG, "waited "
- + (SystemClock.uptimeMillis()-workspaceWaitTime)
+ + (SystemClock.uptimeMillis()-workspaceWaitTime)
+ "ms for previous step to finish binding");
}
}
@@ -473,7 +544,6 @@ public class LauncherModel extends BroadcastReceiver {
android.os.Process.setThreadPriority(mIsLaunching
? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
}
-
if (loadWorkspaceFirst) {
if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
loadAndBindWorkspace();
@@ -575,14 +645,13 @@ public class LauncherModel extends BroadcastReceiver {
if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
return true;
}
-
for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
if (occupied[item.screen][x][y] != null) {
Log.e(TAG, "Error loading shortcut " + item
- + " into cell (" + item.screen + ":"
+ + " into cell (" + item.screen + ":"
+ x + "," + y
- + ") occupied by "
+ + ") occupied by "
+ occupied[item.screen][x][y]);
return false;
}
@@ -614,7 +683,8 @@ public class LauncherModel extends BroadcastReceiver {
final Cursor c = contentResolver.query(
LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
- final ItemInfo occupied[][][] = new ItemInfo[Launcher.SCREEN_COUNT][Launcher.NUMBER_CELLS_X][Launcher.NUMBER_CELLS_Y];
+ final ItemInfo occupied[][][] =
+ new ItemInfo[Launcher.SCREEN_COUNT][mCellCountX][mCellCountY];
try {
final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
@@ -723,7 +793,6 @@ public class LauncherModel extends BroadcastReceiver {
UserFolderInfo folderInfo = findOrMakeUserFolder(mFolders, id);
folderInfo.title = c.getString(titleIndex);
-
folderInfo.id = id;
container = c.getInt(containerIndex);
folderInfo.container = container;
@@ -735,7 +804,6 @@ public class LauncherModel extends BroadcastReceiver {
if (!checkItemPlacement(occupied, folderInfo)) {
break;
}
-
switch (container) {
case LauncherSettings.Favorites.CONTAINER_DESKTOP:
mItems.add(folderInfo);
@@ -758,7 +826,6 @@ public class LauncherModel extends BroadcastReceiver {
itemsToRemove.add(id);
} else {
LiveFolderInfo liveFolderInfo = findOrMakeLiveFolder(mFolders, id);
-
intentDescription = c.getString(intentIndex);
intent = null;
if (intentDescription != null) {
@@ -804,7 +871,7 @@ public class LauncherModel extends BroadcastReceiver {
final AppWidgetProviderInfo provider =
widgets.getAppWidgetInfo(appWidgetId);
-
+
if (!isSafeMode && (provider == null || provider.provider == null ||
provider.provider.getPackageName() == null)) {
Log.e(TAG, "Deleting widget that isn't installed anymore: id="
@@ -865,13 +932,13 @@ public class LauncherModel extends BroadcastReceiver {
if (DEBUG_LOADERS) {
Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
Log.d(TAG, "workspace layout: ");
- for (int y = 0; y < Launcher.NUMBER_CELLS_Y; y++) {
+ for (int y = 0; y < mCellCountY; y++) {
String line = "";
for (int s = 0; s < Launcher.SCREEN_COUNT; s++) {
if (s > 0) {
line += " | ";
}
- for (int x = 0; x < Launcher.NUMBER_CELLS_X; x++) {
+ for (int x = 0; x < mCellCountX; x++) {
line += ((occupied[s][x][y] != null) ? "#" : ".");
}
}
@@ -1095,7 +1162,7 @@ public class LauncherModel extends BroadcastReceiver {
startIndex = i;
for (int j=0; i<N && j<batchSize; j++) {
// This builds the icon bitmaps.
- mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));
+ mAllAppsList.add(new ApplicationInfo(packageManager, apps.get(i), mIconCache));
i++;
}
@@ -1258,6 +1325,15 @@ public class LauncherModel extends BroadcastReceiver {
}
});
}
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (callbacks == mCallbacks.get()) {
+ callbacks.bindPackagesUpdated();
+ }
+ }
+ });
}
}
@@ -1402,11 +1478,11 @@ public class LauncherModel extends BroadcastReceiver {
}
ShortcutInfo addShortcut(Context context, Intent data,
- CellLayout.CellInfo cellInfo, boolean notify) {
+ int screen, int cellX, int cellY, boolean notify) {
final ShortcutInfo info = infoFromShortcutIntent(context, data);
addItemToDatabase(context, info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
- cellInfo.screen, cellInfo.cellX, cellInfo.cellY, notify);
+ screen, cellX, cellY, notify);
return info;
}
diff --git a/src/com/android/launcher2/LiveFolderInfo.java b/src/com/android/launcher2/LiveFolderInfo.java
index 7d0a0f58f..74b021758 100644
--- a/src/com/android/launcher2/LiveFolderInfo.java
+++ b/src/com/android/launcher2/LiveFolderInfo.java
@@ -18,7 +18,6 @@ package com.android.launcher2;
import android.content.ContentValues;
import android.content.Intent;
-import android.graphics.drawable.Drawable;
import android.graphics.Bitmap;
import android.net.Uri;
diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java
new file mode 100644
index 000000000..4e47acb40
--- /dev/null
+++ b/src/com/android/launcher2/PagedView.java
@@ -0,0 +1,1194 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.ActionMode;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+import android.view.animation.AnimationUtils;
+import android.widget.Checkable;
+import android.widget.LinearLayout;
+import android.widget.Scroller;
+
+import com.android.launcher.R;
+
+/**
+ * An abstraction of the original Workspace which supports browsing through a
+ * sequential list of "pages"
+ */
+public abstract class PagedView extends ViewGroup {
+ private static final String TAG = "PagedView";
+ protected static final int INVALID_PAGE = -1;
+
+ // the min drag distance for a fling to register, to prevent random page shifts
+ private static final int MIN_LENGTH_FOR_FLING = 50;
+
+ private static final int PAGE_SNAP_ANIMATION_DURATION = 1000;
+ protected static final float NANOTIME_DIV = 1000000000.0f;
+
+ // the velocity at which a fling gesture will cause us to snap to the next page
+ protected int mSnapVelocity = 500;
+
+ protected float mSmoothingTime;
+ protected float mTouchX;
+
+ protected boolean mFirstLayout = true;
+
+ protected int mCurrentPage;
+ protected int mNextPage = INVALID_PAGE;
+ protected Scroller mScroller;
+ private VelocityTracker mVelocityTracker;
+
+ private float mDownMotionX;
+ private float mLastMotionX;
+ private float mLastMotionY;
+
+ protected final static int TOUCH_STATE_REST = 0;
+ protected final static int TOUCH_STATE_SCROLLING = 1;
+ protected final static int TOUCH_STATE_PREV_PAGE = 2;
+ protected final static int TOUCH_STATE_NEXT_PAGE = 3;
+ protected final static float ALPHA_QUANTIZE_LEVEL = 0.01f;
+
+ protected int mTouchState = TOUCH_STATE_REST;
+
+ protected OnLongClickListener mLongClickListener;
+
+ private boolean mAllowLongPress = true;
+
+ private int mTouchSlop;
+ private int mPagingTouchSlop;
+ private int mMaximumVelocity;
+
+ protected static final int INVALID_POINTER = -1;
+
+ protected int mActivePointerId = INVALID_POINTER;
+
+ private PageSwitchListener mPageSwitchListener;
+
+ private ArrayList<Boolean> mDirtyPageContent;
+ private boolean mDirtyPageAlpha;
+
+ // choice modes
+ protected static final int CHOICE_MODE_NONE = 0;
+ protected static final int CHOICE_MODE_SINGLE = 1;
+ // Multiple selection mode is not supported by all Launcher actions atm
+ protected static final int CHOICE_MODE_MULTIPLE = 2;
+
+ protected int mChoiceMode;
+ private ActionMode mActionMode;
+
+ protected PagedViewIconCache mPageViewIconCache;
+
+ // If true, syncPages and syncPageItems will be called to refresh pages
+ protected boolean mContentIsRefreshable = true;
+
+ // If true, modify alpha of neighboring pages as user scrolls left/right
+ protected boolean mFadeInAdjacentScreens = true;
+
+ // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding
+ // to switch to a new page
+ protected boolean mUsePagingTouchSlop = true;
+
+ // If true, the subclass should directly update mScrollX itself in its computeScroll method
+ // (SmoothPagedView does this)
+ protected boolean mDeferScrollUpdate = false;
+
+ /**
+ * Simple cache mechanism for PagedViewIcon outlines.
+ */
+ class PagedViewIconCache {
+ private final HashMap<Object, Bitmap> iconOutlineCache = new HashMap<Object, Bitmap>();
+
+ public void clear() {
+ iconOutlineCache.clear();
+ }
+ public void addOutline(Object key, Bitmap b) {
+ iconOutlineCache.put(key, b);
+ }
+ public void removeOutline(Object key) {
+ if (iconOutlineCache.containsKey(key)) {
+ iconOutlineCache.remove(key);
+ }
+ }
+ public Bitmap getOutline(Object key) {
+ return iconOutlineCache.get(key);
+ }
+ }
+
+ public interface PageSwitchListener {
+ void onPageSwitch(View newPage, int newPageIndex);
+ }
+
+ public interface PageMovingListener {
+ void onPageBeginMoving();
+ void onPageEndMoving();
+ }
+
+ public PagedView(Context context) {
+ this(context, null);
+ }
+
+ public PagedView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public PagedView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mChoiceMode = CHOICE_MODE_NONE;
+
+ setHapticFeedbackEnabled(false);
+ init();
+ }
+
+ /**
+ * Initializes various states for this workspace.
+ */
+ protected void init() {
+ mDirtyPageContent = new ArrayList<Boolean>();
+ mDirtyPageContent.ensureCapacity(32);
+ mPageViewIconCache = new PagedViewIconCache();
+ mScroller = new Scroller(getContext());
+ mCurrentPage = 0;
+
+ final ViewConfiguration configuration = ViewConfiguration.get(getContext());
+ mTouchSlop = configuration.getScaledTouchSlop();
+ mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
+ mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ }
+
+ public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
+ mPageSwitchListener = pageSwitchListener;
+ if (mPageSwitchListener != null) {
+ mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
+ }
+ }
+
+ /**
+ * Returns the index of the currently displayed page.
+ *
+ * @return The index of the currently displayed page.
+ */
+ int getCurrentPage() {
+ return mCurrentPage;
+ }
+
+ int getPageCount() {
+ return getChildCount();
+ }
+
+ View getPageAt(int index) {
+ return getChildAt(index);
+ }
+
+ int getScrollWidth() {
+ return getWidth();
+ }
+
+ /**
+ * Sets the current page.
+ */
+ void setCurrentPage(int currentPage) {
+ if (!mScroller.isFinished()) mScroller.abortAnimation();
+ if (getChildCount() == 0) return;
+
+ mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1));
+ scrollTo(getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage), 0);
+
+ invalidate();
+ notifyPageSwitchListener();
+ }
+
+ protected void notifyPageSwitchListener() {
+ if (mPageSwitchListener != null) {
+ mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
+ }
+ }
+
+ // a method that subclasses can override to add behavior
+ protected void pageBeginMoving() {
+ }
+
+ // a method that subclasses can override to add behavior
+ protected void pageEndMoving() {
+ }
+
+ /**
+ * Registers the specified listener on each page contained in this workspace.
+ *
+ * @param l The listener used to respond to long clicks.
+ */
+ @Override
+ public void setOnLongClickListener(OnLongClickListener l) {
+ mLongClickListener = l;
+ final int count = getPageCount();
+ for (int i = 0; i < count; i++) {
+ getPageAt(i).setOnLongClickListener(l);
+ }
+ }
+
+ @Override
+ public void scrollTo(int x, int y) {
+ super.scrollTo(x, y);
+ mTouchX = x;
+ mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+ }
+
+ // we moved this functionality to a helper function so SmoothPagedView can reuse it
+ protected boolean computeScrollHelper() {
+ if (mScroller.computeScrollOffset()) {
+ mDirtyPageAlpha = true;
+ scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
+ invalidate();
+ return true;
+ } else if (mNextPage != INVALID_PAGE) {
+ mDirtyPageAlpha = true;
+ mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
+ mNextPage = INVALID_PAGE;
+ notifyPageSwitchListener();
+ pageEndMoving();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void computeScroll() {
+ computeScrollHelper();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ if (widthMode != MeasureSpec.EXACTLY) {
+ throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
+ }
+
+ final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+ if (heightMode != MeasureSpec.EXACTLY) {
+ throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
+ }
+
+ // The children are given the same width and height as the workspace
+ // unless they were set to WRAP_CONTENT
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ // disallowing padding in paged view (just pass 0)
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ int childWidthMode;
+ if (lp.width == LayoutParams.WRAP_CONTENT) {
+ childWidthMode = MeasureSpec.AT_MOST;
+ } else {
+ childWidthMode = MeasureSpec.EXACTLY;
+ }
+
+ int childHeightMode;
+ if (lp.height == LayoutParams.WRAP_CONTENT) {
+ childHeightMode = MeasureSpec.AT_MOST;
+ } else {
+ childHeightMode = MeasureSpec.EXACTLY;
+ }
+
+ final int childWidthMeasureSpec =
+ MeasureSpec.makeMeasureSpec(widthSize, childWidthMode);
+ final int childHeightMeasureSpec =
+ MeasureSpec.makeMeasureSpec(heightSize, childHeightMode);
+
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ }
+
+ setMeasuredDimension(widthSize, heightSize);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
+ setHorizontalScrollBarEnabled(false);
+ int newX = getChildOffset(mCurrentPage) - getRelativeChildOffset(mCurrentPage);
+ scrollTo(newX, 0);
+ mScroller.setFinalX(newX);
+ setHorizontalScrollBarEnabled(true);
+ mFirstLayout = false;
+ }
+
+ final int childCount = getChildCount();
+ int childLeft = 0;
+ if (childCount > 0) {
+ childLeft = getRelativeChildOffset(0);
+ }
+
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != View.GONE) {
+ final int childWidth = child.getMeasuredWidth();
+ final int childHeight = (getMeasuredHeight() - child.getMeasuredHeight()) / 2;
+ child.layout(childLeft, childHeight,
+ childLeft + childWidth, childHeight + child.getMeasuredHeight());
+ childLeft += childWidth;
+ }
+ }
+ }
+
+ protected void updateAdjacentPagesAlpha() {
+ if (mFadeInAdjacentScreens) {
+ if (mDirtyPageAlpha || (mTouchState == TOUCH_STATE_SCROLLING) || !mScroller.isFinished()) {
+ int halfScreenSize = getMeasuredWidth() / 2;
+ int screenCenter = mScrollX + halfScreenSize;
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; ++i) {
+ View layout = (View) getChildAt(i);
+ int childWidth = layout.getMeasuredWidth();
+ int halfChildWidth = (childWidth / 2);
+ int childCenter = getChildOffset(i) + halfChildWidth;
+
+ int d = halfChildWidth;
+ int distanceFromScreenCenter = childCenter - screenCenter;
+ if (distanceFromScreenCenter > 0) {
+ if (i > 0) {
+ d += getChildAt(i - 1).getMeasuredWidth() / 2;
+ }
+ } else {
+ if (i < childCount - 1) {
+ d += getChildAt(i + 1).getMeasuredWidth() / 2;
+ }
+ }
+
+ float dimAlpha = (float) (Math.abs(distanceFromScreenCenter)) / d;
+ dimAlpha = Math.max(0.0f, Math.min(1.0f, (dimAlpha * dimAlpha)));
+ float alpha = 1.0f - dimAlpha;
+
+ if (alpha < ALPHA_QUANTIZE_LEVEL) {
+ alpha = 0.0f;
+ } else if (alpha > 1.0f - ALPHA_QUANTIZE_LEVEL) {
+ alpha = 1.0f;
+ }
+
+ if (Float.compare(alpha, layout.getAlpha()) != 0) {
+ layout.setAlpha(alpha);
+ }
+ }
+ mDirtyPageAlpha = false;
+ }
+ }
+ }
+
+ protected void screenScrolled(int screenCenter) {
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ updateAdjacentPagesAlpha();
+
+ int halfScreenSize = getMeasuredWidth() / 2;
+ int screenCenter = mScrollX + halfScreenSize;
+ screenScrolled(screenCenter);
+ // Find out which screens are visible; as an optimization we only call draw on them
+ // As an optimization, this code assumes that all pages have the same width as the 0th
+ // page.
+ final int pageCount = getChildCount();
+ if (pageCount > 0) {
+ final int pageWidth = getChildAt(0).getMeasuredWidth();
+ final int screenWidth = getMeasuredWidth();
+ int x = getRelativeChildOffset(0) + pageWidth;
+ int leftScreen = 0;
+ int rightScreen = 0;
+ while (x <= mScrollX) {
+ leftScreen++;
+ x += pageWidth;
+ // replace above line with this if you don't assume all pages have same width as 0th
+ // page:
+ // x += getChildAt(leftScreen).getMeasuredWidth();
+ }
+ rightScreen = leftScreen;
+ while (x < mScrollX + screenWidth) {
+ rightScreen++;
+ x += pageWidth;
+ // replace above line with this if you don't assume all pages have same width as 0th
+ // page:
+ //if (rightScreen < pageCount) {
+ // x += getChildAt(rightScreen).getMeasuredWidth();
+ //}
+ }
+ rightScreen = Math.min(getChildCount() - 1, rightScreen);
+
+ final long drawingTime = getDrawingTime();
+ for (int i = leftScreen; i <= rightScreen; i++) {
+ drawChild(canvas, getChildAt(i), drawingTime);
+ }
+ }
+ }
+
+ @Override
+ public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
+ int page = indexOfChild(child);
+ if (page != mCurrentPage || !mScroller.isFinished()) {
+ snapToPage(page);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+ int focusablePage;
+ if (mNextPage != INVALID_PAGE) {
+ focusablePage = mNextPage;
+ } else {
+ focusablePage = mCurrentPage;
+ }
+ View v = getPageAt(focusablePage);
+ if (v != null) {
+ v.requestFocus(direction, previouslyFocusedRect);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean dispatchUnhandledMove(View focused, int direction) {
+ if (direction == View.FOCUS_LEFT) {
+ if (getCurrentPage() > 0) {
+ snapToPage(getCurrentPage() - 1);
+ return true;
+ }
+ } else if (direction == View.FOCUS_RIGHT) {
+ if (getCurrentPage() < getPageCount() - 1) {
+ snapToPage(getCurrentPage() + 1);
+ return true;
+ }
+ }
+ return super.dispatchUnhandledMove(focused, direction);
+ }
+
+ @Override
+ public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+ if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
+ getPageAt(mCurrentPage).addFocusables(views, direction);
+ }
+ if (direction == View.FOCUS_LEFT) {
+ if (mCurrentPage > 0) {
+ getPageAt(mCurrentPage - 1).addFocusables(views, direction);
+ }
+ } else if (direction == View.FOCUS_RIGHT){
+ if (mCurrentPage < getPageCount() - 1) {
+ getPageAt(mCurrentPage + 1).addFocusables(views, direction);
+ }
+ }
+ }
+
+ /**
+ * If one of our descendant views decides that it could be focused now, only
+ * pass that along if it's on the current page.
+ *
+ * This happens when live folders requery, and if they're off page, they
+ * end up calling requestFocus, which pulls it on page.
+ */
+ @Override
+ public void focusableViewAvailable(View focused) {
+ View current = getPageAt(mCurrentPage);
+ View v = focused;
+ while (true) {
+ if (v == current) {
+ super.focusableViewAvailable(focused);
+ return;
+ }
+ if (v == this) {
+ return;
+ }
+ ViewParent parent = v.getParent();
+ if (parent instanceof View) {
+ v = (View)v.getParent();
+ } else {
+ return;
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ if (disallowIntercept) {
+ // We need to make sure to cancel our long press if
+ // a scrollable widget takes over touch events
+ final View currentPage = getChildAt(mCurrentPage);
+ currentPage.cancelLongPress();
+ }
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ /*
+ * This method JUST determines whether we want to intercept the motion.
+ * If we return true, onTouchEvent will be called and we do the actual
+ * scrolling there.
+ */
+
+ /*
+ * Shortcut the most recurring case: the user is in the dragging
+ * state and he is moving his finger. We want to intercept this
+ * motion.
+ */
+ final int action = ev.getAction();
+ if ((action == MotionEvent.ACTION_MOVE) &&
+ (mTouchState == TOUCH_STATE_SCROLLING)) {
+ return true;
+ }
+
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_MOVE: {
+ /*
+ * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
+ * whether the user has moved far enough from his original down touch.
+ */
+ if (mActivePointerId != INVALID_POINTER) {
+ determineScrollingStart(ev);
+ break;
+ }
+ // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN
+ // event. in that case, treat the first occurence of a move event as a ACTION_DOWN
+ // i.e. fall through to the next case (don't break)
+ // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events
+ // while it's small- this was causing a crash before we checked for INVALID_POINTER)
+ }
+
+ case MotionEvent.ACTION_DOWN: {
+ final float x = ev.getX();
+ final float y = ev.getY();
+ // Remember location of down touch
+ mDownMotionX = x;
+ mLastMotionX = x;
+ mLastMotionY = y;
+ mActivePointerId = ev.getPointerId(0);
+ mAllowLongPress = true;
+
+ /*
+ * If being flinged and user touches the screen, initiate drag;
+ * otherwise don't. mScroller.isFinished should be false when
+ * being flinged.
+ */
+ final int xDist = (mScroller.getFinalX() - mScroller.getCurrX());
+ final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop);
+ if (finishedScrolling) {
+ mTouchState = TOUCH_STATE_REST;
+ mScroller.abortAnimation();
+ } else {
+ mTouchState = TOUCH_STATE_SCROLLING;
+ }
+
+ // check if this can be the beginning of a tap on the side of the pages
+ // to scroll the current page
+ if ((mTouchState != TOUCH_STATE_PREV_PAGE) &&
+ (mTouchState != TOUCH_STATE_NEXT_PAGE)) {
+ if (getChildCount() > 0) {
+ int width = getMeasuredWidth();
+ int offset = getRelativeChildOffset(mCurrentPage);
+ if (x < offset) {
+ mTouchState = TOUCH_STATE_PREV_PAGE;
+ } else if (x > (width - offset)) {
+ mTouchState = TOUCH_STATE_NEXT_PAGE;
+ }
+ }
+ }
+ break;
+ }
+
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ // Release the drag
+ pageEndMoving();
+ mTouchState = TOUCH_STATE_REST;
+ mAllowLongPress = false;
+ mActivePointerId = INVALID_POINTER;
+
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ onSecondaryPointerUp(ev);
+ break;
+ }
+
+ /*
+ * The only time we want to intercept motion events is if we are in the
+ * drag mode.
+ */
+ return mTouchState != TOUCH_STATE_REST;
+ }
+
+ protected void animateClickFeedback(View v, final Runnable r) {
+ // animate the view slightly to show click feedback running some logic after it is "pressed"
+ Animation anim = AnimationUtils.loadAnimation(getContext(),
+ R.anim.paged_view_click_feedback);
+ anim.setAnimationListener(new AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {}
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ r.run();
+ }
+ @Override
+ public void onAnimationEnd(Animation animation) {}
+ });
+ v.startAnimation(anim);
+ }
+
+ /*
+ * Determines if we should change the touch state to start scrolling after the
+ * user moves their touch point too far.
+ */
+ private void determineScrollingStart(MotionEvent ev) {
+ /*
+ * Locally do absolute value. mLastMotionX is set to the y value
+ * of the down event.
+ */
+ final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ final float x = ev.getX(pointerIndex);
+ final float y = ev.getY(pointerIndex);
+ final int xDiff = (int) Math.abs(x - mLastMotionX);
+ final int yDiff = (int) Math.abs(y - mLastMotionY);
+
+ final int touchSlop = mTouchSlop;
+ boolean xPaged = xDiff > mPagingTouchSlop;
+ boolean xMoved = xDiff > touchSlop;
+ boolean yMoved = yDiff > touchSlop;
+
+ if (xMoved || yMoved) {
+ if (mUsePagingTouchSlop ? xPaged : xMoved) {
+ // Scroll if the user moved far enough along the X axis
+ mTouchState = TOUCH_STATE_SCROLLING;
+ mLastMotionX = x;
+ mTouchX = mScrollX;
+ mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+ pageBeginMoving();
+ }
+ // Either way, cancel any pending longpress
+ if (mAllowLongPress) {
+ mAllowLongPress = false;
+ // Try canceling the long press. It could also have been scheduled
+ // by a distant descendant, so use the mAllowLongPress flag to block
+ // everything
+ final View currentPage = getPageAt(mCurrentPage);
+ currentPage.cancelLongPress();
+ }
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+
+ final int action = ev.getAction();
+
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN:
+ /*
+ * If being flinged and user touches, stop the fling. isFinished
+ * will be false if being flinged.
+ */
+ if (!mScroller.isFinished()) {
+ mScroller.abortAnimation();
+ }
+
+ // Remember where the motion event started
+ mDownMotionX = mLastMotionX = ev.getX();
+ mActivePointerId = ev.getPointerId(0);
+ if (mTouchState == TOUCH_STATE_SCROLLING) {
+ pageBeginMoving();
+ }
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ if (mTouchState == TOUCH_STATE_SCROLLING) {
+ // Scroll to follow the motion event
+ final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ final float x = ev.getX(pointerIndex);
+ final int deltaX = (int) (mLastMotionX - x);
+ mLastMotionX = x;
+
+ int sx = getScrollX();
+ if (deltaX < 0) {
+ if (sx > 0) {
+ mTouchX += Math.max(-mTouchX, deltaX);
+ mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+ if (!mDeferScrollUpdate) {
+ scrollBy(Math.max(-sx, deltaX), 0);
+ } else {
+ // This will trigger a call to computeScroll() on next drawChild() call
+ invalidate();
+ }
+ }
+ } else if (deltaX > 0) {
+ final int lastChildIndex = getChildCount() - 1;
+ final int availableToScroll = getChildOffset(lastChildIndex) -
+ getRelativeChildOffset(lastChildIndex) - sx;
+ if (availableToScroll > 0) {
+ mTouchX += Math.min(availableToScroll, deltaX);
+ mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+ if (!mDeferScrollUpdate) {
+ scrollBy(Math.min(availableToScroll, deltaX), 0);
+ } else {
+ // This will trigger a call to computeScroll() on next drawChild() call
+ invalidate();
+ }
+ }
+ } else {
+ awakenScrollBars();
+ }
+ } else if ((mTouchState == TOUCH_STATE_PREV_PAGE) ||
+ (mTouchState == TOUCH_STATE_NEXT_PAGE)) {
+ determineScrollingStart(ev);
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ if (mTouchState == TOUCH_STATE_SCROLLING) {
+ final int activePointerId = mActivePointerId;
+ final int pointerIndex = ev.findPointerIndex(activePointerId);
+ final float x = ev.getX(pointerIndex);
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ int velocityX = (int) velocityTracker.getXVelocity(activePointerId);
+ boolean isfling = Math.abs(mDownMotionX - x) > MIN_LENGTH_FOR_FLING;
+
+ final int snapVelocity = mSnapVelocity;
+ if (isfling && velocityX > snapVelocity && mCurrentPage > 0) {
+ snapToPageWithVelocity(mCurrentPage - 1, velocityX);
+ } else if (isfling && velocityX < -snapVelocity &&
+ mCurrentPage < getChildCount() - 1) {
+ snapToPageWithVelocity(mCurrentPage + 1, velocityX);
+ } else {
+ snapToDestination();
+ }
+
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
+ // at this point we have not moved beyond the touch slop
+ // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
+ // we can just page
+ int nextPage = Math.max(0, mCurrentPage - 1);
+ if (nextPage != mCurrentPage) {
+ snapToPage(nextPage);
+ } else {
+ snapToDestination();
+ }
+ } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) {
+ // at this point we have not moved beyond the touch slop
+ // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
+ // we can just page
+ int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1);
+ if (nextPage != mCurrentPage) {
+ snapToPage(nextPage);
+ } else {
+ snapToDestination();
+ }
+ }
+ mTouchState = TOUCH_STATE_REST;
+ mActivePointerId = INVALID_POINTER;
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ mTouchState = TOUCH_STATE_REST;
+ mActivePointerId = INVALID_POINTER;
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ onSecondaryPointerUp(ev);
+ break;
+ }
+
+ return true;
+ }
+
+ private void onSecondaryPointerUp(MotionEvent ev) {
+ final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
+ MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ final int pointerId = ev.getPointerId(pointerIndex);
+ if (pointerId == mActivePointerId) {
+ // This was our active pointer going up. Choose a new
+ // active pointer and adjust accordingly.
+ // TODO: Make this decision more intelligent.
+ final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+ mLastMotionX = mDownMotionX = ev.getX(newPointerIndex);
+ mLastMotionY = ev.getY(newPointerIndex);
+ mActivePointerId = ev.getPointerId(newPointerIndex);
+ if (mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ }
+ }
+ }
+
+ @Override
+ public void requestChildFocus(View child, View focused) {
+ super.requestChildFocus(child, focused);
+ int page = indexOfChild(child);
+ if (page >= 0 && !isInTouchMode()) {
+ snapToPage(page);
+ }
+ }
+
+ protected int getChildIndexForRelativeOffset(int relativeOffset) {
+ final int childCount = getChildCount();
+ int left = getRelativeChildOffset(0);
+ for (int i = 0; i < childCount; ++i) {
+ final int right = (left + getChildAt(i).getMeasuredWidth());
+ if (left <= relativeOffset && relativeOffset <= right) {
+ return i;
+ }
+ left = right;
+ }
+ return -1;
+ }
+
+ protected int getRelativeChildOffset(int index) {
+ return (getMeasuredWidth() - getChildAt(index).getMeasuredWidth()) / 2;
+ }
+
+ protected int getChildOffset(int index) {
+ if (getChildCount() == 0)
+ return 0;
+
+ int offset = getRelativeChildOffset(0);
+ for (int i = 0; i < index; ++i) {
+ offset += getChildAt(i).getMeasuredWidth();
+ }
+ return offset;
+ }
+
+ int getPageNearestToCenterOfScreen() {
+ int minDistanceFromScreenCenter = getMeasuredWidth();
+ int minDistanceFromScreenCenterIndex = -1;
+ int screenCenter = mScrollX + (getMeasuredWidth() / 2);
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; ++i) {
+ View layout = (View) getChildAt(i);
+ int childWidth = layout.getMeasuredWidth();
+ int halfChildWidth = (childWidth / 2);
+ int childCenter = getChildOffset(i) + halfChildWidth;
+ int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
+ if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
+ minDistanceFromScreenCenter = distanceFromScreenCenter;
+ minDistanceFromScreenCenterIndex = i;
+ }
+ }
+ return minDistanceFromScreenCenterIndex;
+ }
+
+ protected void snapToDestination() {
+ snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
+ }
+
+ protected void snapToPageWithVelocity(int whichPage, int velocity) {
+ // We ignore velocity in this implementation, but children (e.g. SmoothPagedView)
+ // can use it
+ snapToPage(whichPage);
+ }
+
+ protected void snapToPage(int whichPage) {
+ snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
+ }
+
+ protected void snapToPage(int whichPage, int duration) {
+ whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
+
+ int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
+ final int sX = getScrollX();
+ final int delta = newX - sX;
+ snapToPage(whichPage, delta, duration);
+ }
+
+ protected void snapToPage(int whichPage, int delta, int duration) {
+ mNextPage = whichPage;
+
+ View focusedChild = getFocusedChild();
+ if (focusedChild != null && whichPage != mCurrentPage &&
+ focusedChild == getChildAt(mCurrentPage)) {
+ focusedChild.clearFocus();
+ }
+
+ pageBeginMoving();
+ awakenScrollBars(duration);
+ if (duration == 0) {
+ duration = Math.abs(delta);
+ }
+
+ if (!mScroller.isFinished()) mScroller.abortAnimation();
+ mScroller.startScroll(getScrollX(), 0, delta, 0, duration);
+
+ // only load some associated pages
+ loadAssociatedPages(mNextPage);
+ notifyPageSwitchListener();
+ invalidate();
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ final SavedState state = new SavedState(super.onSaveInstanceState());
+ state.currentPage = mCurrentPage;
+ return state;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ SavedState savedState = (SavedState) state;
+ super.onRestoreInstanceState(savedState.getSuperState());
+ if (savedState.currentPage != -1) {
+ mCurrentPage = savedState.currentPage;
+ }
+ }
+
+ public void scrollLeft() {
+ if (mScroller.isFinished()) {
+ if (mCurrentPage > 0) snapToPage(mCurrentPage - 1);
+ } else {
+ if (mNextPage > 0) snapToPage(mNextPage - 1);
+ }
+ }
+
+ public void scrollRight() {
+ if (mScroller.isFinished()) {
+ if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1);
+ } else {
+ if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1);
+ }
+ }
+
+ public int getPageForView(View v) {
+ int result = -1;
+ if (v != null) {
+ ViewParent vp = v.getParent();
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ if (vp == getChildAt(i)) {
+ return i;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return True is long presses are still allowed for the current touch
+ */
+ public boolean allowLongPress() {
+ return mAllowLongPress;
+ }
+
+ /**
+ * Set true to allow long-press events to be triggered, usually checked by
+ * {@link Launcher} to accept or block dpad-initiated long-presses.
+ */
+ public void setAllowLongPress(boolean allowLongPress) {
+ mAllowLongPress = allowLongPress;
+ }
+
+ public static class SavedState extends BaseSavedState {
+ int currentPage = -1;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ currentPage = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeInt(currentPage);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+ public void loadAssociatedPages(int page) {
+ if (mContentIsRefreshable) {
+ final int count = getChildCount();
+ if (page < count) {
+ int lowerPageBound = getAssociatedLowerPageBound(page);
+ int upperPageBound = getAssociatedUpperPageBound(page);
+ for (int i = 0; i < count; ++i) {
+ final ViewGroup layout = (ViewGroup) getChildAt(i);
+ final int childCount = layout.getChildCount();
+ if (lowerPageBound <= i && i <= upperPageBound) {
+ if (mDirtyPageContent.get(i)) {
+ syncPageItems(i);
+ mDirtyPageContent.set(i, false);
+ }
+ } else {
+ if (childCount > 0) {
+ layout.removeAllViews();
+ }
+ mDirtyPageContent.set(i, true);
+ }
+ }
+ }
+ }
+ }
+
+ protected int getAssociatedLowerPageBound(int page) {
+ return Math.max(0, page - 1);
+ }
+ protected int getAssociatedUpperPageBound(int page) {
+ final int count = getChildCount();
+ return Math.min(page + 1, count - 1);
+ }
+
+ protected void startChoiceMode(int mode, ActionMode.Callback callback) {
+ if (isChoiceMode(CHOICE_MODE_NONE)) {
+ mChoiceMode = mode;
+ mActionMode = startActionMode(callback);
+ }
+ }
+
+ public void endChoiceMode() {
+ if (!isChoiceMode(CHOICE_MODE_NONE)) {
+ mChoiceMode = CHOICE_MODE_NONE;
+ resetCheckedGrandchildren();
+ if (mActionMode != null) mActionMode.finish();
+ mActionMode = null;
+ }
+ }
+
+ protected boolean isChoiceMode(int mode) {
+ return mChoiceMode == mode;
+ }
+
+ protected ArrayList<Checkable> getCheckedGrandchildren() {
+ ArrayList<Checkable> checked = new ArrayList<Checkable>();
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; ++i) {
+ final ViewGroup layout = (ViewGroup) getChildAt(i);
+ final int grandChildCount = layout.getChildCount();
+ for (int j = 0; j < grandChildCount; ++j) {
+ final View v = layout.getChildAt(j);
+ if (v instanceof Checkable && ((Checkable) v).isChecked()) {
+ checked.add((Checkable) v);
+ }
+ }
+ }
+ return checked;
+ }
+
+ /**
+ * If in CHOICE_MODE_SINGLE and an item is checked, returns that item.
+ * Otherwise, returns null.
+ */
+ protected Checkable getSingleCheckedGrandchild() {
+ if (mChoiceMode == CHOICE_MODE_SINGLE) {
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; ++i) {
+ final ViewGroup layout = (ViewGroup) getChildAt(i);
+ final int grandChildCount = layout.getChildCount();
+ for (int j = 0; j < grandChildCount; ++j) {
+ final View v = layout.getChildAt(j);
+ if (v instanceof Checkable && ((Checkable) v).isChecked()) {
+ return (Checkable) v;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public Object getChosenItem() {
+ View checkedView = (View) getSingleCheckedGrandchild();
+ if (checkedView != null) {
+ return checkedView.getTag();
+ }
+ return null;
+ }
+
+ protected void resetCheckedGrandchildren() {
+ // loop through children, and set all of their children to _not_ be checked
+ final ArrayList<Checkable> checked = getCheckedGrandchildren();
+ for (int i = 0; i < checked.size(); ++i) {
+ final Checkable c = checked.get(i);
+ c.setChecked(false);
+ }
+ }
+
+ /**
+ * This method is called ONLY to synchronize the number of pages that the paged view has.
+ * To actually fill the pages with information, implement syncPageItems() below. It is
+ * guaranteed that syncPageItems() will be called for a particular page before it is shown,
+ * and therefore, individual page items do not need to be updated in this method.
+ */
+ public abstract void syncPages();
+
+ /**
+ * This method is called to synchronize the items that are on a particular page. If views on
+ * the page can be reused, then they should be updated within this method.
+ */
+ public abstract void syncPageItems(int page);
+
+ public void invalidatePageData() {
+ if (mContentIsRefreshable) {
+ // Update all the pages
+ syncPages();
+
+ // Mark each of the pages as dirty
+ final int count = getChildCount();
+ mDirtyPageContent.clear();
+ for (int i = 0; i < count; ++i) {
+ mDirtyPageContent.add(true);
+ }
+
+ // Load any pages that are necessary for the current window of views
+ loadAssociatedPages(mCurrentPage);
+ mDirtyPageAlpha = true;
+ requestLayout();
+ }
+ }
+}
diff --git a/src/com/android/launcher2/PagedViewCellLayout.java b/src/com/android/launcher2/PagedViewCellLayout.java
new file mode 100644
index 000000000..b247a065f
--- /dev/null
+++ b/src/com/android/launcher2/PagedViewCellLayout.java
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+
+/**
+ * An abstraction of the original CellLayout which supports laying out items
+ * which span multiple cells into a grid-like layout. Also supports dimming
+ * to give a preview of its contents.
+ */
+public class PagedViewCellLayout extends ViewGroup {
+ static final String TAG = "PagedViewCellLayout";
+
+ private float mHolographicAlpha;
+
+ private boolean mCenterContent;
+
+ private int mCellCountX;
+ private int mCellCountY;
+ private int mCellWidth;
+ private int mCellHeight;
+ private static int sDefaultCellDimensions = 96;
+
+ public PagedViewCellLayout(Context context) {
+ this(context, null);
+ }
+
+ public PagedViewCellLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public PagedViewCellLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ setAlwaysDrawnWithCacheEnabled(false);
+
+ // setup default cell parameters
+ mCellWidth = mCellHeight = sDefaultCellDimensions;
+ mCellCountX = LauncherModel.getCellCountX();
+ mCellCountY = LauncherModel.getCellCountY();
+ mHolographicAlpha = 0.0f;
+ }
+
+ @Override
+ protected boolean onSetAlpha(int alpha) {
+ return true;
+ }
+
+ @Override
+ public void setAlpha(float alpha) {
+ mHolographicAlpha = 1.0f - alpha;
+ setChildrenAlpha(alpha);
+ super.setAlpha(alpha);
+ }
+
+ @Override
+ public void cancelLongPress() {
+ super.cancelLongPress();
+
+ // Cancel long press for all children
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ child.cancelLongPress();
+ }
+ }
+
+ public boolean addViewToCellLayout(View child, int index, int childId,
+ PagedViewCellLayout.LayoutParams params) {
+ final PagedViewCellLayout.LayoutParams lp = params;
+
+ // Generate an id for each view, this assumes we have at most 256x256 cells
+ // per workspace screen
+ if (lp.cellX >= 0 && lp.cellX <= (mCellCountX - 1) &&
+ lp.cellY >= 0 && (lp.cellY <= mCellCountY - 1)) {
+ // If the horizontal or vertical span is set to -1, it is taken to
+ // mean that it spans the extent of the CellLayout
+ if (lp.cellHSpan < 0) lp.cellHSpan = mCellCountX;
+ if (lp.cellVSpan < 0) lp.cellVSpan = mCellCountY;
+
+ child.setId(childId);
+
+ // We might be in the middle or end of shrinking/fading to a dimmed view
+ // Make sure this view's alpha is set the same as all the rest of the views
+ child.setAlpha(1.0f - mHolographicAlpha);
+
+ addView(child, index, lp);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void requestChildFocus(View child, View focused) {
+ super.requestChildFocus(child, focused);
+ if (child != null) {
+ Rect r = new Rect();
+ child.getDrawingRect(r);
+ requestRectangleOnScreen(r);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // TODO: currently ignoring padding
+
+ int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+ int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
+
+ int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+ int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
+
+ if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
+ throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
+ }
+
+ final int cellWidth = mCellWidth;
+ final int cellHeight = mCellHeight;
+
+ int numWidthGaps = mCellCountX - 1;
+ int numHeightGaps = mCellCountY - 1;
+
+ int vSpaceLeft = heightSpecSize - mPaddingTop
+ - mPaddingBottom - (cellHeight * mCellCountY);
+ int heightGap = vSpaceLeft / numHeightGaps;
+
+ int hSpaceLeft = widthSpecSize - mPaddingLeft
+ - mPaddingRight - (cellWidth * mCellCountX);
+ int widthGap = hSpaceLeft / numWidthGaps;
+
+ // center it around the min gaps
+ int minGap = Math.min(widthGap, heightGap);
+ int paddingLeft = mPaddingLeft;
+ int paddingTop = mPaddingTop;
+ /*
+ if (minGap < heightGap) {
+ // vertical space has shrunken, so change padding accordingly
+ paddingTop += ((heightGap - minGap) * (mCellCountY - 1)) / 2;
+ } else if (minGap < widthGap) {
+ // horizontal space has shrunken, so change padding accordingly
+ paddingLeft += ((widthGap - minGap) * (mCellCountX - 1)) / 2;
+ }
+ */
+ widthGap = heightGap = minGap;
+
+ int newWidth = mPaddingLeft + mPaddingRight + (mCellCountX * cellWidth) +
+ ((mCellCountX - 1) * minGap);
+ int newHeight = mPaddingTop + mPaddingBottom + (mCellCountY * cellHeight) +
+ ((mCellCountY - 1) * minGap);
+
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ PagedViewCellLayout.LayoutParams lp =
+ (PagedViewCellLayout.LayoutParams) child.getLayoutParams();
+ lp.setup(cellWidth, cellHeight, widthGap, heightGap,
+ paddingLeft, paddingTop);
+
+ int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width,
+ MeasureSpec.EXACTLY);
+ int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
+ MeasureSpec.EXACTLY);
+
+ child.measure(childWidthMeasureSpec, childheightMeasureSpec);
+ }
+
+ setMeasuredDimension(newWidth, newHeight);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int count = getChildCount();
+
+ int offsetX = 0;
+ if (mCenterContent) {
+ // determine the max width of all the rows and center accordingly
+ int maxRowWidth = 0;
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ PagedViewCellLayout.LayoutParams lp =
+ (PagedViewCellLayout.LayoutParams) child.getLayoutParams();
+ maxRowWidth = Math.max(maxRowWidth, lp.x + lp.width);
+ }
+ }
+ offsetX = (getMeasuredWidth() / 2) - (maxRowWidth / 2);
+ }
+
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ PagedViewCellLayout.LayoutParams lp =
+ (PagedViewCellLayout.LayoutParams) child.getLayoutParams();
+
+ int childLeft = offsetX + lp.x;
+ int childTop = lp.y;
+ child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
+ }
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return super.onTouchEvent(event) || true;
+ }
+
+ public void enableCenteredContent(boolean enabled) {
+ mCenterContent = enabled;
+ }
+
+ @Override
+ protected void setChildrenDrawingCacheEnabled(boolean enabled) {
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View view = getChildAt(i);
+ view.setDrawingCacheEnabled(enabled);
+ // Update the drawing caches
+ view.buildDrawingCache(true);
+ }
+ }
+
+ public void setCellCount(int xCount, int yCount) {
+ mCellCountX = xCount;
+ mCellCountY = yCount;
+ requestLayout();
+ }
+
+ public void setCellDimensions(int width, int height) {
+ mCellWidth = width;
+ mCellHeight = height;
+ requestLayout();
+ }
+
+ public int getDefaultCellDimensions() {
+ return sDefaultCellDimensions;
+ }
+
+ private void setChildrenAlpha(float alpha) {
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ getChildAt(i).setAlpha(alpha);
+ }
+ }
+
+ public int[] getCellCountForDimensions(int width, int height) {
+ // Always assume we're working with the smallest span to make sure we
+ // reserve enough space in both orientations
+ int smallerSize = Math.min(mCellWidth, mCellHeight);
+
+ // Always round up to next largest cell
+ int spanX = (width + smallerSize) / smallerSize;
+ int spanY = (height + smallerSize) / smallerSize;
+
+ return new int[] { spanX, spanY };
+ }
+
+ /**
+ * Start dragging the specified child
+ *
+ * @param child The child that is being dragged
+ */
+ void onDragChild(View child) {
+ PagedViewCellLayout.LayoutParams lp = (PagedViewCellLayout.LayoutParams) child.getLayoutParams();
+ lp.isDragging = true;
+ }
+
+ /**
+ * Estimates the number of cells that the specified width would take up.
+ */
+ public int estimateCellHSpan(int width) {
+ // TODO: we need to take widthGap into effect
+ return (width + mCellWidth) / mCellWidth;
+ }
+
+ /**
+ * Estimates the number of cells that the specified height would take up.
+ */
+ public int estimateCellVSpan(int height) {
+ // TODO: we need to take heightGap into effect
+ return (height + mCellHeight) / mCellHeight;
+ }
+
+ /**
+ * Estimates the width that the number of vSpan cells will take up.
+ */
+ public int estimateCellWidth(int hSpan) {
+ // TODO: we need to take widthGap into effect
+ return hSpan * mCellWidth;
+ }
+
+ /**
+ * Estimates the height that the number of vSpan cells will take up.
+ */
+ public int estimateCellHeight(int vSpan) {
+ // TODO: we need to take heightGap into effect
+ return vSpan * mCellHeight;
+ }
+
+ @Override
+ public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new PagedViewCellLayout.LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof PagedViewCellLayout.LayoutParams;
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ return new PagedViewCellLayout.LayoutParams(p);
+ }
+
+ public static class LayoutParams extends ViewGroup.MarginLayoutParams {
+ /**
+ * Horizontal location of the item in the grid.
+ */
+ @ViewDebug.ExportedProperty
+ public int cellX;
+
+ /**
+ * Vertical location of the item in the grid.
+ */
+ @ViewDebug.ExportedProperty
+ public int cellY;
+
+ /**
+ * Number of cells spanned horizontally by the item.
+ */
+ @ViewDebug.ExportedProperty
+ public int cellHSpan;
+
+ /**
+ * Number of cells spanned vertically by the item.
+ */
+ @ViewDebug.ExportedProperty
+ public int cellVSpan;
+
+ /**
+ * Is this item currently being dragged
+ */
+ public boolean isDragging;
+
+ // a data object that you can bind to this layout params
+ private Object mTag;
+
+ // X coordinate of the view in the layout.
+ @ViewDebug.ExportedProperty
+ int x;
+ // Y coordinate of the view in the layout.
+ @ViewDebug.ExportedProperty
+ int y;
+
+ public LayoutParams() {
+ super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+ cellHSpan = 1;
+ cellVSpan = 1;
+ }
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ cellHSpan = 1;
+ cellVSpan = 1;
+ }
+
+ public LayoutParams(ViewGroup.LayoutParams source) {
+ super(source);
+ cellHSpan = 1;
+ cellVSpan = 1;
+ }
+
+ public LayoutParams(LayoutParams source) {
+ super(source);
+ this.cellX = source.cellX;
+ this.cellY = source.cellY;
+ this.cellHSpan = source.cellHSpan;
+ this.cellVSpan = source.cellVSpan;
+ }
+
+ public LayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) {
+ super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+ this.cellX = cellX;
+ this.cellY = cellY;
+ this.cellHSpan = cellHSpan;
+ this.cellVSpan = cellVSpan;
+ }
+
+ public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
+ int hStartPadding, int vStartPadding) {
+
+ final int myCellHSpan = cellHSpan;
+ final int myCellVSpan = cellVSpan;
+ final int myCellX = cellX;
+ final int myCellY = cellY;
+
+ width = myCellHSpan * cellWidth + ((myCellHSpan - 1) * widthGap) -
+ leftMargin - rightMargin;
+ height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
+ topMargin - bottomMargin;
+
+ x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
+ y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
+ }
+
+ public Object getTag() {
+ return mTag;
+ }
+
+ public void setTag(Object tag) {
+ mTag = tag;
+ }
+
+ public String toString() {
+ return "(" + this.cellX + ", " + this.cellY + ", " +
+ this.cellHSpan + ", " + this.cellVSpan + ")";
+ }
+ }
+}
diff --git a/src/com/android/launcher2/PagedViewIcon.java b/src/com/android/launcher2/PagedViewIcon.java
new file mode 100644
index 000000000..0714a933c
--- /dev/null
+++ b/src/com/android/launcher2/PagedViewIcon.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region.Op;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.Checkable;
+import android.widget.TextView;
+
+import com.android.launcher.R;
+import com.android.launcher2.PagedView.PagedViewIconCache;
+
+
+
+/**
+ * An icon on a PagedView, specifically for items in the launcher's paged view (with compound
+ * drawables on the top).
+ */
+public class PagedViewIcon extends TextView implements Checkable {
+ private static final String TAG = "PagedViewIcon";
+
+ // holographic outline
+ private final Paint mPaint = new Paint();
+ private static HolographicOutlineHelper sHolographicOutlineHelper;
+ private Bitmap mCheckedOutline;
+ private Bitmap mHolographicOutline;
+ private Canvas mHolographicOutlineCanvas;
+ private boolean mIsHolographicUpdatePass;
+ private Rect mDrawableClipRect;
+
+ private Object mIconCacheKey;
+ private PagedViewIconCache mIconCache;
+
+ private int mAlpha;
+ private int mHolographicAlpha;
+
+ private boolean mIsChecked;
+
+ // Highlight colours
+ private int mHoloBlurColor;
+ private int mHoloOutlineColor;
+ private int mCheckedBlurColor;
+ private int mCheckedOutlineColor;
+
+
+ public PagedViewIcon(Context context) {
+ this(context, null);
+ }
+
+ public PagedViewIcon(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public PagedViewIcon(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagedViewIcon, defStyle, 0);
+ mHoloBlurColor = a.getColor(R.styleable.PagedViewIcon_blurColor, 0);
+ mHoloOutlineColor = a.getColor(R.styleable.PagedViewIcon_outlineColor, 0);
+ mCheckedBlurColor = a.getColor(R.styleable.PagedViewIcon_checkedBlurColor, 0);
+ mCheckedOutlineColor = a.getColor(R.styleable.PagedViewIcon_checkedOutlineColor, 0);
+ a.recycle();
+
+ if (sHolographicOutlineHelper == null) {
+ sHolographicOutlineHelper = new HolographicOutlineHelper();
+ }
+ mDrawableClipRect = new Rect();
+
+ setFocusable(true);
+ setBackgroundDrawable(null);
+ }
+
+ public void applyFromApplicationInfo(ApplicationInfo info, PagedViewIconCache cache) {
+ mIconCache = cache;
+ mIconCacheKey = info;
+ mHolographicOutline = mIconCache.getOutline(mIconCacheKey);
+
+ setCompoundDrawablesWithIntrinsicBounds(null,
+ new FastBitmapDrawable(info.iconBitmap), null, null);
+ setText(info.title);
+ setTag(info);
+ }
+
+ public void applyFromResolveInfo(ResolveInfo info, PackageManager packageManager,
+ PagedViewIconCache cache) {
+ mIconCache = cache;
+ mIconCacheKey = info;
+ mHolographicOutline = mIconCache.getOutline(mIconCacheKey);
+
+ Drawable image = info.loadIcon(packageManager);
+ image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight());
+ setCompoundDrawablesWithIntrinsicBounds(null, image, null, null);
+ setText(info.loadLabel(packageManager));
+ setTag(info);
+ }
+
+ @Override
+ public void setAlpha(float alpha) {
+ final float viewAlpha = sHolographicOutlineHelper.viewAlphaInterpolator(alpha);
+ final float holographicAlpha = sHolographicOutlineHelper.highlightAlphaInterpolator(alpha);
+ mAlpha = (int) (viewAlpha * 255);
+ mHolographicAlpha = (int) (holographicAlpha * 255);
+ super.setAlpha(viewAlpha);
+ }
+
+ public void invalidateCheckedImage() {
+ if (mCheckedOutline != null) {
+ mCheckedOutline.recycle();
+ mCheckedOutline = null;
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ if (mHolographicOutline == null) {
+ // update the clipping rect to be used in the holographic pass below
+ getDrawingRect(mDrawableClipRect);
+ mDrawableClipRect.bottom = getPaddingTop() + getCompoundPaddingTop();
+
+ // set a flag to indicate that we are going to draw the view at full alpha with the text
+ // clipped for the generation of the holographic icon
+ mIsHolographicUpdatePass = true;
+ mHolographicOutline = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
+ Bitmap.Config.ARGB_8888);
+ mHolographicOutlineCanvas = new Canvas(mHolographicOutline);
+ mHolographicOutlineCanvas.concat(getMatrix());
+ draw(mHolographicOutlineCanvas);
+ sHolographicOutlineHelper.applyExpensiveOutlineWithBlur(mHolographicOutline,
+ mHolographicOutlineCanvas, mHoloBlurColor, mHoloOutlineColor);
+ mIsHolographicUpdatePass = false;
+ mIconCache.addOutline(mIconCacheKey, mHolographicOutline);
+ mHolographicOutlineCanvas = null;
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ // draw the view itself
+ if (mIsHolographicUpdatePass) {
+ // only clip to the text view (restore its alpha so that we get a proper outline)
+ canvas.save();
+ canvas.clipRect(mDrawableClipRect, Op.REPLACE);
+ final float alpha = getAlpha();
+ super.setAlpha(1.0f);
+ super.onDraw(canvas);
+ super.setAlpha(alpha);
+ canvas.restore();
+ } else {
+ if (mAlpha > 0) {
+ super.onDraw(canvas);
+ }
+ }
+
+ // draw any blended overlays
+ if (!mIsHolographicUpdatePass) {
+ if (mCheckedOutline == null) {
+ if (mHolographicOutline != null && mHolographicAlpha > 0) {
+ mPaint.setAlpha(mHolographicAlpha);
+ canvas.drawBitmap(mHolographicOutline, 0, 0, mPaint);
+ }
+ } else {
+ mPaint.setAlpha(255);
+ canvas.drawBitmap(mCheckedOutline, 0, 0, mPaint);
+ }
+ }
+ }
+
+ @Override
+ public boolean isChecked() {
+ return mIsChecked;
+ }
+
+ @Override
+ public void setChecked(boolean checked) {
+ if (mIsChecked != checked) {
+ mIsChecked = checked;
+
+ if (mIsChecked) {
+ // update the clipping rect to be used in the holographic pass below
+ getDrawingRect(mDrawableClipRect);
+ mDrawableClipRect.bottom = getPaddingTop() + getCompoundPaddingTop();
+
+ // set a flag to indicate that we are going to draw the view at full alpha with the text
+ // clipped for the generation of the holographic icon
+ mIsHolographicUpdatePass = true;
+ mCheckedOutline = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
+ Bitmap.Config.ARGB_8888);
+ mHolographicOutlineCanvas = new Canvas(mCheckedOutline);
+ mHolographicOutlineCanvas.concat(getMatrix());
+ draw(mHolographicOutlineCanvas);
+ sHolographicOutlineHelper.applyExpensiveOutlineWithBlur(mCheckedOutline,
+ mHolographicOutlineCanvas, mCheckedBlurColor, mCheckedOutlineColor);
+ mIsHolographicUpdatePass = false;
+ mHolographicOutlineCanvas = null;
+ } else {
+ invalidateCheckedImage();
+ }
+
+ invalidate();
+ }
+ }
+
+ @Override
+ public void toggle() {
+ setChecked(!mIsChecked);
+ }
+}
diff --git a/src/com/android/launcher2/PagedViewWidgetIcon.java b/src/com/android/launcher2/PagedViewWidgetIcon.java
new file mode 100644
index 000000000..f285dab7f
--- /dev/null
+++ b/src/com/android/launcher2/PagedViewWidgetIcon.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.Checkable;
+import android.widget.LinearLayout;
+
+import com.android.launcher.R;
+
+/**
+ * An widget icon for use specifically in the CustomizePagedView. In class form so that
+ * we can add logic for how it will look when checked/unchecked.
+ */
+public class PagedViewWidgetIcon extends LinearLayout implements Checkable {
+ private static final String TAG = "PagedViewIcon";
+
+ // Holographic outline
+ private final Paint mPaint = new Paint();
+ private static HolographicOutlineHelper sHolographicOutlineHelper;
+ private final Paint mErasePaint = new Paint();
+ private Bitmap mCheckedOutline;
+ private Canvas mHolographicOutlineCanvas;
+ private boolean mIsHolographicUpdatePass;
+
+ private int mAlpha;
+
+ private boolean mIsChecked;
+
+ // Highlight colours
+ private int mCheckedBlurColor;
+ private int mCheckedOutlineColor;
+
+
+ public PagedViewWidgetIcon(Context context) {
+ this(context, null);
+ }
+
+ public PagedViewWidgetIcon(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public PagedViewWidgetIcon(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PagedViewWidgetIcon,
+ defStyle, 0);
+ mCheckedBlurColor = a.getColor(R.styleable.PagedViewWidgetIcon_checkedBlurColor, 0);
+ mCheckedOutlineColor = a.getColor(R.styleable.PagedViewWidgetIcon_checkedOutlineColor, 0);
+ mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
+ mErasePaint.setFilterBitmap(true);
+ a.recycle();
+
+ if (sHolographicOutlineHelper == null) {
+ sHolographicOutlineHelper = new HolographicOutlineHelper();
+ }
+
+ setWillNotDraw(false);
+ }
+
+ public void invalidateCheckedImage() {
+ if (mCheckedOutline != null) {
+ mCheckedOutline.recycle();
+ mCheckedOutline = null;
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ // Draw the view itself
+ if (mIsHolographicUpdatePass) {
+ canvas.save();
+ final float alpha = getAlpha();
+ super.setAlpha(1.0f);
+ super.onDraw(canvas);
+ super.setAlpha(alpha);
+ canvas.restore();
+ } else {
+ if (mAlpha > 0) {
+ super.onDraw(canvas);
+ }
+ }
+
+ // Draw the holographic checked overlay if necessary
+ if (!mIsHolographicUpdatePass) {
+ if (mCheckedOutline != null) {
+ mPaint.setAlpha(255);
+ canvas.drawBitmap(mCheckedOutline, 0, 0, mPaint);
+ }
+ }
+ }
+
+ @Override
+ public boolean isChecked() {
+ return mIsChecked;
+ }
+
+ @Override
+ public void setChecked(boolean checked) {
+ if (mIsChecked != checked) {
+ mIsChecked = checked;
+
+ if (mIsChecked) {
+ // set a flag to indicate that we are going to draw the view at full alpha
+ mIsHolographicUpdatePass = true;
+ final int width = getMeasuredWidth();
+ final int height = getMeasuredHeight();
+ mCheckedOutline = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ mHolographicOutlineCanvas = new Canvas(mCheckedOutline);
+ mHolographicOutlineCanvas.concat(getMatrix());
+ draw(mHolographicOutlineCanvas);
+ sHolographicOutlineHelper.applyExpensiveOutlineWithBlur(mCheckedOutline,
+ mHolographicOutlineCanvas, mCheckedBlurColor, mCheckedOutlineColor);
+
+ // Unlike PagedViewIcon, we can't seem to properly set the clip rect for all the
+ // children to respect when drawing... so for now, we erase over those parts in the
+ // checked highlight image
+ mHolographicOutlineCanvas.drawRect(0, findViewById(R.id.divider).getTop(),
+ width, height, mErasePaint);
+
+ mIsHolographicUpdatePass = false;
+ mHolographicOutlineCanvas = null;
+ } else {
+ invalidateCheckedImage();
+ }
+
+ invalidate();
+ }
+ }
+
+ @Override
+ public void toggle() {
+ setChecked(!mIsChecked);
+ }
+}
diff --git a/src/com/android/launcher2/PendingAddItemInfo.java b/src/com/android/launcher2/PendingAddItemInfo.java
new file mode 100644
index 000000000..23e23308d
--- /dev/null
+++ b/src/com/android/launcher2/PendingAddItemInfo.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import android.content.ComponentName;
+
+/**
+ * We pass this object with a drag from the customization tray
+ */
+class PendingAddItemInfo extends ItemInfo {
+ /**
+ * The component that will be created.
+ */
+ ComponentName componentName;
+} \ No newline at end of file
diff --git a/src/com/android/launcher2/ShortcutInfo.java b/src/com/android/launcher2/ShortcutInfo.java
index 2c5aec436..72f2d518c 100644
--- a/src/com/android/launcher2/ShortcutInfo.java
+++ b/src/com/android/launcher2/ShortcutInfo.java
@@ -16,16 +16,14 @@
package com.android.launcher2;
+import java.util.ArrayList;
+
import android.content.ComponentName;
import android.content.ContentValues;
-import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
import android.util.Log;
-import java.util.ArrayList;
-
/**
* Represents a launchable icon on the workspaces and in folders.
*/
diff --git a/src/com/android/launcher2/ShortcutsAdapter.java b/src/com/android/launcher2/ShortcutsAdapter.java
index 19c3af0a8..93c500a44 100644
--- a/src/com/android/launcher2/ShortcutsAdapter.java
+++ b/src/com/android/launcher2/ShortcutsAdapter.java
@@ -16,16 +16,15 @@
package com.android.launcher2;
+import java.util.ArrayList;
+
import android.content.Context;
-import android.content.pm.PackageManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
-import java.util.ArrayList;
-
import com.android.launcher.R;
/**
diff --git a/src/com/android/launcher2/SmoothPagedView.java b/src/com/android/launcher2/SmoothPagedView.java
new file mode 100644
index 000000000..56037ffb8
--- /dev/null
+++ b/src/com/android/launcher2/SmoothPagedView.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.animation.Interpolator;
+import android.widget.Scroller;
+
+
+public abstract class SmoothPagedView extends PagedView {
+ private static final float SMOOTHING_SPEED = 0.75f;
+ private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED));
+
+ private float mBaseLineFlingVelocity;
+ private float mFlingVelocityInfluence;
+
+ static final int OVERSHOOT_MODE = 0;
+ static final int QUINTIC_MODE = 1;
+
+ int mScrollMode;
+
+ private Interpolator mScrollInterpolator;
+
+ private static class WorkspaceOvershootInterpolator implements Interpolator {
+ private static final float DEFAULT_TENSION = 1.3f;
+ private float mTension;
+
+ public WorkspaceOvershootInterpolator() {
+ mTension = DEFAULT_TENSION;
+ }
+
+ public void setDistance(int distance) {
+ mTension = distance > 0 ? DEFAULT_TENSION / distance : DEFAULT_TENSION;
+ }
+
+ public void disableSettle() {
+ mTension = 0.f;
+ }
+
+ 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;
+ }
+ }
+
+ private static class QuinticInterpolator implements Interpolator {
+ public QuinticInterpolator() {
+ }
+
+ public float getInterpolation(float t) {
+ t -= 1.0f;
+ return t*t*t*t*t + 1;
+ }
+ }
+
+ /**
+ * Used to inflate the Workspace from XML.
+ *
+ * @param context The application's context.
+ * @param attrs The attributes set containing the Workspace's customization values.
+ */
+ public SmoothPagedView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ /**
+ * Used to inflate the Workspace from XML.
+ *
+ * @param context The application's context.
+ * @param attrs The attributes set containing the Workspace's customization values.
+ * @param defStyle Unused.
+ */
+ public SmoothPagedView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ mUsePagingTouchSlop = false;
+
+ // This means that we'll take care of updating the scroll parameter ourselves (we do it
+ // in computeScroll)
+ mDeferScrollUpdate = true;
+ }
+
+ protected int getScrollMode() {
+ return OVERSHOOT_MODE;
+ }
+
+ /**
+ * Initializes various states for this workspace.
+ */
+ @Override
+ protected void init() {
+ super.init();
+
+ mScrollMode = getScrollMode();
+ if (mScrollMode == QUINTIC_MODE) {
+ mBaseLineFlingVelocity = 700.0f;
+ mFlingVelocityInfluence = 0.8f;
+ mScrollInterpolator = new QuinticInterpolator();
+ } else { // QUINTIC_MODE
+ mBaseLineFlingVelocity = 2500.0f;
+ mFlingVelocityInfluence = 0.4f;
+ mScrollInterpolator = new WorkspaceOvershootInterpolator();
+ }
+ mScroller = new Scroller(getContext(), mScrollInterpolator);
+ }
+
+ @Override
+ protected void snapToDestination() {
+ snapToPageWithVelocity(getPageNearestToCenterOfScreen(), 0);
+ }
+
+ @Override
+ protected void snapToPageWithVelocity(int whichPage, int velocity) {
+ snapToPageWithVelocity(whichPage, 0, true);
+ }
+
+ void snapToPageWithVelocity(int whichPage, int velocity, boolean settle) {
+ // if (!mScroller.isFinished()) return;
+
+ whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
+
+ final int screenDelta = Math.max(1, Math.abs(whichPage - mCurrentPage));
+ final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
+ final int delta = newX - mScrollX;
+ int duration;
+ if (mScrollMode == OVERSHOOT_MODE) {
+ duration = (screenDelta + 1) * 100;
+ } else { // QUINTIC_MODE
+ duration = Math.round(Math.abs(delta) * 0.6f);
+ }
+
+ if (!mScroller.isFinished()) {
+ mScroller.abortAnimation();
+ }
+
+ if (mScrollMode == OVERSHOOT_MODE) {
+ if (settle) {
+ ((WorkspaceOvershootInterpolator) mScrollInterpolator).setDistance(screenDelta);
+ } else {
+ ((WorkspaceOvershootInterpolator) mScrollInterpolator).disableSettle();
+ }
+ }
+
+ velocity = Math.abs(velocity);
+ if (velocity > 0) {
+ duration += (duration / (velocity / mBaseLineFlingVelocity)) * mFlingVelocityInfluence;
+ } else {
+ duration += 100;
+ }
+
+ snapToPage(whichPage, delta, duration);
+ }
+
+ @Override
+ protected void snapToPage(int whichPage) {
+ snapToPageWithVelocity(whichPage, 0, false);
+ }
+
+ @Override
+ public void computeScroll() {
+ boolean scrollComputed = computeScrollHelper();
+
+ if (!scrollComputed && mTouchState == TOUCH_STATE_SCROLLING) {
+ final float now = System.nanoTime() / NANOTIME_DIV;
+ final float e = (float) Math.exp((now - mSmoothingTime) / SMOOTHING_CONSTANT);
+ final float dx = mTouchX - mScrollX;
+ mScrollX += dx * e;
+ mSmoothingTime = now;
+
+ // Keep generating points as long as we're more than 1px away from the target
+ if (dx > 1.f || dx < -1.f) {
+ invalidate();
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher2/SymmetricalLinearTween.java b/src/com/android/launcher2/SymmetricalLinearTween.java
index 2e0ed8f03..da02242c8 100644
--- a/src/com/android/launcher2/SymmetricalLinearTween.java
+++ b/src/com/android/launcher2/SymmetricalLinearTween.java
@@ -17,9 +17,7 @@
package com.android.launcher2;
import android.os.Handler;
-import android.os.Message;
import android.os.SystemClock;
-import android.util.Log;
/**
* Provides an animation between 0.0f and 1.0f over a given duration.
diff --git a/src/com/android/launcher2/UserFolder.java b/src/com/android/launcher2/UserFolder.java
index d49c27aea..d6799f75e 100644
--- a/src/com/android/launcher2/UserFolder.java
+++ b/src/com/android/launcher2/UserFolder.java
@@ -3,10 +3,8 @@ package com.android.launcher2;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.ArrayAdapter;
import com.android.launcher.R;
@@ -40,11 +38,6 @@ public class UserFolder extends Folder implements DropTarget {
itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT)
&& item.container != mInfo.id;
}
-
- public Rect estimateDropLocation(DragSource source, int x, int y, int xOffset, int yOffset,
- DragView dragView, Object dragInfo, Rect recycle) {
- return null;
- }
public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
DragView dragView, Object dragInfo) {
@@ -79,6 +72,10 @@ public class UserFolder extends Folder implements DropTarget {
}
}
+ public boolean isDropEnabled() {
+ return true;
+ }
+
void bind(FolderInfo info) {
super.bind(info);
setContentAdapter(new ShortcutsAdapter(mContext, ((UserFolderInfo) info).contents));
@@ -91,4 +88,10 @@ public class UserFolder extends Folder implements DropTarget {
super.onOpen();
requestFocus();
}
+
+ @Override
+ public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
+ DragView dragView, Object dragInfo) {
+ return null;
+ }
}
diff --git a/src/com/android/launcher2/Utilities.java b/src/com/android/launcher2/Utilities.java
index 757e48e30..c67ff99de 100644
--- a/src/com/android/launcher2/Utilities.java
+++ b/src/com/android/launcher2/Utilities.java
@@ -16,9 +16,8 @@
package com.android.launcher2;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.PaintDrawable;
+import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
@@ -26,19 +25,19 @@ import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
-import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.TableMaskFilter;
import android.graphics.Typeface;
-import android.text.Layout.Alignment;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.PaintDrawable;
import android.text.StaticLayout;
import android.text.TextPaint;
+import android.text.Layout.Alignment;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.content.res.Resources;
-import android.content.Context;
import com.android.launcher.R;
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 7761cb348..160283bfc 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -16,9 +16,13 @@
package com.android.launcher2;
-import java.util.ArrayList;
-import java.util.HashSet;
+import com.android.launcher.R;
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.Animator.AnimatorListener;
import android.app.WallpaperManager;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
@@ -27,138 +31,112 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
+import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.Camera;
import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.IBinder;
-import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
-import android.view.VelocityTracker;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.animation.Interpolator;
-import android.widget.Scroller;
import android.widget.TextView;
-import com.android.launcher.R;
+import java.util.ArrayList;
+import java.util.HashSet;
/**
- * The workspace is a wide area with a wallpaper and a finite number of screens. Each
- * screen contains a number of icons, folders or widgets the user can interact with.
- * A workspace is meant to be used with a fixed width only.
+ * The workspace is a wide area with a wallpaper and a finite number of pages.
+ * Each page contains a number of icons, folders or widgets the user can
+ * interact with. A workspace is meant to be used with a fixed width only.
*/
-public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller {
+public class Workspace extends SmoothPagedView
+ implements DropTarget, DragSource, DragScroller, View.OnTouchListener {
@SuppressWarnings({"UnusedDeclaration"})
private static final String TAG = "Launcher.Workspace";
- private static final int INVALID_SCREEN = -1;
-
- /**
- * The velocity at which a fling gesture will cause us to snap to the next screen
- */
- private static final int SNAP_VELOCITY = 600;
+
+ // This is how much the workspace shrinks when we enter all apps or
+ // customization mode
+ private static final float SHRINK_FACTOR = 0.16f;
+
+ // Y rotation to apply to the workspace screens
+ private static final float WORKSPACE_ROTATION = 12.5f;
+
+ // These are extra scale factors to apply to the mini home screens
+ // so as to achieve the desired transform
+ private static final float EXTRA_SCALE_FACTOR_0 = 0.97f;
+ private static final float EXTRA_SCALE_FACTOR_1 = 1.0f;
+ private static final float EXTRA_SCALE_FACTOR_2 = 1.08f;
+
+ private static final int BACKGROUND_FADE_OUT_DELAY = 300;
+ private static final int BACKGROUND_FADE_OUT_DURATION = 300;
+ private static final int BACKGROUND_FADE_IN_DURATION = 100;
+
+ static final int SCROLL_RIGHT = 0;
+ static final int SCROLL_LEFT = 1;
+
+ // These animators are used to fade the
+ private ObjectAnimator<Float> mBackgroundFadeIn;
+ private ObjectAnimator<Float> mBackgroundFadeOut;
+ private float mBackgroundAlpha = 0;
+
+ private enum ShrinkPosition { SHRINK_TO_TOP, SHRINK_TO_MIDDLE, SHRINK_TO_BOTTOM };
private final WallpaperManager mWallpaperManager;
-
- private int mDefaultScreen;
- private boolean mFirstLayout = true;
+ private int mDefaultPage;
- private int mCurrentScreen;
- private int mNextScreen = INVALID_SCREEN;
- private Scroller mScroller;
- private VelocityTracker mVelocityTracker;
+ private boolean mWaitingToShrinkToBottom = false;
/**
* CellInfo for the cell that is currently being dragged
*/
private CellLayout.CellInfo mDragInfo;
-
+
/**
* Target drop area calculated during last acceptDrop call.
*/
private int[] mTargetCell = null;
- private float mLastMotionX;
- private float mLastMotionY;
-
- private final static int TOUCH_STATE_REST = 0;
- private final static int TOUCH_STATE_SCROLLING = 1;
-
- private int mTouchState = TOUCH_STATE_REST;
-
- private OnLongClickListener mLongClickListener;
+ /**
+ * The CellLayout that is currently being dragged over
+ */
+ private CellLayout mDragTargetLayout = null;
private Launcher mLauncher;
private IconCache mIconCache;
private DragController mDragController;
-
- /**
- * Cache of vacant cells, used during drag events and invalidated as needed.
- */
- private CellLayout.CellInfo mVacantCache = null;
-
+
private int[] mTempCell = new int[2];
private int[] mTempEstimate = new int[2];
+ private float[] mTempOriginXY = new float[2];
+ private float[] mTempDragCoordinates = new float[2];
+ private float[] mTempDragBottomRightCoordinates = new float[2];
+ private Matrix mTempInverseMatrix = new Matrix();
- private boolean mAllowLongPress = true;
-
- private int mTouchSlop;
- private int mMaximumVelocity;
-
- private static final int INVALID_POINTER = -1;
+ private static final int DEFAULT_CELL_COUNT_X = 4;
+ private static final int DEFAULT_CELL_COUNT_Y = 4;
- private int mActivePointerId = INVALID_POINTER;
-
private Drawable mPreviousIndicator;
private Drawable mNextIndicator;
-
- private static final float NANOTIME_DIV = 1000000000.0f;
- private static final float SMOOTHING_SPEED = 0.75f;
- private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED));
- private float mSmoothingTime;
- private float mTouchX;
-
- private WorkspaceOvershootInterpolator mScrollInterpolator;
-
- private static final float BASELINE_FLING_VELOCITY = 2500.f;
- private static final float FLING_VELOCITY_INFLUENCE = 0.4f;
-
- private static class WorkspaceOvershootInterpolator implements Interpolator {
- private static final float DEFAULT_TENSION = 1.3f;
- private float mTension;
-
- public WorkspaceOvershootInterpolator() {
- mTension = DEFAULT_TENSION;
- }
-
- public void setDistance(int distance) {
- mTension = distance > 0 ? DEFAULT_TENSION / distance : DEFAULT_TENSION;
- }
- public void disableSettle() {
- mTension = 0.f;
- }
+ // State variable that indicated whether the pages are small (ie when you're
+ // in all apps or customize mode)
+ private boolean mIsSmall;
+ private AnimatorListener mUnshrinkAnimationListener;
- 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;
- }
- }
-
/**
* Used to inflate the Workspace from XML.
*
* @param context The application's context.
- * @param attrs The attribtues set containing the Workspace's customization values.
+ * @param attrs The attributes set containing the Workspace's customization values.
*/
public Workspace(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -168,37 +146,61 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
* Used to inflate the Workspace from XML.
*
* @param context The application's context.
- * @param attrs The attribtues set containing the Workspace's customization values.
+ * @param attrs The attributes set containing the Workspace's customization values.
* @param defStyle Unused.
*/
public Workspace(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ mContentIsRefreshable = false;
+
+ if (!LauncherApplication.isScreenXLarge()) {
+ mFadeInAdjacentScreens = false;
+ }
mWallpaperManager = WallpaperManager.getInstance(context);
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Workspace, defStyle, 0);
- mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1);
+
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.Workspace, defStyle, 0);
+ int cellCountX = a.getInt(R.styleable.Workspace_cellCountX, DEFAULT_CELL_COUNT_X);
+ int cellCountY = a.getInt(R.styleable.Workspace_cellCountY, DEFAULT_CELL_COUNT_Y);
+ mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
a.recycle();
+ LauncherModel.updateWorkspaceLayoutCells(cellCountX, cellCountY);
setHapticFeedbackEnabled(false);
+
initWorkspace();
}
/**
* Initializes various states for this workspace.
*/
- private void initWorkspace() {
+ protected void initWorkspace() {
Context context = getContext();
- mScrollInterpolator = new WorkspaceOvershootInterpolator();
- mScroller = new Scroller(context, mScrollInterpolator);
- mCurrentScreen = mDefaultScreen;
- Launcher.setScreen(mCurrentScreen);
+ mCurrentPage = mDefaultPage;
+ Launcher.setScreen(mCurrentPage);
LauncherApplication app = (LauncherApplication)context.getApplicationContext();
mIconCache = app.getIconCache();
- final ViewConfiguration configuration = ViewConfiguration.get(getContext());
- mTouchSlop = configuration.getScaledTouchSlop();
- mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ mUnshrinkAnimationListener = new AnimatorListener() {
+ public void onAnimationStart(Animator animation) {}
+ public void onAnimationEnd(Animator animation) {
+ mIsSmall = false;
+ }
+ public void onAnimationCancel(Animator animation) {}
+ public void onAnimationRepeat(Animator animation) {}
+ };
+
+ mSnapVelocity = 600;
+ }
+
+ @Override
+ protected int getScrollMode() {
+ if (LauncherApplication.isScreenXLarge()) {
+ return SmoothPagedView.QUINTIC_MODE;
+ } else {
+ return SmoothPagedView.OVERSHOOT_MODE;
+ }
}
@Override
@@ -245,30 +247,32 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
* @return The open folder on the current screen, or null if there is none
*/
Folder getOpenFolder() {
- CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen);
- int count = currentScreen.getChildCount();
+ CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
+ int count = currentPage.getChildCount();
for (int i = 0; i < count; i++) {
- View child = currentScreen.getChildAt(i);
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
- if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
- return (Folder) child;
+ View child = currentPage.getChildAt(i);
+ if (child instanceof Folder) {
+ Folder folder = (Folder) child;
+ if (folder.getInfo().opened)
+ return folder;
}
}
return null;
}
ArrayList<Folder> getOpenFolders() {
- final int screens = getChildCount();
- ArrayList<Folder> folders = new ArrayList<Folder>(screens);
+ final int screenCount = getChildCount();
+ ArrayList<Folder> folders = new ArrayList<Folder>(screenCount);
- for (int screen = 0; screen < screens; screen++) {
- CellLayout currentScreen = (CellLayout) getChildAt(screen);
- int count = currentScreen.getChildCount();
+ for (int screen = 0; screen < screenCount; screen++) {
+ CellLayout currentPage = (CellLayout) getChildAt(screen);
+ int count = currentPage.getChildCount();
for (int i = 0; i < count; i++) {
- View child = currentScreen.getChildAt(i);
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
- if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
- folders.add((Folder) child);
+ View child = currentPage.getChildAt(i);
+ if (child instanceof Folder) {
+ Folder folder = (Folder) child;
+ if (folder.getInfo().opened)
+ folders.add(folder);
break;
}
}
@@ -277,62 +281,19 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
return folders;
}
- boolean isDefaultScreenShowing() {
- return mCurrentScreen == mDefaultScreen;
- }
-
- /**
- * Returns the index of the currently displayed screen.
- *
- * @return The index of the currently displayed screen.
- */
- int getCurrentScreen() {
- return mCurrentScreen;
+ boolean isDefaultPageShowing() {
+ return mCurrentPage == mDefaultPage;
}
/**
* Sets the current screen.
*
- * @param currentScreen
+ * @param currentPage
*/
- void setCurrentScreen(int currentScreen) {
- if (!mScroller.isFinished()) mScroller.abortAnimation();
- clearVacantCache();
- mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1));
- mPreviousIndicator.setLevel(mCurrentScreen);
- mNextIndicator.setLevel(mCurrentScreen);
- scrollTo(mCurrentScreen * getWidth(), 0);
- updateWallpaperOffset();
- invalidate();
- }
-
- /**
- * Adds the specified child in the current screen. The position and dimension of
- * the child are defined by x, y, spanX and spanY.
- *
- * @param child The child to add in one of the workspace's screens.
- * @param x The X position of the child in the screen's grid.
- * @param y The Y position of the child in the screen's grid.
- * @param spanX The number of cells spanned horizontally by the child.
- * @param spanY The number of cells spanned vertically by the child.
- */
- void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) {
- addInScreen(child, mCurrentScreen, x, y, spanX, spanY, false);
- }
-
- /**
- * Adds the specified child in the current screen. The position and dimension of
- * the child are defined by x, y, spanX and spanY.
- *
- * @param child The child to add in one of the workspace's screens.
- * @param x The X position of the child in the screen's grid.
- * @param y The Y position of the child in the screen's grid.
- * @param spanX The number of cells spanned horizontally by the child.
- * @param spanY The number of cells spanned vertically by the child.
- * @param insert When true, the child is inserted at the beginning of the children list.
- */
- void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) {
- addInScreen(child, mCurrentScreen, x, y, spanX, spanY, insert);
+ @Override
+ void setCurrentPage(int currentPage) {
+ super.setCurrentPage(currentPage);
+ updateWallpaperOffset(mScrollX);
}
/**
@@ -350,6 +311,10 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
addInScreen(child, screen, x, y, spanX, spanY, false);
}
+ void addInFullScreen(View child, int screen) {
+ addInScreen(child, screen, 0, 0, -1, -1);
+ }
+
/**
* Adds the specified child in the specified screen. The position and dimension of
* the child are defined by x, y, spanX and spanY.
@@ -369,8 +334,6 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
return;
}
- clearVacantCache();
-
final CellLayout group = (CellLayout) getChildAt(screen);
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
if (lp == null) {
@@ -381,124 +344,202 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
lp.cellHSpan = spanX;
lp.cellVSpan = spanY;
}
- group.addView(child, insert ? 0 : -1, lp);
+
+ // Get the canonical child id to uniquely represent this view in this screen
+ int childId = LauncherModel.getCellLayoutChildId(child.getId(), screen, x, y, spanX, spanY);
+ if (!group.addViewToCellLayout(child, insert ? 0 : -1, childId, lp)) {
+ // TODO: This branch occurs when the workspace is adding views
+ // outside of the defined grid
+ // maybe we should be deleting these items from the LauncherModel?
+ Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
+ }
+
if (!(child instanceof Folder)) {
child.setHapticFeedbackEnabled(false);
child.setOnLongClickListener(mLongClickListener);
}
if (child instanceof DropTarget) {
- mDragController.addDropTarget((DropTarget)child);
+ mDragController.addDropTarget((DropTarget) child);
}
}
- CellLayout.CellInfo findAllVacantCells(boolean[] occupied) {
- CellLayout group = (CellLayout) getChildAt(mCurrentScreen);
- if (group != null) {
- return group.findAllVacantCells(occupied, null);
+ public boolean onTouch(View v, MotionEvent event) {
+ // this is an intercepted event being forwarded from a cell layout
+ if (mIsSmall) {
+ mLauncher.onWorkspaceClick((CellLayout) v);
+ return true;
}
- return null;
+ return false;
}
- private void clearVacantCache() {
- if (mVacantCache != null) {
- mVacantCache.clearVacantCells();
- mVacantCache = null;
+ @Override
+ public boolean dispatchUnhandledMove(View focused, int direction) {
+ if (mIsSmall) {
+ // when the home screens are shrunken, shouldn't allow side-scrolling
+ return false;
}
+ return super.dispatchUnhandledMove(focused, direction);
}
- /**
- * Registers the specified listener on each screen contained in this workspace.
- *
- * @param l The listener used to respond to long clicks.
- */
@Override
- public void setOnLongClickListener(OnLongClickListener l) {
- mLongClickListener = l;
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- getChildAt(i).setOnLongClickListener(l);
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (mIsSmall) {
+ // when the home screens are shrunken, shouldn't allow side-scrolling
+ return false;
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ protected void pageBeginMoving() {
+ if (mNextPage != INVALID_PAGE) {
+ // we're snapping to a particular screen
+ // there's an issue where the alpha of neighboring pages doesn't get updated
+ // if drawing cache is enabled on children-- we only use that on xlarge devices,
+ // so disable drawing cache in those cases
+ if (!LauncherApplication.isScreenXLarge()) {
+ enableChildrenCache(mCurrentPage, mNextPage);
+ }
+ } else {
+ // this is when user is actively dragging a particular screen, they might
+ // swipe it either left or right (but we won't advance by more than one screen)
+ if (!LauncherApplication.isScreenXLarge()) {
+ enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
+ }
}
+ showOutlines();
}
+ protected void pageEndMoving() {
+ if (!LauncherApplication.isScreenXLarge()) {
+ clearChildrenCache();
+ }
+ hideOutlines();
+ }
+
+ @Override
+ protected void notifyPageSwitchListener() {
+ super.notifyPageSwitchListener();
+
+ if (mPreviousIndicator != null) {
+ // if we know the next page, we show the indication for it right away; it looks
+ // weird if the indicators are lagging
+ int page = mNextPage;
+ if (page == INVALID_PAGE) {
+ page = mCurrentPage;
+ }
+ mPreviousIndicator.setLevel(page);
+ mNextIndicator.setLevel(page);
+ }
+ Launcher.setScreen(mCurrentPage);
+ };
+
private void updateWallpaperOffset() {
updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft));
}
private void updateWallpaperOffset(int scrollRange) {
- IBinder token = getWindowToken();
- if (token != null) {
- mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 );
- mWallpaperManager.setWallpaperOffsets(getWindowToken(),
- Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0);
+ final boolean isStaticWallpaper = (mWallpaperManager != null) &&
+ (mWallpaperManager.getWallpaperInfo() == null);
+ if (LauncherApplication.isScreenXLarge() && !isStaticWallpaper) {
+ IBinder token = getWindowToken();
+ if (token != null) {
+ mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 );
+ mWallpaperManager.setWallpaperOffsets(getWindowToken(),
+ Math.max(0.f, Math.min(mScrollX/(float)scrollRange, 1.f)), 0);
+ }
}
}
-
- @Override
- public void scrollTo(int x, int y) {
- super.scrollTo(x, y);
- mTouchX = x;
- mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
+
+ private float getScaleXForRotation(float degrees) {
+ return (float) (1.0f / Math.cos(Math.PI * degrees / 180.0f));
}
-
- @Override
- public void computeScroll() {
- if (mScroller.computeScrollOffset()) {
- mTouchX = mScrollX = mScroller.getCurrX();
- mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
- mScrollY = mScroller.getCurrY();
- updateWallpaperOffset();
- postInvalidate();
- } else if (mNextScreen != INVALID_SCREEN) {
- mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1));
- mPreviousIndicator.setLevel(mCurrentScreen);
- mNextIndicator.setLevel(mCurrentScreen);
- Launcher.setScreen(mCurrentScreen);
- mNextScreen = INVALID_SCREEN;
- clearChildrenCache();
- } else if (mTouchState == TOUCH_STATE_SCROLLING) {
- final float now = System.nanoTime() / NANOTIME_DIV;
- final float e = (float) Math.exp((now - mSmoothingTime) / SMOOTHING_CONSTANT);
- final float dx = mTouchX - mScrollX;
- mScrollX += dx * e;
- mSmoothingTime = now;
-
- // Keep generating points as long as we're more than 1px away from the target
- if (dx > 1.f || dx < -1.f) {
- updateWallpaperOffset();
- postInvalidate();
- }
+
+ public void showOutlines() {
+ if (mBackgroundFadeOut != null) mBackgroundFadeOut.cancel();
+ if (mBackgroundFadeIn != null) mBackgroundFadeIn.cancel();
+ mBackgroundFadeIn = new ObjectAnimator<Float>(BACKGROUND_FADE_IN_DURATION, this,
+ new PropertyValuesHolder<Float>("backgroundAlpha", 1.0f));
+ mBackgroundFadeIn.start();
+ }
+
+ public void hideOutlines() {
+ if (mBackgroundFadeIn != null) mBackgroundFadeIn.cancel();
+ if (mBackgroundFadeOut != null) mBackgroundFadeOut.cancel();
+ mBackgroundFadeOut = new ObjectAnimator<Float>(BACKGROUND_FADE_OUT_DURATION, this,
+ new PropertyValuesHolder<Float>("backgroundAlpha", 0.0f));
+ mBackgroundFadeOut.setStartDelay(BACKGROUND_FADE_OUT_DELAY);
+ mBackgroundFadeOut.start();
+ }
+
+ public void setBackgroundAlpha(float alpha) {
+ mBackgroundAlpha = alpha;
+ for (int i = 0; i < getChildCount(); i++) {
+ CellLayout cl = (CellLayout) getChildAt(i);
+ cl.setBackgroundAlpha(alpha);
}
}
+ public float getBackgroundAlpha() {
+ return mBackgroundAlpha;
+ }
+
@Override
- protected void dispatchDraw(Canvas canvas) {
- boolean restore = false;
- int restoreCount = 0;
-
- // ViewGroup.dispatchDraw() supports many features we don't need:
- // clip to padding, layout animation, animation listener, disappearing
- // children, etc. The following implementation attempts to fast-track
- // the drawing dispatch by drawing only what we know needs to be drawn.
-
- boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN;
- // If we are not scrolling or flinging, draw only the current screen
- if (fastDraw) {
- drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime());
- } else {
- final long drawingTime = getDrawingTime();
- final float scrollPos = (float) mScrollX / getWidth();
- final int leftScreen = (int) scrollPos;
- final int rightScreen = leftScreen + 1;
- if (leftScreen >= 0) {
- drawChild(canvas, getChildAt(leftScreen), drawingTime);
+ protected void screenScrolled(int screenCenter) {
+ View cur = getChildAt(mCurrentPage);
+ View toRight = getChildAt(mCurrentPage + 1);
+ View toLeft = getChildAt(mCurrentPage - 1);
+
+ for (int i = 0; i < mCurrentPage - 1; i++) {
+ View v = getChildAt(i);
+ if (v != null) {
+ v.setRotationY(WORKSPACE_ROTATION);
+ v.setScaleX(getScaleXForRotation(WORKSPACE_ROTATION));
}
- if (scrollPos != leftScreen && rightScreen < getChildCount()) {
- drawChild(canvas, getChildAt(rightScreen), drawingTime);
+ }
+ for (int i = mCurrentPage + 1; i < getChildCount(); i++) {
+ View v = getChildAt(i);
+ if (v != null) {
+ v.setRotationY(-WORKSPACE_ROTATION);
+ v.setScaleX(getScaleXForRotation(-WORKSPACE_ROTATION));
}
}
- if (restore) {
- canvas.restoreToCount(restoreCount);
+ int pageWidth = cur.getMeasuredWidth();
+ int delta = screenCenter - (mCurrentPage * pageWidth + pageWidth / 2 +
+ getRelativeChildOffset(0));
+
+ float scrollProgress = Math.abs(delta/(pageWidth*1.0f));
+ int scrollDirection = delta > 0 ? SCROLL_LEFT : SCROLL_RIGHT;
+
+ float rotation;
+
+ if (scrollDirection == SCROLL_RIGHT) {
+ rotation = -scrollProgress * WORKSPACE_ROTATION;
+ cur.setRotationY(rotation);
+ cur.setScaleX(getScaleXForRotation(rotation));
+ if (toLeft != null) {
+ rotation = WORKSPACE_ROTATION * (1 - scrollProgress);
+ toLeft.setRotationY(rotation);
+ toLeft.setScaleX(getScaleXForRotation(rotation));
+ }
+ if (toRight != null) {
+ toRight.setRotationY(-WORKSPACE_ROTATION);
+ toRight.setScaleX(getScaleXForRotation(WORKSPACE_ROTATION));
+ }
+ } else {
+ rotation = scrollProgress * WORKSPACE_ROTATION;
+ cur.setRotationY(rotation);
+ cur.setScaleX(getScaleXForRotation(rotation));
+
+ if (toRight != null) {
+ rotation = -WORKSPACE_ROTATION * (1 - scrollProgress);
+ toRight.setRotationY(rotation);
+ toRight.setScaleX(getScaleXForRotation(rotation));
+ }
+ if (toLeft != null) {
+ toLeft.setRotationY(WORKSPACE_ROTATION);
+ toLeft.setScaleX(getScaleXForRotation(WORKSPACE_ROTATION));
+ }
}
}
@@ -509,113 +550,60 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
}
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- final int width = MeasureSpec.getSize(widthMeasureSpec);
- final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- if (widthMode != MeasureSpec.EXACTLY) {
- throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
- }
-
- final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- if (heightMode != MeasureSpec.EXACTLY) {
- throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
- }
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
- // The children are given the same width and height as the workspace
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
+ // if shrinkToBottom() is called on initialization, it has to be deferred
+ // until after the first call to onLayout so that it has the correct width
+ if (mWaitingToShrinkToBottom) {
+ shrinkToBottom(false);
+ mWaitingToShrinkToBottom = false;
}
-
- if (mFirstLayout) {
- setHorizontalScrollBarEnabled(false);
- scrollTo(mCurrentScreen * width, 0);
- setHorizontalScrollBarEnabled(true);
- updateWallpaperOffset(width * (getChildCount() - 1));
- mFirstLayout = false;
+ if (LauncherApplication.isInPlaceRotationEnabled()) {
+ // When the device is rotated, the scroll position of the current screen
+ // needs to be refreshed
+ setCurrentPage(getCurrentPage());
}
}
@Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- int childLeft = 0;
+ protected void dispatchDraw(Canvas canvas) {
+ if (mIsSmall) {
+ // Draw all the workspaces if we're small
+ final int pageCount = getChildCount();
+ final long drawingTime = getDrawingTime();
+ for (int i = 0; i < pageCount; i++) {
+ final View page = (View) getChildAt(i);
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- final View child = getChildAt(i);
- if (child.getVisibility() != View.GONE) {
- final int childWidth = child.getMeasuredWidth();
- child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
- childLeft += childWidth;
+ drawChild(canvas, page, drawingTime);
}
+ } else {
+ super.dispatchDraw(canvas);
}
}
@Override
- public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
- int screen = indexOfChild(child);
- if (screen != mCurrentScreen || !mScroller.isFinished()) {
- snapToScreen(screen);
- return true;
- }
- return false;
- }
-
- @Override
protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
if (!mLauncher.isAllAppsVisible()) {
final Folder openFolder = getOpenFolder();
if (openFolder != null) {
return openFolder.requestFocus(direction, previouslyFocusedRect);
} else {
- int focusableScreen;
- if (mNextScreen != INVALID_SCREEN) {
- focusableScreen = mNextScreen;
- } else {
- focusableScreen = mCurrentScreen;
- }
- getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect);
+ return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
}
}
return false;
}
@Override
- public boolean dispatchUnhandledMove(View focused, int direction) {
- if (direction == View.FOCUS_LEFT) {
- if (getCurrentScreen() > 0) {
- snapToScreen(getCurrentScreen() - 1);
- return true;
- }
- } else if (direction == View.FOCUS_RIGHT) {
- if (getCurrentScreen() < getChildCount() - 1) {
- snapToScreen(getCurrentScreen() + 1);
- return true;
- }
- }
- return super.dispatchUnhandledMove(focused, direction);
- }
-
- @Override
public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
if (!mLauncher.isAllAppsVisible()) {
final Folder openFolder = getOpenFolder();
- if (openFolder == null) {
- getChildAt(mCurrentScreen).addFocusables(views, direction);
- if (direction == View.FOCUS_LEFT) {
- if (mCurrentScreen > 0) {
- getChildAt(mCurrentScreen - 1).addFocusables(views, direction);
- }
- } else if (direction == View.FOCUS_RIGHT){
- if (mCurrentScreen < getChildCount() - 1) {
- getChildAt(mCurrentScreen + 1).addFocusables(views, direction);
- }
- }
- } else {
+ if (openFolder != null) {
openFolder.addFocusables(views, direction);
+ } else {
+ super.addFocusables(views, direction, focusableMode);
}
}
}
@@ -623,203 +611,28 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- if (mLauncher.isAllAppsVisible()) {
+ // (In XLarge mode, the workspace is shrunken below all apps, and responds to taps
+ // ie when you click on a mini-screen, it zooms back to that screen)
+ if (!LauncherApplication.isScreenXLarge() && mLauncher.isAllAppsVisible()) {
return false;
}
}
return super.dispatchTouchEvent(ev);
}
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- final boolean allAppsVisible = mLauncher.isAllAppsVisible();
- if (allAppsVisible) {
- return false; // We don't want the events. Let them fall through to the all apps view.
- }
-
- /*
- * This method JUST determines whether we want to intercept the motion.
- * If we return true, onTouchEvent will be called and we do the actual
- * scrolling there.
- */
-
- /*
- * Shortcut the most recurring case: the user is in the dragging
- * state and he is moving his finger. We want to intercept this
- * motion.
- */
- final int action = ev.getAction();
- if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
- return true;
- }
-
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(ev);
-
- switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_MOVE: {
- /*
- * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
- * whether the user has moved far enough from his original down touch.
- */
-
- /*
- * Locally do absolute value. mLastMotionX is set to the y value
- * of the down event.
- */
- final int pointerIndex = ev.findPointerIndex(mActivePointerId);
- final float x = ev.getX(pointerIndex);
- final float y = ev.getY(pointerIndex);
- final int xDiff = (int) Math.abs(x - mLastMotionX);
- final int yDiff = (int) Math.abs(y - mLastMotionY);
-
- final int touchSlop = mTouchSlop;
- boolean xMoved = xDiff > touchSlop;
- boolean yMoved = yDiff > touchSlop;
-
- if (xMoved || yMoved) {
-
- if (xMoved) {
- // Scroll if the user moved far enough along the X axis
- mTouchState = TOUCH_STATE_SCROLLING;
- mLastMotionX = x;
- mTouchX = mScrollX;
- mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
- enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
- }
- // Either way, cancel any pending longpress
- if (mAllowLongPress) {
- mAllowLongPress = false;
- // Try canceling the long press. It could also have been scheduled
- // by a distant descendant, so use the mAllowLongPress flag to block
- // everything
- final View currentScreen = getChildAt(mCurrentScreen);
- currentScreen.cancelLongPress();
- }
- }
- break;
- }
-
- case MotionEvent.ACTION_DOWN: {
- final float x = ev.getX();
- final float y = ev.getY();
- // Remember location of down touch
- mLastMotionX = x;
- mLastMotionY = y;
- mActivePointerId = ev.getPointerId(0);
- mAllowLongPress = true;
-
- /*
- * If being flinged and user touches the screen, initiate drag;
- * otherwise don't. mScroller.isFinished should be false when
- * being flinged.
- */
- mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
- break;
- }
-
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
-
- if (mTouchState != TOUCH_STATE_SCROLLING) {
- final CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen);
- if (!currentScreen.lastDownOnOccupiedCell()) {
- getLocationOnScreen(mTempCell);
- // Send a tap to the wallpaper if the last down was on empty space
- final int pointerIndex = ev.findPointerIndex(mActivePointerId);
- mWallpaperManager.sendWallpaperCommand(getWindowToken(),
- "android.wallpaper.tap",
- mTempCell[0] + (int) ev.getX(pointerIndex),
- mTempCell[1] + (int) ev.getY(pointerIndex), 0, null);
- }
- }
-
- // Release the drag
- clearChildrenCache();
- mTouchState = TOUCH_STATE_REST;
- mActivePointerId = INVALID_POINTER;
- mAllowLongPress = false;
-
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
-
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- onSecondaryPointerUp(ev);
- break;
- }
-
- /*
- * The only time we want to intercept motion events is if we are in the
- * drag mode.
- */
- return mTouchState != TOUCH_STATE_REST;
- }
-
- private void onSecondaryPointerUp(MotionEvent ev) {
- final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
- MotionEvent.ACTION_POINTER_INDEX_SHIFT;
- final int pointerId = ev.getPointerId(pointerIndex);
- if (pointerId == mActivePointerId) {
- // This was our active pointer going up. Choose a new
- // active pointer and adjust accordingly.
- // TODO: Make this decision more intelligent.
- final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
- mLastMotionX = ev.getX(newPointerIndex);
- mLastMotionY = ev.getY(newPointerIndex);
- mActivePointerId = ev.getPointerId(newPointerIndex);
- if (mVelocityTracker != null) {
- mVelocityTracker.clear();
- }
- }
- }
-
- /**
- * If one of our descendant views decides that it could be focused now, only
- * pass that along if it's on the current screen.
- *
- * This happens when live folders requery, and if they're off screen, they
- * end up calling requestFocus, which pulls it on screen.
- */
- @Override
- public void focusableViewAvailable(View focused) {
- View current = getChildAt(mCurrentScreen);
- View v = focused;
- while (true) {
- if (v == current) {
- super.focusableViewAvailable(focused);
- return;
- }
- if (v == this) {
- return;
- }
- ViewParent parent = v.getParent();
- if (parent instanceof View) {
- v = (View)v.getParent();
- } else {
- return;
- }
+ void enableChildrenCache(int fromPage, int toPage) {
+ if (fromPage > toPage) {
+ final int temp = fromPage;
+ fromPage = toPage;
+ toPage = temp;
}
- }
- void enableChildrenCache(int fromScreen, int toScreen) {
- if (fromScreen > toScreen) {
- final int temp = fromScreen;
- fromScreen = toScreen;
- toScreen = temp;
- }
-
- final int count = getChildCount();
+ final int screenCount = getChildCount();
- fromScreen = Math.max(fromScreen, 0);
- toScreen = Math.min(toScreen, count - 1);
+ fromPage = Math.max(fromPage, 0);
+ toPage = Math.min(toPage, screenCount - 1);
- for (int i = fromScreen; i <= toScreen; i++) {
+ for (int i = fromPage; i <= toPage; i++) {
final CellLayout layout = (CellLayout) getChildAt(i);
layout.setChildrenDrawnWithCacheEnabled(true);
layout.setChildrenDrawingCacheEnabled(true);
@@ -827,8 +640,8 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
}
void clearChildrenCache() {
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
+ final int screenCount = getChildCount();
+ for (int i = 0; i < screenCount; i++) {
final CellLayout layout = (CellLayout) getChildAt(i);
layout.setChildrenDrawnWithCacheEnabled(false);
}
@@ -836,371 +649,656 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
@Override
public boolean onTouchEvent(MotionEvent ev) {
-
if (mLauncher.isAllAppsVisible()) {
// Cancel any scrolling that is in progress.
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
- snapToScreen(mCurrentScreen);
+ snapToPage(mCurrentPage);
return false; // We don't want the events. Let them fall through to the all apps view.
}
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(ev);
+ return super.onTouchEvent(ev);
+ }
- final int action = ev.getAction();
+ public boolean isSmall() {
+ return mIsSmall;
+ }
- switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN:
- /*
- * If being flinged and user touches, stop the fling. isFinished
- * will be false if being flinged.
- */
- if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
- }
+ void shrinkToTop(boolean animated) {
+ shrink(ShrinkPosition.SHRINK_TO_TOP, animated);
+ }
- // Remember where the motion event started
- mLastMotionX = ev.getX();
- mActivePointerId = ev.getPointerId(0);
- if (mTouchState == TOUCH_STATE_SCROLLING) {
- enableChildrenCache(mCurrentScreen - 1, mCurrentScreen + 1);
- }
- break;
- case MotionEvent.ACTION_MOVE:
- if (mTouchState == TOUCH_STATE_SCROLLING) {
- // Scroll to follow the motion event
- final int pointerIndex = ev.findPointerIndex(mActivePointerId);
- final float x = ev.getX(pointerIndex);
- final float deltaX = mLastMotionX - x;
- mLastMotionX = x;
-
- if (deltaX < 0) {
- if (mTouchX > 0) {
- mTouchX += Math.max(-mTouchX, deltaX);
- mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
- invalidate();
- }
- } else if (deltaX > 0) {
- final float availableToScroll = getChildAt(getChildCount() - 1).getRight() -
- mTouchX - getWidth();
- if (availableToScroll > 0) {
- mTouchX += Math.min(availableToScroll, deltaX);
- mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
- invalidate();
- }
- } else {
- awakenScrollBars();
- }
- }
- break;
- case MotionEvent.ACTION_UP:
- if (mTouchState == TOUCH_STATE_SCROLLING) {
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- final int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId);
-
- final int screenWidth = getWidth();
- final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth;
- final float scrolledPos = (float) mScrollX / screenWidth;
-
- if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
- // Fling hard enough to move left.
- // Don't fling across more than one screen at a time.
- final int bound = scrolledPos < whichScreen ?
- mCurrentScreen - 1 : mCurrentScreen;
- snapToScreen(Math.min(whichScreen, bound), velocityX, true);
- } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) {
- // Fling hard enough to move right
- // Don't fling across more than one screen at a time.
- final int bound = scrolledPos > whichScreen ?
- mCurrentScreen + 1 : mCurrentScreen;
- snapToScreen(Math.max(whichScreen, bound), velocityX, true);
- } else {
- snapToScreen(whichScreen, 0, true);
- }
+ void shrinkToMiddle() {
+ shrink(ShrinkPosition.SHRINK_TO_MIDDLE, true);
+ }
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- }
- mTouchState = TOUCH_STATE_REST;
- mActivePointerId = INVALID_POINTER;
- break;
- case MotionEvent.ACTION_CANCEL:
- mTouchState = TOUCH_STATE_REST;
- mActivePointerId = INVALID_POINTER;
- break;
- case MotionEvent.ACTION_POINTER_UP:
- onSecondaryPointerUp(ev);
- break;
- }
+ void shrinkToBottom() {
+ shrinkToBottom(true);
+ }
- return true;
+ void shrinkToBottom(boolean animated) {
+ if (mFirstLayout) {
+ // (mFirstLayout == "first layout has not happened yet")
+ // if we get a call to shrink() as part of our initialization (for example, if
+ // Launcher is started in All Apps mode) then we need to wait for a layout call
+ // to get our width so we can layout the mini-screen views correctly
+ mWaitingToShrinkToBottom = true;
+ } else {
+ shrink(ShrinkPosition.SHRINK_TO_BOTTOM, animated);
+ }
}
-
- void snapToScreen(int whichScreen) {
- snapToScreen(whichScreen, 0, false);
+
+ private float getYScaleForScreen(int screen) {
+ int x = Math.abs(screen - 2);
+
+ // TODO: This should be generalized for use with arbitrary rotation angles.
+ switch(x) {
+ case 0: return EXTRA_SCALE_FACTOR_0;
+ case 1: return EXTRA_SCALE_FACTOR_1;
+ case 2: return EXTRA_SCALE_FACTOR_2;
+ }
+ return 1.0f;
}
- private void snapToScreen(int whichScreen, int velocity, boolean settle) {
- //if (!mScroller.isFinished()) return;
+ // we use this to shrink the workspace for the all apps view and the customize view
+ private void shrink(ShrinkPosition shrinkPosition, boolean animated) {
+ mIsSmall = true;
+ // we intercept and reject all touch events when we're small, so be sure to reset the state
+ mTouchState = TOUCH_STATE_REST;
+ mActivePointerId = INVALID_POINTER;
- whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
-
- clearVacantCache();
- enableChildrenCache(mCurrentScreen, whichScreen);
+ final Resources res = getResources();
+ final int screenWidth = getWidth();
+ final int screenHeight = getHeight();
- mNextScreen = whichScreen;
+ // Making the assumption that all pages have the same width as the 0th
+ final int pageWidth = getChildAt(0).getMeasuredWidth();
+ final int pageHeight = getChildAt(0).getMeasuredHeight();
- mPreviousIndicator.setLevel(mNextScreen);
- mNextIndicator.setLevel(mNextScreen);
+ final int scaledPageWidth = (int) (SHRINK_FACTOR * pageWidth);
+ final int scaledPageHeight = (int) (SHRINK_FACTOR * pageHeight);
+ final float extraScaledSpacing = res.getDimension(R.dimen.smallScreenExtraSpacing);
- View focusedChild = getFocusedChild();
- if (focusedChild != null && whichScreen != mCurrentScreen &&
- focusedChild == getChildAt(mCurrentScreen)) {
- focusedChild.clearFocus();
+ final int screenCount = getChildCount();
+ float totalWidth = screenCount * scaledPageWidth + (screenCount - 1) * extraScaledSpacing;
+
+ float newY = getResources().getDimension(R.dimen.smallScreenVerticalMargin);
+ if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM) {
+ newY = screenHeight - newY - scaledPageHeight;
+ } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_MIDDLE) {
+ newY = screenHeight / 2 - scaledPageHeight / 2;
}
-
- final int screenDelta = Math.max(1, Math.abs(whichScreen - mCurrentScreen));
- final int newX = whichScreen * getWidth();
- final int delta = newX - mScrollX;
- int duration = (screenDelta + 1) * 100;
- if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
+ // We animate all the screens to the centered position in workspace
+ // At the same time, the screens become greyed/dimmed
+
+ // newX is initialized to the left-most position of the centered screens
+ float newX = mScroller.getFinalX() + screenWidth / 2 - totalWidth / 2;
+
+ // We are going to scale about the center of the view, so we need to adjust the positions
+ // of the views accordingly
+ newX -= (pageWidth - scaledPageWidth) / 2.0f;
+ newY -= (pageHeight - scaledPageHeight) / 2.0f;
+ for (int i = 0; i < screenCount; i++) {
+ CellLayout cl = (CellLayout) getChildAt(i);
+
+ float rotation = (-i + 2) * WORKSPACE_ROTATION;
+ float rotationScaleX = (float) (1.0f / Math.cos(Math.PI * rotation / 180.0f));
+ float rotationScaleY = getYScaleForScreen(i);
+
+ if (animated) {
+ final int duration = res.getInteger(R.integer.config_workspaceShrinkTime);
+ new ObjectAnimator<Float>(duration, cl,
+ new PropertyValuesHolder<Float>("x", newX),
+ new PropertyValuesHolder<Float>("y", newY),
+ new PropertyValuesHolder<Float>("scaleX", SHRINK_FACTOR * rotationScaleX),
+ new PropertyValuesHolder<Float>("scaleY", SHRINK_FACTOR * rotationScaleY),
+ new PropertyValuesHolder<Float>("backgroundAlpha", 1.0f),
+ new PropertyValuesHolder<Float>("dimmableProgress", 1.0f),
+ new PropertyValuesHolder<Float>("alpha", 0.0f),
+ new PropertyValuesHolder<Float>("rotationY", rotation)).start();
+ } else {
+ cl.setX((int)newX);
+ cl.setY((int)newY);
+ cl.setScaleX(SHRINK_FACTOR * rotationScaleX);
+ cl.setScaleY(SHRINK_FACTOR * rotationScaleY);
+ cl.setBackgroundAlpha(1.0f);
+ cl.setDimmableProgress(1.0f);
+ cl.setAlpha(0.0f);
+ cl.setRotationY(rotation);
+ }
+ // increment newX for the next screen
+ newX += scaledPageWidth + extraScaledSpacing;
+ cl.setOnInterceptTouchListener(this);
}
-
- if (settle) {
- mScrollInterpolator.setDistance(screenDelta);
- } else {
- mScrollInterpolator.disableSettle();
+ setChildrenDrawnWithCacheEnabled(true);
+ }
+
+ // We call this when we trigger an unshrink by clicking on the CellLayout cl
+ public void unshrink(CellLayout clThatWasClicked) {
+ int newCurrentPage = mCurrentPage;
+ final int screenCount = getChildCount();
+ for (int i = 0; i < screenCount; i++) {
+ if (getChildAt(i) == clThatWasClicked) {
+ newCurrentPage = i;
+ }
}
-
- velocity = Math.abs(velocity);
- if (velocity > 0) {
- duration += (duration / (velocity / BASELINE_FLING_VELOCITY))
- * FLING_VELOCITY_INFLUENCE;
- } else {
- duration += 100;
+ unshrink(newCurrentPage);
+ }
+
+ private void unshrink(int newCurrentPage) {
+ if (mIsSmall) {
+ int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage);
+ int delta = newX - mScrollX;
+
+ final int screenCount = getChildCount();
+ for (int i = 0; i < screenCount; i++) {
+ CellLayout cl = (CellLayout) getChildAt(i);
+ cl.setX(cl.getX() + delta);
+ }
+ snapToPage(newCurrentPage);
+ unshrink();
+
+ setCurrentPage(newCurrentPage);
}
+ }
- awakenScrollBars(duration);
- mScroller.startScroll(mScrollX, 0, delta, 0, duration);
- invalidate();
+ void unshrink() {
+ unshrink(true);
+ }
+
+ void unshrink(boolean animated) {
+ if (mIsSmall) {
+ AnimatorSet s = new AnimatorSet();
+ final int screenCount = getChildCount();
+
+ final int duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
+ for (int i = 0; i < screenCount; i++) {
+ final CellLayout cl = (CellLayout)getChildAt(i);
+ float finalAlphaValue = (i == mCurrentPage) ? 1.0f : 0.0f;
+ float rotation = 0.0f;
+
+ if (i < mCurrentPage) {
+ rotation = WORKSPACE_ROTATION;
+ } else if (i > mCurrentPage) {
+ rotation = -WORKSPACE_ROTATION;
+ }
+
+ if (animated) {
+ s.playTogether(
+ new ObjectAnimator<Float>(duration, cl, "translationX", 0.0f),
+ new ObjectAnimator<Float>(duration, cl, "translationY", 0.0f),
+ new ObjectAnimator<Float>(duration, cl, "scaleX", 1.0f),
+ new ObjectAnimator<Float>(duration, cl, "scaleY", 1.0f),
+ new ObjectAnimator<Float>(duration, cl, "backgroundAlpha", 0.0f),
+ new ObjectAnimator<Float>(duration, cl, "alpha", finalAlphaValue),
+ new ObjectAnimator<Float>(duration, cl, "dimmableProgress", 0.0f),
+ new ObjectAnimator<Float>(duration, cl, "rotationY", rotation));
+ } else {
+ cl.setTranslationX(0.0f);
+ cl.setTranslationY(0.0f);
+ cl.setScaleX(1.0f);
+ cl.setScaleY(1.0f);
+ cl.setBackgroundAlpha(0.0f);
+ cl.setDimmableProgress(0.0f);
+ cl.setAlpha(finalAlphaValue);
+ cl.setRotationY(rotation);
+ }
+ }
+ s.addListener(mUnshrinkAnimationListener);
+ s.start();
+ }
}
void startDrag(CellLayout.CellInfo cellInfo) {
View child = cellInfo.cell;
-
+
// Make sure the drag was started by a long press as opposed to a long click.
if (!child.isInTouchMode()) {
return;
}
-
+
mDragInfo = cellInfo;
- mDragInfo.screen = mCurrentScreen;
-
- CellLayout current = ((CellLayout) getChildAt(mCurrentScreen));
+ mDragInfo.screen = mCurrentPage;
+
+ CellLayout current = ((CellLayout) getChildAt(mCurrentPage));
current.onDragChild(child);
mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
invalidate();
}
- @Override
- protected Parcelable onSaveInstanceState() {
- final SavedState state = new SavedState(super.onSaveInstanceState());
- state.currentScreen = mCurrentScreen;
- return state;
- }
-
- @Override
- protected void onRestoreInstanceState(Parcelable state) {
- SavedState savedState = (SavedState) state;
- super.onRestoreInstanceState(savedState.getSuperState());
- if (savedState.currentScreen != -1) {
- mCurrentScreen = savedState.currentScreen;
- Launcher.setScreen(mCurrentScreen);
- }
- }
+ void addApplicationShortcut(ShortcutInfo info, int screen, int cellX, int cellY,
+ boolean insertAtFirst, int intersectX, int intersectY) {
+ final CellLayout cellLayout = (CellLayout) getChildAt(screen);
+ View view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo) info);
- void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo) {
- addApplicationShortcut(info, cellInfo, false);
+ final int[] cellXY = new int[2];
+ cellLayout.findCellForSpanThatIntersects(cellXY, 1, 1, intersectX, intersectY);
+ addInScreen(view, screen, cellXY[0], cellXY[1], 1, 1, insertAtFirst);
+ LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
+ LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
+ cellXY[0], cellXY[1]);
}
- void addApplicationShortcut(ShortcutInfo info, CellLayout.CellInfo cellInfo,
- boolean insertAtFirst) {
- final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen);
- final int[] result = new int[2];
-
- layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result);
- onDropExternal(result[0], result[1], info, layout, insertAtFirst);
- }
public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
DragView dragView, Object dragInfo) {
- final CellLayout cellLayout = getCurrentDropLayout();
+ CellLayout cellLayout;
+ int originX = x - xOffset;
+ int originY = y - yOffset;
+ if (mIsSmall) {
+ cellLayout = findMatchingPageForDragOver(dragView, originX, originY);
+ if (cellLayout == null) {
+ // cancel the drag if we're not over a mini-screen at time of drop
+ // TODO: maybe add a nice fade here?
+ return;
+ }
+ // get originX and originY in the local coordinate system of the screen
+ mTempOriginXY[0] = originX;
+ mTempOriginXY[1] = originY;
+ mapPointGlobalToLocal(cellLayout, mTempOriginXY);
+ originX = (int)mTempOriginXY[0];
+ originY = (int)mTempOriginXY[1];
+ } else {
+ cellLayout = getCurrentDropLayout();
+ }
if (source != this) {
- onDropExternal(x - xOffset, y - yOffset, dragInfo, cellLayout);
+ onDropExternal(originX, originY, dragInfo, cellLayout);
} else {
// Move internally
if (mDragInfo != null) {
final View cell = mDragInfo.cell;
- int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
+ int index = mScroller.isFinished() ? mCurrentPage : mNextPage;
if (index != mDragInfo.screen) {
final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
originalCellLayout.removeView(cell);
- cellLayout.addView(cell);
+ addInScreen(cell, index, mDragInfo.cellX, mDragInfo.cellY,
+ mDragInfo.spanX, mDragInfo.spanY);
}
- mTargetCell = estimateDropCell(x - xOffset, y - yOffset,
- mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, mTargetCell);
- cellLayout.onDropChild(cell, mTargetCell);
+ mTargetCell = findNearestVacantArea(originX, originY,
+ mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout,
+ mTargetCell);
+ cellLayout.onDropChild(cell);
+
+ // update the item's position after drop
final ItemInfo info = (ItemInfo) cell.getTag();
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
+ cellLayout.onMove(cell, mTargetCell[0], mTargetCell[1]);
+ lp.cellX = mTargetCell[0];
+ lp.cellY = mTargetCell[1];
+
LauncherModel.moveItemInDatabase(mLauncher, info,
- LauncherSettings.Favorites.CONTAINER_DESKTOP, index, lp.cellX, lp.cellY);
+ LauncherSettings.Favorites.CONTAINER_DESKTOP, index,
+ lp.cellX, lp.cellY);
}
}
}
- public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
+ public void onDragEnter(DragSource source, int x, int y, int xOffset,
+ int yOffset, DragView dragView, Object dragInfo) {
+ }
+
+ public DropTarget getDropTargetDelegate(DragSource source, int x, int y, int xOffset, int yOffset,
DragView dragView, Object dragInfo) {
- clearVacantCache();
+
+ if (mIsSmall) {
+ // If we're shrunken, don't let anyone drag on folders/etc that are on the mini-screens
+ return null;
+ }
+ // We may need to delegate the drag to a child view. If a 1x1 item
+ // would land in a cell occupied by a DragTarget (e.g. a Folder),
+ // then drag events should be handled by that child.
+
+ ItemInfo item = (ItemInfo)dragInfo;
+ CellLayout currentLayout = getCurrentDropLayout();
+
+ int dragPointX, dragPointY;
+ if (item.spanX == 1 && item.spanY == 1) {
+ // For a 1x1, calculate the drop cell exactly as in onDragOver
+ dragPointX = x - xOffset;
+ dragPointY = y - yOffset;
+ } else {
+ // Otherwise, use the exact drag coordinates
+ dragPointX = x;
+ dragPointY = y;
+ }
+ dragPointX += mScrollX - currentLayout.getLeft();
+ dragPointY += mScrollY - currentLayout.getTop();
+
+ // If we are dragging over a cell that contains a DropTarget that will
+ // accept the drop, delegate to that DropTarget.
+ final int[] cellXY = mTempCell;
+ currentLayout.estimateDropCell(dragPointX, dragPointY, item.spanX, item.spanY, cellXY);
+ View child = currentLayout.getChildAt(cellXY[0], cellXY[1]);
+ if (child instanceof DropTarget) {
+ DropTarget target = (DropTarget)child;
+ if (target.acceptDrop(source, x, y, xOffset, yOffset, dragView, dragInfo)) {
+ return target;
+ }
+ }
+ return null;
+ }
+
+
+ private void mapPointGlobalToLocal(View v, float[] xy) {
+ xy[0] = xy[0] + mScrollX - v.getLeft();
+ xy[1] = xy[1] + mScrollY - v.getTop();
+ v.getMatrix().invert(mTempInverseMatrix);
+ mTempInverseMatrix.mapPoints(xy);
+ }
+
+ // xy = upper left corner of item being dragged
+ // bottomRightXy = lower right corner of item being dragged
+ // This method will see which mini-screen is most overlapped by the item being dragged, and
+ // return it. It will also transform the parameters xy and bottomRightXy into the local
+ // coordinate space of the returned screen
+ private CellLayout findMatchingPageForDragOver(DragView dragView, int originX, int originY) {
+ float x = originX + dragView.getScaledDragRegionXOffset();
+ float y = originY + dragView.getScaledDragRegionYOffset();
+ float right = x + dragView.getScaledDragRegionWidth();
+ float bottom = y + dragView.getScaledDragRegionHeight();
+
+ // We loop through all the screens (ie CellLayouts) and see which one overlaps the most
+ // with the item being dragged.
+ final int screenCount = getChildCount();
+ CellLayout bestMatchingScreen = null;
+ float smallestDistSoFar = Float.MAX_VALUE;
+ final float[] xy = mTempDragCoordinates;
+ final float[] bottomRightXy = mTempDragBottomRightCoordinates;
+ for (int i = 0; i < screenCount; i++) {
+ CellLayout cl = (CellLayout)getChildAt(i);
+ // Transform the coordinates of the item being dragged to the CellLayout's coordinates
+ float left = cl.getLeft();
+ float top = cl.getTop();
+ xy[0] = x + mScrollX - left;
+ xy[1] = y + mScrollY - top;
+
+ bottomRightXy[0] = right + mScrollX - left;
+ bottomRightXy[1] = bottom + mScrollY - top;
+
+ cl.getMatrix().invert(mTempInverseMatrix);
+ mTempInverseMatrix.mapPoints(xy);
+ mTempInverseMatrix.mapPoints(bottomRightXy);
+
+ float dragRegionX = xy[0];
+ float dragRegionY = xy[1];
+ float dragRegionRight = bottomRightXy[0];
+ float dragRegionBottom = bottomRightXy[1];
+ float dragRegionCenterX = (dragRegionX + dragRegionRight) / 2.0f;
+ float dragRegionCenterY = (dragRegionY + dragRegionBottom) / 2.0f;
+
+ // Find the overlapping region
+ float overlapLeft = Math.max(0f, dragRegionX);
+ float overlapTop = Math.max(0f, dragRegionY);
+ float overlapBottom = Math.min(cl.getHeight(), dragRegionBottom);
+ float overlapRight = Math.min(cl.getWidth(), dragRegionRight);
+ if (overlapRight >= 0 && overlapLeft <= cl.getWidth() &&
+ (overlapTop >= 0 && overlapBottom <= cl.getHeight())) {
+ // Calculate the distance between the two centers
+ float distX = dragRegionCenterX - cl.getWidth()/2;
+ float distY = dragRegionCenterY - cl.getHeight()/2;
+ float dist = distX * distX + distY * distY;
+
+ float overlap = (overlapRight - overlapLeft) * (overlapBottom - overlapTop);
+
+ // Calculate the closest overlapping region
+ if (overlap > 0 && dist < smallestDistSoFar) {
+ smallestDistSoFar = dist;
+ bestMatchingScreen = cl;
+ }
+ }
+ }
+
+ if (bestMatchingScreen != mDragTargetLayout) {
+ if (mDragTargetLayout != null) {
+ mDragTargetLayout.onDragExit();
+ }
+ mDragTargetLayout = bestMatchingScreen;
+ }
+ return bestMatchingScreen;
}
public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
DragView dragView, Object dragInfo) {
+ CellLayout currentLayout;
+ int originX = x - xOffset;
+ int originY = y - yOffset;
+ if (mIsSmall) {
+ currentLayout = findMatchingPageForDragOver(dragView, originX, originY);
+
+ if (currentLayout == null) {
+ return;
+ }
+
+ currentLayout.setHover(true);
+ // get originX and originY in the local coordinate system of the screen
+ mTempOriginXY[0] = originX;
+ mTempOriginXY[1] = originY;
+ mapPointGlobalToLocal(currentLayout, mTempOriginXY);
+ originX = (int)mTempOriginXY[0];
+ originY = (int)mTempOriginXY[1];
+ } else {
+ currentLayout = getCurrentDropLayout();
+ }
+
+ final ItemInfo item = (ItemInfo)dragInfo;
+
+ if (dragInfo instanceof LauncherAppWidgetInfo) {
+ LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo)dragInfo;
+
+ if (widgetInfo.spanX == -1) {
+ // Calculate the grid spans needed to fit this widget
+ int[] spans = currentLayout.rectToCell(widgetInfo.minWidth, widgetInfo.minHeight, null);
+ item.spanX = spans[0];
+ item.spanY = spans[1];
+ }
+ }
+
+ if (source != this) {
+ // This is a hack to fix the point used to determine which cell an icon from the all
+ // apps screen is over
+ if (item != null && item.spanX == 1 && currentLayout != null) {
+ int dragRegionLeft = (dragView.getWidth() - currentLayout.getCellWidth()) / 2;
+
+ originX += dragRegionLeft - dragView.getDragRegionLeft();
+ if (dragView.getDragRegionWidth() != currentLayout.getCellWidth()) {
+ dragView.setDragRegion(dragView.getDragRegionLeft(), dragView.getDragRegionTop(),
+ currentLayout.getCellWidth(), dragView.getDragRegionHeight());
+ }
+ }
+ }
+ if (currentLayout != mDragTargetLayout) {
+ if (mDragTargetLayout != null) {
+ mDragTargetLayout.onDragExit();
+ }
+ mDragTargetLayout = currentLayout;
+ }
+
+ // only visualize the drop locations for moving icons within the home screen on tablet
+ // on phone, we also visualize icons dragged in from All Apps
+ if ((!LauncherApplication.isScreenXLarge() || source == this)
+ && mDragTargetLayout != null) {
+ final View child = (mDragInfo == null) ? null : mDragInfo.cell;
+ int localOriginX = originX - (mDragTargetLayout.getLeft() - mScrollX);
+ int localOriginY = originY - (mDragTargetLayout.getTop() - mScrollY);
+ mDragTargetLayout.visualizeDropLocation(
+ child, localOriginX, localOriginY, item.spanX, item.spanY, child);
+ }
}
- public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,
- DragView dragView, Object dragInfo) {
- clearVacantCache();
+ public void onDragExit(DragSource source, int x, int y, int xOffset,
+ int yOffset, DragView dragView, Object dragInfo) {
+ if (mDragTargetLayout != null) {
+ mDragTargetLayout.onDragExit();
+ mDragTargetLayout = null;
+ }
}
- private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout) {
+ private void onDropExternal(int x, int y, Object dragInfo,
+ CellLayout cellLayout) {
onDropExternal(x, y, dragInfo, cellLayout, false);
}
-
- private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout,
- boolean insertAtFirst) {
- // Drag from somewhere else
+
+ /**
+ * Add the item specified by dragInfo to the given layout.
+ * This is basically the equivalent of onDropExternal, except it's not initiated
+ * by drag and drop.
+ * @return true if successful
+ */
+ public boolean addExternalItemToScreen(Object dragInfo, View layout) {
+ CellLayout cl = (CellLayout) layout;
ItemInfo info = (ItemInfo) dragInfo;
- View view;
+ if (cl.findCellForSpan(mTempEstimate, info.spanX, info.spanY)) {
+ onDropExternal(0, 0, dragInfo, cl, false);
+ return true;
+ }
+ mLauncher.showOutOfSpaceMessage();
+ return false;
+ }
+
+ // Drag from somewhere else
+ private void onDropExternal(int x, int y, Object dragInfo,
+ CellLayout cellLayout, boolean insertAtFirst) {
+ int screen = indexOfChild(cellLayout);
+ if (dragInfo instanceof PendingAddItemInfo) {
+ PendingAddItemInfo info = (PendingAddItemInfo) dragInfo;
+ // When dragging and dropping from customization tray, we deal with creating
+ // widgets/shortcuts/folders in a slightly different way
+ int[] touchXY = new int[2];
+ touchXY[0] = x;
+ touchXY[1] = y;
+ switch (info.itemType) {
+ case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+ mLauncher.addAppWidgetFromDrop(info.componentName, screen, touchXY);
+ break;
+ case LauncherSettings.Favorites.ITEM_TYPE_LIVE_FOLDER:
+ mLauncher.addLiveFolderFromDrop(info.componentName, screen, touchXY);
+ break;
+ case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+ mLauncher.processShortcutFromDrop(info.componentName, screen, touchXY);
+ break;
+ default:
+ throw new IllegalStateException("Unknown item type: " + info.itemType);
+ }
+ cellLayout.onDragExit();
+ return;
+ }
+
+ // This is for other drag/drop cases, like dragging from All Apps
+ ItemInfo info = (ItemInfo) dragInfo;
+
+ View view = null;
switch (info.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
if (info.container == NO_ID && info instanceof ApplicationInfo) {
// Came from all apps -- make a copy
- info = new ShortcutInfo((ApplicationInfo)info);
+ info = new ShortcutInfo((ApplicationInfo) info);
}
- view = mLauncher.createShortcut(R.layout.application, cellLayout, (ShortcutInfo)info);
+ view = mLauncher.createShortcut(R.layout.application, cellLayout,
+ (ShortcutInfo) info);
break;
case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
- (ViewGroup) getChildAt(mCurrentScreen), ((UserFolderInfo) info));
+ cellLayout, ((UserFolderInfo) info));
break;
default:
throw new IllegalStateException("Unknown item type: " + info.itemType);
}
- cellLayout.addView(view, insertAtFirst ? 0 : -1);
- view.setHapticFeedbackEnabled(false);
- view.setOnLongClickListener(mLongClickListener);
- if (view instanceof DropTarget) {
- mDragController.addDropTarget((DropTarget) view);
- }
-
- mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell);
- cellLayout.onDropChild(view, mTargetCell);
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
+ // If the view is null, it has already been added.
+ if (view == null) {
+ cellLayout.onDragExit();
+ } else {
+ mTargetCell = findNearestVacantArea(x, y, 1, 1, null, cellLayout, mTargetCell);
+ addInScreen(view, indexOfChild(cellLayout), mTargetCell[0],
+ mTargetCell[1], info.spanX, info.spanY, insertAtFirst);
+ cellLayout.onDropChild(view);
+ CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
- LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
- LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen, lp.cellX, lp.cellY);
+ LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
+ LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
+ lp.cellX, lp.cellY);
+ }
}
-
+
/**
* Return the current {@link CellLayout}, correctly picking the destination
* screen while a scroll is in progress.
*/
private CellLayout getCurrentDropLayout() {
- int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
+ int index = mScroller.isFinished() ? mCurrentPage : mNextPage;
return (CellLayout) getChildAt(index);
}
/**
+ * Return the current CellInfo describing our current drag; this method exists
+ * so that Launcher can sync this object with the correct info when the activity is created/
+ * destroyed
+ *
+ */
+ public CellLayout.CellInfo getDragInfo() {
+ return mDragInfo;
+ }
+
+ /**
* {@inheritDoc}
*/
public boolean acceptDrop(DragSource source, int x, int y,
int xOffset, int yOffset, DragView dragView, Object dragInfo) {
- final CellLayout layout = getCurrentDropLayout();
- final CellLayout.CellInfo cellInfo = mDragInfo;
- final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
- final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
-
- if (mVacantCache == null) {
- final View ignoreView = cellInfo == null ? null : cellInfo.cell;
- mVacantCache = layout.findAllVacantCells(null, ignoreView);
+ CellLayout layout;
+ if (mIsSmall) {
+ layout = findMatchingPageForDragOver(dragView, x - xOffset, y - yOffset);
+ if (layout == null) {
+ // cancel the drag if we're not over a mini-screen at time of drop
+ return false;
+ }
+ } else {
+ layout = getCurrentDropLayout();
}
+ final CellLayout.CellInfo dragCellInfo = mDragInfo;
+ final int spanX = dragCellInfo == null ? 1 : dragCellInfo.spanX;
+ final int spanY = dragCellInfo == null ? 1 : dragCellInfo.spanY;
- return mVacantCache.findCellForSpan(mTempEstimate, spanX, spanY, false);
- }
-
- /**
- * {@inheritDoc}
- */
- public Rect estimateDropLocation(DragSource source, int x, int y,
- int xOffset, int yOffset, DragView dragView, Object dragInfo, Rect recycle) {
- final CellLayout layout = getCurrentDropLayout();
-
- final CellLayout.CellInfo cellInfo = mDragInfo;
- final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
- final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
- final View ignoreView = cellInfo == null ? null : cellInfo.cell;
-
- final Rect location = recycle != null ? recycle : new Rect();
-
- // Find drop cell and convert into rectangle
- int[] dropCell = estimateDropCell(x - xOffset, y - yOffset,
- spanX, spanY, ignoreView, layout, mTempCell);
-
- if (dropCell == null) {
- return null;
+ final View ignoreView = dragCellInfo == null ? null : dragCellInfo.cell;
+
+ if (layout.findCellForSpanIgnoring(null, spanX, spanY, ignoreView)) {
+ return true;
+ } else {
+ mLauncher.showOutOfSpaceMessage();
+ return false;
}
-
- layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate);
- location.left = mTempEstimate[0];
- location.top = mTempEstimate[1];
-
- layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate);
- location.right = mTempEstimate[0];
- location.bottom = mTempEstimate[1];
-
- return location;
}
/**
* Calculate the nearest cell where the given object would be dropped.
*/
- private int[] estimateDropCell(int pixelX, int pixelY,
+ private int[] findNearestVacantArea(int pixelX, int pixelY,
int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
- // Create vacant cell cache if none exists
- if (mVacantCache == null) {
- mVacantCache = layout.findAllVacantCells(null, ignoreView);
- }
+
+ final int[] cellXY = mTempCell;
+ int localPixelX = pixelX - (layout.getLeft() - mScrollX);
+ int localPixelY = pixelY - (layout.getTop() - mScrollY);
+ layout.estimateDropCell(localPixelX, localPixelY, spanX, spanY, cellXY);
+ layout.cellToPoint(cellXY[0], cellXY[1], mTempEstimate);
// Find the best target drop location
- return layout.findNearestVacantArea(pixelX, pixelY,
- spanX, spanY, mVacantCache, recycle);
+ return layout.findNearestVacantArea(
+ mTempEstimate[0], mTempEstimate[1], spanX, spanY, ignoreView, recycle);
}
-
+
+ /**
+ * Estimate the size that a child with the given dimensions will take in the current screen.
+ */
+ void estimateChildSize(int minWidth, int minHeight, int[] result) {
+ ((CellLayout)getChildAt(mCurrentPage)).estimateChildSize(minWidth, minHeight, result);
+ }
+
void setLauncher(Launcher launcher) {
mLauncher = launcher;
}
@@ -1210,16 +1308,14 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
}
public void onDropCompleted(View target, boolean success) {
- clearVacantCache();
-
- if (success){
+ if (success) {
if (target != this && mDragInfo != null) {
final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
cellLayout.removeView(mDragInfo.cell);
if (mDragInfo.cell instanceof DropTarget) {
mDragController.removeDropTarget((DropTarget)mDragInfo.cell);
}
- //final Object tag = mDragInfo.cell.getTag();
+ // final Object tag = mDragInfo.cell.getTag();
}
} else {
if (mDragInfo != null) {
@@ -1231,40 +1327,32 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
mDragInfo = null;
}
- public void scrollLeft() {
- clearVacantCache();
- if (mScroller.isFinished()) {
- if (mCurrentScreen > 0) snapToScreen(mCurrentScreen - 1);
- } else {
- if (mNextScreen > 0) snapToScreen(mNextScreen - 1);
- }
+ public boolean isDropEnabled() {
+ return true;
}
- public void scrollRight() {
- clearVacantCache();
- if (mScroller.isFinished()) {
- if (mCurrentScreen < getChildCount() -1) snapToScreen(mCurrentScreen + 1);
- } else {
- if (mNextScreen < getChildCount() -1) snapToScreen(mNextScreen + 1);
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ super.onRestoreInstanceState(state);
+ Launcher.setScreen(mCurrentPage);
+ }
+
+ @Override
+ public void scrollLeft() {
+ if (!mIsSmall) {
+ super.scrollLeft();
}
}
- public int getScreenForView(View v) {
- int result = -1;
- if (v != null) {
- ViewParent vp = v.getParent();
- int count = getChildCount();
- for (int i = 0; i < count; i++) {
- if (vp == getChildAt(i)) {
- return i;
- }
- }
+ @Override
+ public void scrollRight() {
+ if (!mIsSmall) {
+ super.scrollRight();
}
- return result;
}
public Folder getFolderForTag(Object tag) {
- int screenCount = getChildCount();
+ final int screenCount = getChildCount();
for (int screen = 0; screen < screenCount; screen++) {
CellLayout currentScreen = ((CellLayout) getChildAt(screen));
int count = currentScreen.getChildCount();
@@ -1273,7 +1361,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
Folder f = (Folder) child;
- if (f.getInfo() == tag) {
+ if (f.getInfo() == tag && f.getInfo().opened) {
return f;
}
}
@@ -1297,23 +1385,9 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
return null;
}
- /**
- * @return True is long presses are still allowed for the current touch
- */
- public boolean allowLongPress() {
- return mAllowLongPress;
- }
-
- /**
- * Set true to allow long-press events to be triggered, usually checked by
- * {@link Launcher} to accept or block dpad-initiated long-presses.
- */
- public void setAllowLongPress(boolean allowLongPress) {
- mAllowLongPress = allowLongPress;
- }
void removeItems(final ArrayList<ApplicationInfo> apps) {
- final int count = getChildCount();
+ final int screenCount = getChildCount();
final PackageManager manager = getContext().getPackageManager();
final AppWidgetManager widgets = AppWidgetManager.getInstance(getContext());
@@ -1323,7 +1397,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
packageNames.add(apps.get(i).componentName.getPackageName());
}
- for (int i = 0; i < count; i++) {
+ for (int i = 0; i < screenCount; i++) {
final CellLayout layout = (CellLayout) getChildAt(i);
// Avoid ANRs by treating each screen separately
@@ -1331,17 +1405,17 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
public void run() {
final ArrayList<View> childrenToRemove = new ArrayList<View>();
childrenToRemove.clear();
-
+
int childCount = layout.getChildCount();
for (int j = 0; j < childCount; j++) {
final View view = layout.getChildAt(j);
Object tag = view.getTag();
-
+
if (tag instanceof ShortcutInfo) {
final ShortcutInfo info = (ShortcutInfo) tag;
final Intent intent = info.intent;
final ComponentName name = intent.getComponent();
-
+
if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
for (String packageName: packageNames) {
if (packageName.equals(name.getPackageName())) {
@@ -1356,12 +1430,12 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
final ArrayList<ShortcutInfo> toRemove = new ArrayList<ShortcutInfo>(1);
final int contentsCount = contents.size();
boolean removedFromFolder = false;
-
+
for (int k = 0; k < contentsCount; k++) {
final ShortcutInfo appInfo = contents.get(k);
final Intent intent = appInfo.intent;
final ComponentName name = intent.getComponent();
-
+
if (Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
for (String packageName: packageNames) {
if (packageName.equals(name.getPackageName())) {
@@ -1372,11 +1446,12 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
}
}
}
-
+
contents.removeAll(toRemove);
if (removedFromFolder) {
final Folder folder = getOpenFolder();
- if (folder != null) folder.notifyDataSetChanged();
+ if (folder != null)
+ folder.notifyDataSetChanged();
}
} else if (tag instanceof LiveFolderInfo) {
final LiveFolderInfo info = (LiveFolderInfo) tag;
@@ -1388,7 +1463,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
for (String packageName: packageNames) {
if (packageName.equals(providerInfo.packageName)) {
LauncherModel.deleteItemFromDatabase(mLauncher, info);
- childrenToRemove.add(view);
+ childrenToRemove.add(view);
}
}
}
@@ -1400,13 +1475,13 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
for (String packageName: packageNames) {
if (packageName.equals(provider.provider.getPackageName())) {
LauncherModel.deleteItemFromDatabase(mLauncher, info);
- childrenToRemove.add(view);
+ childrenToRemove.add(view);
}
}
}
}
}
-
+
childCount = childrenToRemove.size();
for (int j = 0; j < childCount; j++) {
View child = childrenToRemove.get(j);
@@ -1415,7 +1490,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
mDragController.removeDropTarget((DropTarget)child);
}
}
-
+
if (childCount > 0) {
layout.requestLayout();
layout.invalidate();
@@ -1426,10 +1501,8 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
}
void updateShortcuts(ArrayList<ApplicationInfo> apps) {
- final PackageManager pm = mLauncher.getPackageManager();
-
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
+ final int screenCount = getChildCount();
+ for (int i = 0; i < screenCount; i++) {
final CellLayout layout = (CellLayout) getChildAt(i);
int childCount = layout.getChildCount();
for (int j = 0; j < childCount; j++) {
@@ -1445,7 +1518,7 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
final int appCount = apps.size();
- for (int k=0; k<appCount; k++) {
+ for (int k = 0; k < appCount; k++) {
ApplicationInfo app = apps.get(k);
if (app.componentName.equals(name)) {
info.setIcon(mIconCache.getIcon(info.intent));
@@ -1462,47 +1535,30 @@ public class Workspace extends ViewGroup implements DropTarget, DragSource, Drag
void moveToDefaultScreen(boolean animate) {
if (animate) {
- snapToScreen(mDefaultScreen);
+ if (mIsSmall) {
+ unshrink(mDefaultPage);
+ } else {
+ snapToPage(mDefaultPage);
+ }
} else {
- setCurrentScreen(mDefaultScreen);
+ setCurrentPage(mDefaultPage);
}
- getChildAt(mDefaultScreen).requestFocus();
+ getChildAt(mDefaultPage).requestFocus();
}
void setIndicators(Drawable previous, Drawable next) {
mPreviousIndicator = previous;
mNextIndicator = next;
- previous.setLevel(mCurrentScreen);
- next.setLevel(mCurrentScreen);
+ previous.setLevel(mCurrentPage);
+ next.setLevel(mCurrentPage);
}
- public static class SavedState extends BaseSavedState {
- int currentScreen = -1;
-
- SavedState(Parcelable superState) {
- super(superState);
- }
-
- private SavedState(Parcel in) {
- super(in);
- currentScreen = in.readInt();
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- super.writeToParcel(out, flags);
- out.writeInt(currentScreen);
- }
-
- public static final Parcelable.Creator<SavedState> CREATOR =
- new Parcelable.Creator<SavedState>() {
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in);
- }
+ @Override
+ public void syncPages() {
+ }
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
+ @Override
+ public void syncPageItems(int page) {
}
+
}
diff --git a/src/com/android/launcher2/allapps.rs b/src/com/android/launcher2/allapps.rs
new file mode 100644
index 000000000..acee82bf0
--- /dev/null
+++ b/src/com/android/launcher2/allapps.rs
@@ -0,0 +1,390 @@
+#pragma version(1)
+
+#pragma rs java_package_name(com.android.launcher2)
+
+#include "rs_graphics.rsh"
+
+#define PI 3.14159f
+
+// Constants from Java
+int COLUMNS_PER_PAGE_PORTRAIT;
+int ROWS_PER_PAGE_PORTRAIT;
+int COLUMNS_PER_PAGE_LANDSCAPE;
+int ROWS_PER_PAGE_LANDSCAPE;
+
+int gIconCount;
+int gSelectedIconIndex = -1;
+rs_allocation gSelectedIconTexture;
+rs_allocation gHomeButton;
+
+rs_program_fragment gPFTexNearest;
+rs_program_fragment gPFTexMip;
+rs_program_fragment gPFTexMipAlpha;
+rs_program_vertex gPVCurve;
+rs_program_store gPS;
+rs_mesh gSMCell;
+
+rs_allocation *gIconIDs;
+rs_allocation *gLabelIDs;
+
+typedef struct VpConsts {
+ rs_matrix4x4 Proj;
+ float4 Position;
+ float4 ScaleOffset;
+ float2 BendPos;
+ float2 ImgSize;
+} VpConsts_t;
+VpConsts_t *vpConstants;
+
+
+#pragma rs export_var(COLUMNS_PER_PAGE_PORTRAIT, ROWS_PER_PAGE_PORTRAIT, COLUMNS_PER_PAGE_LANDSCAPE, ROWS_PER_PAGE_LANDSCAPE, gIconCount, gSelectedIconIndex, gSelectedIconTexture, gHomeButton, gTargetPos, gPFTexNearest, gPFTexMip, gPFTexMipAlpha, gPVCurve, gPS, gSMCell, gIconIDs, gLabelIDs, vpConstants)
+#pragma rs export_func(move, moveTo, setZoom, fling)
+
+
+// Attraction to center values from page edge to page center.
+static float g_AttractionTable[9] = {20.f, 20.f, 20.f, 10.f, -10.f, -20.f, -20.f, -20.f, -20.f};
+static float g_FrictionTable[9] = {10.f, 10.f, 11.f, 15.f, 15.f, 11.f, 10.f, 10.f, 10.f};
+static float g_PhysicsTableSize = 7;
+
+static float gZoomTarget;
+static float gTargetPos;
+static float g_PosPage = 0.f;
+static float g_PosVelocity = 0.f;
+static float g_LastPositionX = 0.f;
+static bool g_LastTouchDown = false;
+static float g_DT;
+static int g_PosMax;
+static float g_Zoom = 0.f;
+static float g_Animation = 1.f;
+static float g_OldPosPage;
+static float g_OldPosVelocity;
+static float g_OldZoom;
+static float g_MoveToTotalTime = 0.2f;
+static float g_MoveToTime = 0.f;
+static float g_MoveToOldPos = 0.f;
+
+static int g_Cols;
+static int g_Rows;
+
+rs_allocation g_VPConstAlloc;
+
+// Drawing constants, should be parameters ======
+#define VIEW_ANGLE 1.28700222f
+
+
+static void updateReadback() {
+ if ((g_OldPosPage != g_PosPage) ||
+ (g_OldPosVelocity != g_PosVelocity) ||
+ (g_OldZoom != g_Zoom)) {
+
+ g_OldPosPage = g_PosPage;
+ g_OldPosVelocity = g_PosVelocity;
+ g_OldZoom = g_Zoom;
+
+ int i[3];
+ i[0] = g_PosPage * (1 << 16);
+ i[1] = g_PosVelocity * (1 << 16);
+ i[2] = g_OldZoom * (1 << 16);
+ rsSendToClientBlocking(1, &i[0], sizeof(i));
+ }
+}
+
+void init() {
+}
+
+void move(float newPos) {
+ if (g_LastTouchDown) {
+ float dx = -(newPos - g_LastPositionX);
+ g_PosVelocity = 0;
+ g_PosPage += dx * 5.2f;
+
+ float pmin = -0.49f;
+ float pmax = g_PosMax + 0.49f;
+ g_PosPage = clamp(g_PosPage, pmin, pmax);
+ }
+ g_LastTouchDown = true;
+ g_LastPositionX = newPos;
+ g_MoveToTime = 0;
+}
+
+void moveTo(float targetPos) {
+ gTargetPos = targetPos;
+ g_MoveToTime = g_MoveToTotalTime;
+ g_PosVelocity = 0;
+ g_MoveToOldPos = g_PosPage;
+}
+
+void setZoom(float z, /*bool*/ int animate) {
+ gZoomTarget = z;
+ if (gZoomTarget < 0.001f) {
+ gZoomTarget = 0;
+ }
+ if (!animate) {
+ g_Zoom = gZoomTarget;
+ }
+ updateReadback();
+}
+
+void fling(float newPos, float vel) {
+ move(newPos);
+
+ g_LastTouchDown = false;
+ g_PosVelocity = -vel * 4;
+ float av = fabs(g_PosVelocity);
+ float minVel = 3.5f;
+
+ minVel *= 1.f - (fabs(rsFrac(g_PosPage + 0.5f) - 0.5f) * 0.45f);
+
+ if (av < minVel && av > 0.2f) {
+ if (g_PosVelocity > 0) {
+ g_PosVelocity = minVel;
+ } else {
+ g_PosVelocity = -minVel;
+ }
+ }
+
+ if (g_PosPage <= 0) {
+ g_PosVelocity = max(0.f, g_PosVelocity);
+ }
+ if (g_PosPage > g_PosMax) {
+ g_PosVelocity = min(0.f, g_PosVelocity);
+ }
+}
+
+// Interpolates values in the range 0..1 to a curve that eases in
+// and out.
+static float getInterpolation(float input) {
+ return (cos((input + 1) * PI) * 0.5f) + 0.5f;
+}
+
+
+static void updatePos() {
+ if (g_LastTouchDown) {
+ return;
+ }
+
+ float tablePosNorm = rsFrac(g_PosPage + 0.5f);
+ float tablePosF = tablePosNorm * g_PhysicsTableSize;
+ int tablePosI = tablePosF;
+ float tablePosFrac = tablePosF - tablePosI;
+ float accel = mix(g_AttractionTable[tablePosI],
+ g_AttractionTable[tablePosI + 1],
+ tablePosFrac) * g_DT;
+ float friction = mix(g_FrictionTable[tablePosI],
+ g_FrictionTable[tablePosI + 1],
+ tablePosFrac) * g_DT;
+
+ if (g_MoveToTime) {
+ // New position is old posiition + (total distance) * (interpolated time)
+ g_PosPage = g_MoveToOldPos + (gTargetPos - g_MoveToOldPos) * getInterpolation((g_MoveToTotalTime - g_MoveToTime) / g_MoveToTotalTime);
+ g_MoveToTime -= g_DT;
+ if (g_MoveToTime <= 0) {
+ g_MoveToTime = 0;
+ g_PosPage = gTargetPos;
+ }
+ return;
+ }
+
+ // If our velocity is low OR acceleration is opposing it, apply it.
+ if (fabs(g_PosVelocity) < 4.0f || (g_PosVelocity * accel) < 0) {
+ g_PosVelocity += accel;
+ }
+ //RS_DEBUG(g_PosPage);
+ //RS_DEBUG(g_PosVelocity);
+ //RS_DEBUG(friction);
+ //RS_DEBUG(accel);
+
+ // Normal physics
+ if (g_PosVelocity > 0) {
+ g_PosVelocity -= friction;
+ g_PosVelocity = max(g_PosVelocity, 0.f);
+ } else {
+ g_PosVelocity += friction;
+ g_PosVelocity = min(g_PosVelocity, 0.f);
+ }
+
+ if ((friction > fabs(g_PosVelocity)) && (friction > fabs(accel))) {
+ // Special get back to center and overcome friction physics.
+ float t = tablePosNorm - 0.5f;
+ if (fabs(t) < (friction * g_DT)) {
+ // really close, just snap
+ g_PosPage = round(g_PosPage);
+ g_PosVelocity = 0;
+ } else {
+ if (t > 0) {
+ g_PosVelocity = -friction;
+ } else {
+ g_PosVelocity = friction;
+ }
+ }
+ }
+
+ // Check for out of boundry conditions.
+ if (g_PosPage < 0 && g_PosVelocity < 0) {
+ float damp = 1.0f + (g_PosPage * 4);
+ damp = clamp(damp, 0.f, 0.9f);
+ g_PosVelocity *= damp;
+ }
+ if (g_PosPage > g_PosMax && g_PosVelocity > 0) {
+ float damp = 1.0f - ((g_PosPage - g_PosMax) * 4);
+ damp = clamp(damp, 0.f, 0.9f);
+ g_PosVelocity *= damp;
+ }
+
+ g_PosPage += g_PosVelocity * g_DT;
+ g_PosPage = clamp(g_PosPage, -0.49f, g_PosMax + 0.49f);
+}
+
+static void
+draw_home_button()
+{
+ rsgBindTexture(gPFTexNearest, 0, gHomeButton);
+
+ float w = rsgGetWidth();
+ float h = rsgGetHeight();
+ float tw = rsAllocationGetDimX(gHomeButton);
+ float th = rsAllocationGetDimY(gHomeButton);
+
+ float x;
+ float y;
+ if (w > h) {
+ x = w - (tw * (1 - g_Animation)) + 20;
+ y = (h - th) * 0.5f;
+ } else {
+ x = (w - tw) / 2;
+ y = -g_Animation * th;
+ y -= 30; // move the house to the edge of the screen as it doesn't fill the texture.
+ }
+
+ rsgDrawSpriteScreenspace(x, y, 0, tw, th);
+}
+
+static void drawFrontGrid(float rowOffset, float p)
+{
+ float h = rsgGetHeight();
+ float w = rsgGetWidth();
+
+ int intRowOffset = rowOffset;
+ float rowFrac = rowOffset - intRowOffset;
+ float colWidth = 120.f;//w / 4;
+ float rowHeight = colWidth + 25.f;
+ float yoff = 0.5f * h + 1.5f * rowHeight;
+
+ int row, col;
+ int colCount = 4;
+ if (w > h) {
+ colCount = 6;
+ rowHeight -= 12.f;
+ yoff = 0.47f * h + 1.0f * rowHeight;
+ }
+
+ int iconNum = (intRowOffset - 5) * colCount;
+
+ rsgBindProgramVertex(gPVCurve);
+
+ vpConstants->Position.z = p;
+
+ for (row = -5; row < 15; row++) {
+ float y = yoff - ((-rowFrac + row) * rowHeight);
+
+ for (col=0; col < colCount; col++) {
+ if (iconNum >= gIconCount) {
+ return;
+ }
+
+ if (iconNum >= 0) {
+ float x = colWidth * col + (colWidth / 2);
+ vpConstants->Position.x = x + 0.2f;
+
+ if (gSelectedIconIndex == iconNum && !p && rsIsObject(gSelectedIconTexture)) {
+ rsgBindProgramFragment(gPFTexNearest);
+ rsgBindTexture(gPFTexNearest, 0, gSelectedIconTexture);
+ vpConstants->ImgSize.x = rsAllocationGetDimX(gSelectedIconTexture);
+ vpConstants->ImgSize.y = rsAllocationGetDimY(gSelectedIconTexture);
+ vpConstants->Position.y = y - (rsAllocationGetDimY(gSelectedIconTexture)
+ - rsAllocationGetDimY(gIconIDs[iconNum])) * 0.5f;
+ rsAllocationMarkDirty(g_VPConstAlloc);
+ rsgDrawMesh(gSMCell);
+ }
+
+ rsgBindProgramFragment(gPFTexMip);
+ vpConstants->ImgSize.x = rsAllocationGetDimX(gIconIDs[iconNum]);
+ vpConstants->ImgSize.y = rsAllocationGetDimY(gIconIDs[iconNum]);
+ vpConstants->Position.y = y - 0.2f;
+ rsAllocationMarkDirty(g_VPConstAlloc);
+ rsgBindTexture(gPFTexMip, 0, gIconIDs[iconNum]);
+ rsgDrawMesh(gSMCell);
+
+ rsgBindProgramFragment(gPFTexMipAlpha);
+ vpConstants->ImgSize.x = rsAllocationGetDimX(gLabelIDs[iconNum]);
+ vpConstants->ImgSize.y = rsAllocationGetDimY(gLabelIDs[iconNum]);
+ vpConstants->Position.y = y - 64.f - 0.2f;
+ rsAllocationMarkDirty(g_VPConstAlloc);
+ rsgBindTexture(gPFTexMipAlpha, 0, gLabelIDs[iconNum]);
+ rsgDrawMesh(gSMCell);
+ }
+ iconNum++;
+ }
+ }
+}
+
+
+int root()
+{
+ // Compute dt in seconds.
+ // physics may break if DT is large.
+ g_DT = min(rsGetDt(), 0.1f);
+ g_VPConstAlloc = rsGetAllocation(vpConstants);
+
+ if (g_Zoom != gZoomTarget) {
+ float dz = g_DT * 1.7f;
+ if (gZoomTarget < 0.5f) {
+ dz = -dz;
+ }
+ if (fabs(g_Zoom - gZoomTarget) < fabs(dz)) {
+ g_Zoom = gZoomTarget;
+ } else {
+ g_Zoom += dz;
+ }
+ updateReadback();
+ }
+ g_Animation = pow(1.f - g_Zoom, 3.f);
+
+ // Set clear value to dim the background based on the zoom position.
+ if ((g_Zoom < 0.001f) && (gZoomTarget < 0.001f)) {
+ rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ // When we're zoomed out and not tracking motion events, reset the pos to 0.
+ if (!g_LastTouchDown) {
+ g_PosPage = 0;
+ }
+ return 0;
+ } else {
+ rsgClearColor(0.0f, 0.0f, 0.0f, g_Zoom);
+ }
+
+ rsgBindProgramStore(gPS);
+
+ // icons & labels
+ if (rsgGetWidth() > rsgGetHeight()) {
+ g_Cols = COLUMNS_PER_PAGE_LANDSCAPE;
+ g_Rows = ROWS_PER_PAGE_LANDSCAPE;
+ } else {
+ g_Cols = COLUMNS_PER_PAGE_PORTRAIT;
+ g_Rows = ROWS_PER_PAGE_PORTRAIT;
+ }
+
+ g_PosMax = ((gIconCount + (g_Cols-1)) / g_Cols) - g_Rows;
+ if (g_PosMax < 0) g_PosMax = 0;
+
+ updatePos();
+ updateReadback();
+
+ // Draw the icons ========================================
+ drawFrontGrid(g_PosPage, g_Animation);
+
+ rsgBindProgramFragment(gPFTexNearest);
+ draw_home_button();
+ return (g_PosVelocity != 0) || rsFrac(g_PosPage) || g_Zoom != gZoomTarget || (g_MoveToTime != 0);
+}
+
+