summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRajesh Yengisetty <rajesh@cyngn.com>2014-07-22 17:21:09 -0700
committerRaj Yengisetty <rajesh@cyngn.com>2015-03-06 15:37:44 -0800
commitd9324529ed3b5dcd23f10f1c147b783725deedee (patch)
tree762dee35e0e6cc1af77634997a86b492e7cd33f3
parent73b5610b8c69485d91c36253428627fbcd78274d (diff)
downloadandroid_packages_apps_Trebuchet-d9324529ed3b5dcd23f10f1c147b783725deedee.tar.gz
android_packages_apps_Trebuchet-d9324529ed3b5dcd23f10f1c147b783725deedee.tar.bz2
android_packages_apps_Trebuchet-d9324529ed3b5dcd23f10f1c147b783725deedee.zip
Trebuchet: Implement new app drawer.
commit 535c5e89c7484ae96f83af021487a8061f3e0b04 Author: Adnan <adnan@cyngn.com> Date: Tue Jul 22 17:21:09 2014 -0700 Trebuchet: Implement new app drawer. Change-Id: Ibf6152352520ece4dff201b8013378a1ad562894 commit d46c52032ef20d9f97409114e6efc34650245fb6 Author: Raj Yengisetty <rajesh@cyngn.com> Date: Fri Jul 25 09:13:38 2014 -0700 Fix: With the new App Drawer implementation AppsDrawerListAdapter should clone it's AppInfo ArrayList before removing elements from it, otherwise the AppsCustomizePagedView will not add any apps to it's instance variables and will never dispaly widgets. Repro: Long press on workspace to get to overview mode Click widgets button Observe no Widgets (empty page) Change-Id: I7574ae23dbf3298674585060d5da5e00a5852d46 commit b0c7e4fad041e492816a36e538a505983d996a75 Author: Adnan <adnan@cyngn.com> Date: Tue Jul 22 19:06:15 2014 -0700 Catapult: Allow elements to be draggable out of drawer. Change-Id: I711b42bad0fc6ae14399f3eb88ec60cd443f87d1 commit 00148d42288a7ee37e8aa05cccb121e9bcf6fbda Author: Danesh M <danesh@cyngn.com> Date: Wed Jul 23 10:37:19 2014 -0700 Catapult : Make flowlayout wrap_content Change-Id: I9db6d39274b71ee2750058fd65f4917701394ac0 commit 268e707d14be0c29448b9f5aee4f26a3180c75fd Author: Adnan <adnan@cyngn.com> Date: Wed Jul 23 13:27:06 2014 -0700 Catapult: Use recyclerview for side app drawer. Change-Id: Ie6f916792375f82a8f97a0369b98b813416216e8 commit efcbdc635e34511b73939639cbcf910728b1b3b2 Author: Adnan <adnan@cyngn.com> Date: Wed Jul 23 14:30:37 2014 -0700 Catapult: Handle app install and uninstall in drawer. Change-Id: Ia22876574e96b9ff0d4b4d93a853d7e92633376c commit 3fb7c6cb0b4bb9cfaf1d11c787df14c6767a72a9 Author: Danesh M <danesh@cyngn.com> Date: Thu Jul 24 10:19:57 2014 -0700 Catapult : Improve scroll performance Change-Id: I294beb9c9e1946724108bad2c0bca8839c5bf56b commit 85828036bd4af0a242f408ba18236b11920a0120 Author: Adnan <adnan@cyngn.com> Date: Thu Jul 24 10:46:58 2014 -0700 Catapult: Use setHasFixedSize on RecyclerView. Change-Id: Id06faa37d69f2d3ef533f829d991971ea2a32012 commit 4206cc348bb8aa788b0e4a0f2080deeb55a73d38 Author: Adnan <adnan@cyngn.com> Date: Thu Jul 24 10:49:15 2014 -0700 Catapult: Show App Info and Uninstall options on drag. Change-Id: Ica307eef4d2a086733f84704d129ac893f9bbb34 commit 283db64b47a5ec395e7ec07d5e34660c98cef91d Author: Adnan <adnan@cyngn.com> Date: Thu Jul 24 15:00:37 2014 -0700 Catapult: Properly handle install, remove, and update. Change-Id: I16138503bb5d31e999f293afc00e1adc2d574727 commit ca19c304fac9de8b97c0345b0c9a7cf3c3bdf155 Author: Adnan <adnan@cyngn.com> Date: Thu Jul 24 15:27:09 2014 -0700 Catapult: Ignore index 0 when added. Change-Id: Id00a41feb1813c419fda4425327024292d51e300 commit f595d415d3b8d9775d7448f1bbc4b542ea40c7a3 Author: Adnan <adnan@cyngn.com> Date: Thu Jul 24 15:51:32 2014 -0700 Catapult: Clean up after drag. Change-Id: I858c9aa5e30bc6ca13d4ca53397b769bb6b2b86b commit c337a69e43f213cacc0fe50a0d001f4cb3a5d9f4 Author: Adnan <adnan@cyngn.com> Date: Fri Jul 25 12:46:39 2014 -0700 Catapult: Fix app updates for new data structure. - Also fix line wrapping in class. Change-Id: I3c4f33d6e3e236952ea6d73f8402f32ac059f810 commit ef6b88c44f2c6c3372136773fa01821c4fc7ba62 Author: Danesh M <danesh@cyngn.com> Date: Thu Jul 24 23:26:41 2014 -0700 Catapult : Add scrubber Change-Id: Ied2e44e2d393bc5537f77c29a4b099da1e1f2a64 commit a96939b669f8adfad7bb633aa9acfa18fe72ff1e Author: Adnan <adnan@cyngn.com> Date: Mon Jul 28 11:01:37 2014 -0700 Catapult: Fix crashes for addition of next sections in list. Change-Id: I66d8e02523595655458d0f30d75ccfbe3b65e69d commit 48dd4872ba59499e7cea950fe4b1f8e4c6774a24 Author: Adnan <adnan@cyngn.com> Date: Mon Jul 28 11:59:20 2014 -0700 Catapult: Place all special or numerics in a single category. - Also implement AutoFitTextView for character resizing. Change-Id: Ida4e702b43dc934835021cbbf3e0b41d9cea0b16 commit 40ed85a3f518dcabfbf8533f534934e029cb3835 Author: Adnan <adnan@cyngn.com> Date: Mon Jul 28 13:22:14 2014 -0700 Catapult: Disable split touch events within drawer item. Change-Id: I990717140d5b420b9a658aede317206947e9fbce commit d76583a1a4e5dd0a5792ce6d39adc427934d7db9 Author: Adnan <adnan@cyngn.com> Date: Mon Jul 28 15:53:23 2014 -0700 Catapult: Add spacer to last item in list. Change-Id: Ia3f2d7d12c09886ab24e8976549518c1bb1a9a0b commit 467e9cb6428b33fd6846be8d30bb8dcfe127acdf Author: Adnan <adnan@cyngn.com> Date: Tue Jul 29 12:39:37 2014 -0700 Catapult: Group special characters together under section. Change-Id: Ic2508eb101e1604c9e1544bdc858c7487f426a44 commit 1f566b1b5b9bac71d0d79d07084f024ba67982e1 Author: Danesh M <danesh@cyngn.com> Date: Tue Jul 29 14:25:26 2014 -0700 Catapult : Fix scrubber - Make seekbar height of entire scrubber - Cancel viewproperty animator instead of Animation - Reduce scrubber animation duration - Ignore swipe events if in scrubber area Change-Id: I3a80b5bc392105bd1d5deff1defed8c083512029 commit b54ace6ee3ecca153f4c8fd2cda35366b205106f Author: Danesh M <danesh@cyngn.com> Date: Tue Jul 29 14:29:44 2014 -0700 Catapult : Check if appdrawer is null when intercepting Change-Id: I8d1b095acc8484d2ffb3f3ffc07608a639c9a3a3 commit 7a544638641e953aa2e75e6bc3f353906887440e Author: Adnan <adnan@cyngn.com> Date: Tue Jul 29 18:23:30 2014 -0700 Catapult: Use isLetter to fix locale issues in some languages. Change-Id: I253139ab3a93a9b48d3158f790e3975f776ce666 commit 96d1198ec28e5b858aa5cda268eecdf2335c6cf2 Author: Danesh Mondegarian <danesh@cyngn.com> Date: Fri Aug 8 23:30:15 2014 -0700 Catapult : Improve scrubber 9patch Change-Id: Ide249c0fe269d2f8966c753f6fdd9c1f673a0df9 commit 4920d0fdd94f7b0df6fccc3d16f5b98cbf4d19a2 Author: Danesh M <danesh@cyngn.com> Date: Fri Aug 8 15:44:20 2014 -0700 Catapult : Improve app drawer perf Change-Id: I78f699dbeac2618dceaa463940175e33ce685677 commit 10004f024b01029b2c137520407cefb5568b8c47 Author: Raj Yengisetty <rajesh@cyngn.com> Date: Thu Feb 26 11:42:17 2015 -0800 Remove scrubber swipe logic from Workspace Change-Id: I44d94a412ccdb60ce0f537fd6ddb71c2daffc906 Change-Id: Ibf6152352520ece4dff201b8013378a1ad562894
-rw-r--r--Android.mk3
-rw-r--r--res/drawable-nodpi/letter_indicator.9.pngbin0 -> 3614 bytes
-rw-r--r--res/drawable/scrubber_back.xml20
-rw-r--r--res/drawable/seek_back.xml21
-rw-r--r--res/layout-land/launcher.xml6
-rw-r--r--res/layout-port/launcher.xml5
-rw-r--r--res/layout-sw720dp/launcher.xml6
-rw-r--r--res/layout/app_drawer_container.xml34
-rw-r--r--res/layout/app_drawer_item.xml49
-rw-r--r--res/layout/drawer_icon.xml36
-rw-r--r--res/layout/scrub_layout.xml100
-rw-r--r--res/values/attrs.xml5
-rw-r--r--res/values/dimens.xml5
-rw-r--r--src/com/android/launcher3/AppDrawerIconView.java74
-rw-r--r--src/com/android/launcher3/AppDrawerListAdapter.java480
-rw-r--r--src/com/android/launcher3/AppDrawerScrubber.java141
-rw-r--r--src/com/android/launcher3/AutoFitTextView.java381
-rw-r--r--src/com/android/launcher3/BubbleTextView.java10
-rw-r--r--src/com/android/launcher3/Launcher.java148
-rw-r--r--src/com/android/launcher3/PagedViewIcon.java134
-rw-r--r--src/com/android/launcher3/Utilities.java8
21 files changed, 1625 insertions, 41 deletions
diff --git a/Android.mk b/Android.mk
index 1f98b76d8..c7e2dfde1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -23,7 +23,8 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13 \
+ android-support-v7-recyclerview
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
$(call all-java-files-under, WallpaperPicker/src) \
diff --git a/res/drawable-nodpi/letter_indicator.9.png b/res/drawable-nodpi/letter_indicator.9.png
new file mode 100644
index 000000000..af3578ece
--- /dev/null
+++ b/res/drawable-nodpi/letter_indicator.9.png
Binary files differ
diff --git a/res/drawable/scrubber_back.xml b/res/drawable/scrubber_back.xml
new file mode 100644
index 000000000..56cf5af73
--- /dev/null
+++ b/res/drawable/scrubber_back.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#99333333"/>
+ <corners android:radius="5dip"/>
+</shape>
diff --git a/res/drawable/seek_back.xml b/res/drawable/seek_back.xml
new file mode 100644
index 000000000..a49796c05
--- /dev/null
+++ b/res/drawable/seek_back.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The CyanogenMod 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="line">
+ <stroke android:width="4dp" android:color="#FFF"/>
+ <size android:height="2dp" />
+</shape>
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 8cd867366..4c097ba0a 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -62,5 +62,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
+
+ <include layout="@layout/app_drawer_container"
+ android:id="@+id/app_drawer_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone" />
</com.android.launcher3.DragLayer>
</FrameLayout>
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index ad1027829..e193e4820 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -83,5 +83,10 @@
android:id="@+id/overview_panel"
android:visibility="gone" />
+ <include layout="@layout/app_drawer_container"
+ android:id="@+id/app_drawer_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone" />
</com.android.launcher3.DragLayer>
</FrameLayout>
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 62615411f..772110527 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -81,5 +81,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
+
+ <include layout="@layout/app_drawer_container"
+ android:id="@+id/app_drawer_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone" />
</com.android.launcher3.DragLayer>
</FrameLayout>
diff --git a/res/layout/app_drawer_container.xml b/res/layout/app_drawer_container.xml
new file mode 100644
index 000000000..c0eb97a99
--- /dev/null
+++ b/res/layout/app_drawer_container.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <FrameLayout
+ android:id="@+id/fake_page_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+ <FrameLayout
+ android:id="@+id/fake_page"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="invisible"
+ android:clipToPadding="false" />
+ </FrameLayout>
+</FrameLayout> \ No newline at end of file
diff --git a/res/layout/app_drawer_item.xml b/res/layout/app_drawer_item.xml
new file mode 100644
index 000000000..37bdeea6a
--- /dev/null
+++ b/res/layout/app_drawer_item.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:autofit="http://schemas.android.com/apk/res-auto"
+ android:orientation="vertical"
+ android:splitMotionEvents="false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <LinearLayout
+ android:layout_marginTop="10dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+ <com.android.launcher3.AutoFitTextView
+ android:id="@+id/drawer_item_title"
+ android:layout_width="30dp"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dp"
+ android:includeFontPadding="false"
+ android:gravity="start|top"
+ android:singleLine="true"
+ autofit:minTextSize="8sp"
+ android:textSize="35sp"
+ android:textColor="@android:color/white"/>
+ <LinearLayout
+ android:id="@+id/drawer_item_flow"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" />
+ </LinearLayout>
+ <Space android:id="@+id/spacer"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:visibility="gone" />
+</LinearLayout>
diff --git a/res/layout/drawer_icon.xml b/res/layout/drawer_icon.xml
new file mode 100644
index 000000000..1cdea7884
--- /dev/null
+++ b/res/layout/drawer_icon.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod 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.launcher3.AppDrawerIconView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
+
+ <TextView
+ android:id="@+id/label"
+ style="@style/WorkspaceIcon"
+ android:shadowRadius="0"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+</com.android.launcher3.AppDrawerIconView>
diff --git a/res/layout/scrub_layout.xml b/res/layout/scrub_layout.xml
new file mode 100644
index 000000000..e8761e306
--- /dev/null
+++ b/res/layout/scrub_layout.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_marginRight="20dp"
+ android:layout_marginLeft="20dp"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:clickable="true"
+ android:layout_marginTop="-5dp"
+ android:layout_below="@+id/scrubberIndicator"
+ android:orientation="horizontal"
+ android:background="@drawable/scrubber_back"
+ android:layout_width="match_parent"
+ android:layout_height="50dp">
+
+ <Space
+ android:layout_weight="0.1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent" />
+
+ <TextView
+ android:id="@+id/firstSection"
+ android:gravity="center"
+ android:textColor="@android:color/white"
+ android:paddingRight="10dp"
+ android:textStyle="bold"
+ style="?android:attr/textAppearanceLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent" />
+
+ <LinearLayout
+ android:layout_weight="0.9"
+ android:layout_width="0dp"
+ android:layout_gravity="center"
+ android:orientation="vertical"
+ android:layout_height="match_parent">
+
+ <SeekBar
+ android:id="@+id/scrubber"
+ android:paddingLeft="0dp"
+ android:paddingRight="0dp"
+ android:layout_marginRight="-10dp"
+ android:layout_marginLeft="-10dp"
+ android:thumb="@android:color/transparent"
+ android:thumbOffset="-10dp"
+ android:progressDrawable="@drawable/seek_back"
+ android:layout_width="match_parent"
+ android:layout_gravity="center"
+ android:layout_height="match_parent" />
+
+ </LinearLayout>
+
+
+ <TextView
+ android:gravity="center"
+ android:id="@+id/lastSection"
+ android:paddingLeft="10dp"
+ android:textColor="@android:color/white"
+ android:textStyle="bold"
+ style="?android:attr/textAppearanceLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent" />
+
+ <Space
+ android:layout_weight="0.1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent" />
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/scrubberIndicator"
+ android:background="@drawable/letter_indicator"
+ android:layout_width="80dp"
+ android:textSize="30sp"
+ android:gravity="center"
+ android:textColor="@android:color/white"
+ android:clickable="false"
+ android:layout_marginBottom="-20dp"
+ android:visibility="invisible"
+ android:layout_height="100dp" />
+
+</RelativeLayout>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 240f31171..b32c5a2bc 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -154,4 +154,9 @@
<attr name="overlay" format="boolean"/>
</declare-styleable>
+ <declare-styleable name="AutofitTextView">
+ <attr name="minTextSize" format="dimension" />
+ <attr name="precision" format="float" />
+ <attr name="sizeToFit" format="boolean" />
+ </declare-styleable>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d591535d3..7ceaf8bd5 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -118,4 +118,9 @@
<dimen name="grid_padding">15dp</dimen>
<dimen name="grid_custom_text">50dp</dimen>
+
+ <dimen name="scrubber_bottom_padding">30dp</dimen>
+
+ <!-- Vertical app drawer padding -->
+ <dimen name="vertical_app_drawer_icon_padding">5px</dimen>
</resources>
diff --git a/src/com/android/launcher3/AppDrawerIconView.java b/src/com/android/launcher3/AppDrawerIconView.java
new file mode 100644
index 000000000..d8564b6ed
--- /dev/null
+++ b/src/com/android/launcher3/AppDrawerIconView.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.MotionEvent;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * AppDrawerIconView - represents icons in the vertical app drawer.
+ * Found to be more performant than the BubbleTextView used in the
+ * legacy app drawer.
+ */
+public class AppDrawerIconView extends LinearLayout {
+
+ TextView mLabel;
+ ImageView mIcon;
+
+ public AppDrawerIconView(Context context) {
+ super(context);
+ }
+
+ public AppDrawerIconView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AppDrawerIconView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mLabel = (TextView) findViewById(R.id.label);
+ mIcon = (ImageView) findViewById(R.id.image);
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+ mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
+ mLabel.setShadowLayer(BubbleTextView.SHADOW_LARGE_RADIUS, 0.0f,
+ BubbleTextView.SHADOW_Y_OFFSET, BubbleTextView.SHADOW_LARGE_COLOUR);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ setAlpha(PagedViewIcon.PRESS_ALPHA);
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ setAlpha(1f);
+ break;
+ }
+ return super.onTouchEvent(event);
+ }
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/AppDrawerListAdapter.java b/src/com/android/launcher3/AppDrawerListAdapter.java
new file mode 100644
index 000000000..891c32357
--- /dev/null
+++ b/src/com/android/launcher3/AppDrawerListAdapter.java
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2015 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.ComponentName;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.support.v7.widget.RecyclerView;
+import android.widget.LinearLayout;
+import android.widget.SectionIndexer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * AppDrawerListAdapter - list adapter for the vertical app drawer
+ */
+public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdapter.ViewHolder>
+ implements View.OnLongClickListener, DragSource, SectionIndexer {
+
+ private static final int SCRUBBER_MARGIN_FROM_BOTTOM_DP = 80;
+ private static final char NUMERIC_OR_SPECIAL_CHAR = '#';
+ private static final String NUMERIC_OR_SPECIAL_HEADER = "#";
+
+ private ArrayList<AppItemIndexedInfo> mHeaderList;
+ private LayoutInflater mLayoutInflater;
+
+ private Launcher mLauncher;
+ private DeviceProfile mDeviceProfile;
+ private LinkedHashMap<String, Integer> mSectionHeaders;
+ private LinearLayout.LayoutParams mIconParams, mSpacerParams;
+ private Rect mIconRect;
+
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+ public AutoFitTextView mTextView;
+ public ViewGroup mLayout;
+ public View mSpacer;
+ public ViewHolder(View itemView) {
+ super(itemView);
+ mTextView = (AutoFitTextView) itemView.findViewById(R.id.drawer_item_title);
+ mLayout = (ViewGroup) itemView.findViewById(R.id.drawer_item_flow);
+ mSpacer = itemView.findViewById(R.id.spacer);
+ }
+ }
+
+ public AppDrawerListAdapter(Launcher launcher) {
+ mLauncher = launcher;
+ mHeaderList = new ArrayList<AppItemIndexedInfo>();
+ mDeviceProfile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+ mLayoutInflater = LayoutInflater.from(launcher);
+ initParams();
+ }
+
+ private void initParams() {
+ mIconParams = new
+ LinearLayout.LayoutParams(mDeviceProfile.folderCellWidthPx,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ mSpacerParams = new
+ LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
+ (int) Utilities.convertDpToPixel(SCRUBBER_MARGIN_FROM_BOTTOM_DP, mLauncher));
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+ mIconRect = new Rect(0, 0, grid.allAppsIconSizePx, grid.allAppsIconSizePx);
+ }
+
+ /**
+ * Create and populate mHeaderList (buckets for app sorting)
+ * @param info
+ */
+ public void populateByCharacter(ArrayList<AppInfo> info) {
+ if (info == null || info.size() <= 0) {
+ return;
+ }
+
+ // Create a clone of AppInfo ArrayList to preserve data
+ ArrayList<AppInfo> tempInfo = new ArrayList<AppInfo>(info.size());
+ for (AppInfo i : info) {
+ tempInfo.add(i);
+ }
+
+ ListIterator<AppInfo> it = tempInfo.listIterator();
+ ArrayList<AppInfo> appInfos = new ArrayList<AppInfo>();
+ appInfos.clear();
+
+ // get next app
+ AppInfo app = it.next();
+
+ // get starting character
+ boolean isSpecial = false;
+ char startChar = app.title.toString().toUpperCase().charAt(0);
+ if (!Character.isLetter(startChar)) {
+ isSpecial = true;
+ }
+
+ // now iterate through
+ for (AppInfo info1 : tempInfo) {
+ char newChar = info1.title.toString().toUpperCase().charAt(0);
+ // if same character
+ if (newChar == startChar) {
+ // add it
+ appInfos.add(info1);
+ } else if (isSpecial && !Character.isLetter(newChar)) {
+ appInfos.add(info1);
+ }
+ }
+
+ for (int i = 0; i < appInfos.size(); i += mDeviceProfile.numColumnsBase) {
+ int endIndex = (int) Math.min(i + mDeviceProfile.numColumnsBase, appInfos.size());
+ ArrayList<AppInfo> subList = new ArrayList<AppInfo>(appInfos.subList(i, endIndex));
+ AppItemIndexedInfo indexInfo;
+ if (isSpecial) {
+ indexInfo = new AppItemIndexedInfo('#', subList, i != 0);
+ } else {
+ indexInfo = new AppItemIndexedInfo(startChar, subList, i != 0);
+ }
+ mHeaderList.add(indexInfo);
+ }
+
+ for (AppInfo remove : appInfos) {
+ // remove from mApps
+ tempInfo.remove(remove);
+ }
+ populateByCharacter(tempInfo);
+ }
+
+ public void setApps(ArrayList<AppInfo> list) {
+ if (!LauncherAppState.isDisableAllApps()) {
+ mHeaderList.clear();
+ Collections.sort(list, LauncherModel.getAppNameComparator());
+ populateByCharacter(list);
+ populateSectionHeaders();
+ mLauncher.updateScrubber();
+ this.notifyDataSetChanged();
+ }
+ }
+
+ private void populateSectionHeaders() {
+ if (mSectionHeaders == null || mSectionHeaders.size() != mHeaderList.size()) {
+ mSectionHeaders = new LinkedHashMap<String, Integer>();
+ }
+ int count = 0;
+ for (int i = 0; i < mHeaderList.size(); i++) {
+ AppItemIndexedInfo info = mHeaderList.get(i);
+ if (!mHeaderList.get(i).isChild) {
+ mSectionHeaders.put(String.valueOf(mHeaderList.get(i).mChar), count);
+ }
+ if (info.mInfo.size() < mDeviceProfile.numColumnsBase) {
+ count++;
+ } else {
+ count += info.mInfo.size() / mDeviceProfile.numColumnsBase;
+ }
+ }
+ }
+
+ private void reset() {
+ ArrayList<AppInfo> infos = getAllApps();
+ setApps(infos);
+ }
+
+ private ArrayList<AppInfo> getAllApps() {
+ ArrayList<AppInfo> indexedInfos = new ArrayList<AppInfo>();
+
+ for (int j = 0; j < mHeaderList.size(); ++j) {
+ AppItemIndexedInfo indexedInfo = mHeaderList.get(j);
+ for (AppInfo info : indexedInfo.mInfo) {
+ indexedInfos.add(info);
+ }
+ }
+ return indexedInfos;
+ }
+
+ public void updateApps(ArrayList<AppInfo> 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.
+ if (!LauncherAppState.isDisableAllApps()) {
+ removeAppsWithoutInvalidate(list);
+ addAppsWithoutInvalidate(list);
+ reset();
+ }
+ }
+
+
+ public void addApps(ArrayList<AppInfo> list) {
+ if (!LauncherAppState.isDisableAllApps()) {
+ addAppsWithoutInvalidate(list);
+ reset();
+ }
+ }
+
+ private void addAppsWithoutInvalidate(ArrayList<AppInfo> list) {
+ // We add it in place, in alphabetical order
+ int count = list.size();
+ for (int i = 0; i < count; ++i) {
+ AppInfo info = list.get(i);
+ boolean found = false;
+ AppItemIndexedInfo lastInfoForSection = null;
+ for (int j = 0; j < mHeaderList.size(); ++j) {
+ AppItemIndexedInfo indexedInfo = mHeaderList.get(j);
+ if (info.title.charAt(0) == indexedInfo.mChar) {
+ Collections.sort(indexedInfo.mInfo, LauncherModel.getAppNameComparator());
+ int index =
+ Collections.binarySearch(indexedInfo.mInfo,
+ info, LauncherModel.getAppNameComparator());
+ if (index >= 0) {
+ found = true;
+ break;
+ } else {
+ lastInfoForSection = indexedInfo;
+ }
+ }
+ }
+ if (!found) {
+ if (lastInfoForSection != null) {
+ lastInfoForSection.mInfo.add(info);
+ } else {
+ // we need to create a new section
+ ArrayList<AppInfo> newInfos = new ArrayList<AppInfo>();
+ newInfos.add(info);
+ AppItemIndexedInfo newInfo =
+ new AppItemIndexedInfo(info.title.charAt(0), newInfos, false);
+ mHeaderList.add(newInfo);
+ }
+ }
+ }
+ }
+
+ public void removeApps(ArrayList<AppInfo> appInfos) {
+ if (!LauncherAppState.isDisableAllApps()) {
+ removeAppsWithoutInvalidate(appInfos);
+ //recreate everything
+ reset();
+ }
+ }
+
+ private void removeAppsWithoutInvalidate(ArrayList<AppInfo> list) {
+ // loop through all the apps and remove apps that have the same component
+ int length = list.size();
+ for (int i = 0; i < length; ++i) {
+ AppInfo info = list.get(i);
+ for (int j = 0; j < mHeaderList.size(); ++j) {
+ AppItemIndexedInfo indexedInfo = mHeaderList.get(j);
+ ArrayList<AppInfo> clonedIndexedInfoApps =
+ (ArrayList<AppInfo>) indexedInfo.mInfo.clone();
+ int index =
+ findAppByComponent(clonedIndexedInfoApps, info);
+ if (index > -1) {
+ indexedInfo.mInfo.remove(info);
+ }
+ }
+ }
+ }
+
+ private int findAppByComponent(List<AppInfo> list, AppInfo item) {
+ ComponentName removeComponent = item.intent.getComponent();
+ int length = list.size();
+ for (int i = 0; i < length; ++i) {
+ AppInfo info = list.get(i);
+ if (info.intent.getComponent().equals(removeComponent)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /*
+ * AllAppsView implementation
+ */
+ public void setup(Launcher launcher) {
+ mLauncher = launcher;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(parent.getContext()).
+ inflate(R.layout.app_drawer_item, parent, false);
+ ViewHolder holder = new ViewHolder(v);
+ holder.mSpacer.setLayoutParams(mSpacerParams);
+ for (int i = 0; i < mDeviceProfile.numColumnsBase; i++) {
+ AppDrawerIconView icon = (AppDrawerIconView) mLayoutInflater.inflate(
+ R.layout.drawer_icon, holder.mLayout, false);
+ icon.setLayoutParams(mIconParams);
+ icon.setOnClickListener(mLauncher);
+ icon.setOnLongClickListener(this);
+ int padding = (int) mLauncher.getResources()
+ .getDimension(R.dimen.vertical_app_drawer_icon_padding);
+ icon.setPadding(padding, padding, padding, padding);
+ holder.mLayout.addView(icon);
+ }
+ return holder;
+ }
+
+ @Override
+ public int getItemCount() {
+ return mHeaderList.size();
+ }
+
+ public AppItemIndexedInfo getItemAt(int position) {
+ if (position < mHeaderList.size())
+ return mHeaderList.get(position);
+ return null;
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ AppItemIndexedInfo indexedInfo = mHeaderList.get(position);
+ holder.mTextView.setVisibility(indexedInfo.isChild ? View.INVISIBLE : View.VISIBLE);
+ if (!indexedInfo.isChild) {
+ if (indexedInfo.mChar == NUMERIC_OR_SPECIAL_CHAR) {
+ holder.mTextView.setText(NUMERIC_OR_SPECIAL_HEADER);
+ } else {
+ holder.mTextView.setText(String.valueOf(indexedInfo.mChar));
+ }
+ }
+ final int size = indexedInfo.mInfo.size();
+ for (int i = 0; i < holder.mLayout.getChildCount(); i++) {
+ AppDrawerIconView icon = (AppDrawerIconView) holder.mLayout.getChildAt(i);
+ if (i >= size) {
+ icon.setVisibility(View.INVISIBLE);
+ } else {
+ icon.setVisibility(View.VISIBLE);
+ AppInfo info = indexedInfo.mInfo.get(i);
+ icon.setTag(info);
+ Drawable d = Utilities.createIconDrawable(info.iconBitmap);
+ d.setBounds(mIconRect);
+ icon.mIcon.setImageDrawable(d);
+ icon.mLabel.setText(info.title);
+ }
+ }
+ if (position == getItemCount() - 1) {
+ holder.mSpacer.setVisibility(View.VISIBLE);
+ } else {
+ holder.mSpacer.setVisibility(View.GONE);
+ }
+ holder.itemView.setTag(indexedInfo);
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (v instanceof AppDrawerIconView) {
+ beginDraggingApplication(v);
+ mLauncher.showWorkspace();
+ }
+ return false;
+ }
+
+ @Override
+ public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
+ boolean success) {
+ // Return early and wait for onFlingToDeleteCompleted if this was the result of a fling
+ if (isFlingToDelete) return;
+
+ endDragging(target, false, success);
+
+ // Display an error message if the drag failed due to there not being enough space on the
+ // target layout we were dropping on.
+ if (!success) {
+ boolean showOutOfSpaceMessage = false;
+ if (target instanceof Workspace) {
+ int currentScreen = mLauncher.getCurrentWorkspaceScreen();
+ Workspace workspace = (Workspace) target;
+ CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
+ ItemInfo itemInfo = (ItemInfo) d.dragInfo;
+ if (layout != null) {
+ layout.calculateSpans(itemInfo);
+ showOutOfSpaceMessage =
+ !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
+ }
+ }
+ if (showOutOfSpaceMessage) {
+ mLauncher.showOutOfSpaceMessage(false);
+ }
+
+ d.deferDragViewCleanupPostAnimation = false;
+ }
+ }
+
+ /**
+ * Clean up after dragging.
+ *
+ * @param target where the item was dragged to (can be null if the item was flung)
+ */
+ private void endDragging(View target, boolean isFlingToDelete, boolean success) {
+ if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
+ !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
+ // Exit spring loaded mode if we have not successfully dropped or have not handled the
+ // drop in Workspace
+ mLauncher.getWorkspace().removeExtraEmptyScreenDelayed(true, new Runnable() {
+ @Override
+ public void run() {
+ mLauncher.exitSpringLoadedDragMode();
+ mLauncher.unlockScreenOrientation(false);
+ }
+ }, 0, true);
+ } else {
+ mLauncher.unlockScreenOrientation(false);
+ }
+ }
+
+ @Override
+ public boolean supportsFlingToDelete() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsAppInfoDropTarget() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsDeleteDropTarget() {
+ return false;
+ }
+
+ @Override
+ public float getIntrinsicIconScaleFactor() {
+ return (float) mDeviceProfile.allAppsIconSizePx / mDeviceProfile.iconSizePx;
+ }
+
+ private void beginDraggingApplication(View v) {
+ // mLauncher.getWorkspace().onDragStartedWithItem(v);
+ mLauncher.getWorkspace().beginDragShared(v, this);
+ }
+
+ @Override
+ public void onFlingToDeleteCompleted() {
+ // We just dismiss the drag when we fling, so cleanup here
+ }
+
+ public class AppItemIndexedInfo {
+ private boolean isChild;
+ private char mChar;
+ private ArrayList<AppInfo> mInfo;
+
+ private AppItemIndexedInfo(char startChar, ArrayList<AppInfo> info, boolean isChild) {
+ this.mChar = startChar;
+ this.mInfo = info;
+ this.isChild = isChild;
+ }
+
+ public char getChar() {
+ return mChar;
+ }
+ }
+
+ @Override
+ public Object[] getSections() {
+ return mSectionHeaders.keySet().toArray(new String[mSectionHeaders.size()]);
+ }
+
+ @Override
+ public int getPositionForSection(int sectionIndex) {
+ return mSectionHeaders.get(getSections()[sectionIndex]);
+ }
+
+ @Override
+ public int getSectionForPosition(int position) {
+ return mSectionHeaders.get(mHeaderList.get(position).mChar);
+ }
+}
diff --git a/src/com/android/launcher3/AppDrawerScrubber.java b/src/com/android/launcher3/AppDrawerScrubber.java
new file mode 100644
index 000000000..41c3199b5
--- /dev/null
+++ b/src/com/android/launcher3/AppDrawerScrubber.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.LinearLayoutManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.SectionIndexer;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class AppDrawerScrubber extends LinearLayout implements OnClickListener {
+
+ private final int SCRUBBER_INDICATOR_DISPLAY_DURATION = 200;
+ private final float SCRUBBER_INDICATOR_DISPLAY_TRANSLATIONY = 20f;
+
+ private AppDrawerListAdapter mAdapter;
+ private RecyclerView mListView;
+ private TextView mFirstIndicator, mLastIndicator;
+ private TextView mScrubberIndicator;
+ private SeekBar mSeekBar;
+ private String[] mSections;
+ private LinearLayoutManager mLayoutManager;
+
+ public AppDrawerScrubber(Context context) {
+ super(context);
+ LayoutInflater.from(context).inflate(R.layout.scrub_layout, this);
+ mFirstIndicator = ((TextView) findViewById(R.id.firstSection));
+ mFirstIndicator.setOnClickListener(this);
+ mLastIndicator = ((TextView) findViewById(R.id.lastSection));
+ mLastIndicator.setOnClickListener(this);
+ mScrubberIndicator = (TextView) findViewById(R.id.scrubberIndicator);
+ mSeekBar = (SeekBar) findViewById(R.id.scrubber);
+ init();
+ }
+
+ public void updateSections() {
+ mSections = (String[]) mAdapter.getSections();
+ mSeekBar.setMax(mSections.length - 1);
+ mFirstIndicator.setText(mSections[0]);
+ mLastIndicator.setText(mSections[mSections.length - 1]);
+ }
+
+ public void setSource(RecyclerView listView) {
+ mListView = listView;
+ mAdapter = (AppDrawerListAdapter) listView.getAdapter();
+ mLayoutManager = (LinearLayoutManager) listView.getLayoutManager();
+ }
+
+ private boolean isReady() {
+ return mListView != null &&
+ mAdapter != null &&
+ mSections != null;
+ }
+
+ private void init() {
+ mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, final int progress, boolean fromUser) {
+ if (!isReady()) {
+ return;
+ }
+ resetScrubber();
+ mScrubberIndicator.setTranslationX((progress * seekBar.getWidth()) /
+ mSections.length);
+ String section = String.valueOf(mSections[progress]);
+ mLayoutManager.scrollToPositionWithOffset(
+ mAdapter.getPositionForSection(progress), 0);
+ mScrubberIndicator.setText(section);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ if (!isReady()) {
+ return;
+ }
+ resetScrubber();
+ mScrubberIndicator.setAlpha(1f);
+ mScrubberIndicator.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ if (!isReady()) {
+ return;
+ }
+ resetScrubber();
+ mScrubberIndicator.animate()
+ .alpha(0f)
+ .translationYBy(SCRUBBER_INDICATOR_DISPLAY_TRANSLATIONY)
+ .setDuration(SCRUBBER_INDICATOR_DISPLAY_DURATION)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mScrubberIndicator.setVisibility(View.INVISIBLE);
+ }
+ });
+ }
+
+ private void resetScrubber() {
+ mScrubberIndicator.animate().cancel();
+ mScrubberIndicator.setTranslationY(0f);
+ }
+ });
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v == mFirstIndicator) {
+ int positionForFirstSection = mAdapter.getPositionForSection(0);
+ mLayoutManager.scrollToPositionWithOffset(positionForFirstSection, 0);
+ } else if (v == mLastIndicator) {
+ int positionForLastSection = mAdapter.getPositionForSection(mSections.length - 1);
+ mLayoutManager.scrollToPositionWithOffset(positionForLastSection, 0);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/AutoFitTextView.java b/src/com/android/launcher3/AutoFitTextView.java
new file mode 100644
index 000000000..208dd4073
--- /dev/null
+++ b/src/com/android/launcher3/AutoFitTextView.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2014 Grantland Chew
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.text.method.TransformationMethod;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.TypedValue;
+import android.widget.TextView;
+
+/**
+ * A TextView that resizes it's text to be no larger than the width of the view.
+ *
+ * @author Grantland Chew <grantlandchew@gmail.com>
+ */
+public class AutoFitTextView extends TextView {
+
+ private static final String TAG = "AutoFitTextView";
+ private static final boolean SPEW = false;
+
+ // Minimum size of the text in pixels
+ private static final int DEFAULT_MIN_TEXT_SIZE = 8; //sp
+ // How precise we want to be when reaching the target textWidth size
+ private static final float PRECISION = 0.5f;
+
+ // Attributes
+ private boolean mSizeToFit;
+ private int mMaxLines;
+ private float mMinTextSize;
+ private float mMaxTextSize;
+ private float mPrecision;
+ private TextPaint mPaint;
+
+ public AutoFitTextView(Context context) {
+ super(context);
+ init(context, null, 0);
+ }
+
+ public AutoFitTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs, 0);
+ }
+
+ public AutoFitTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context, attrs, defStyle);
+ }
+
+ private void init(Context context, AttributeSet attrs, int defStyle) {
+ float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
+ boolean sizeToFit = true;
+ int minTextSize = (int) scaledDensity * DEFAULT_MIN_TEXT_SIZE;
+ float precision = PRECISION;
+
+ if (attrs != null) {
+ TypedArray ta = context.obtainStyledAttributes(
+ attrs,
+ R.styleable.AutofitTextView,
+ defStyle,
+ 0);
+ sizeToFit = ta.getBoolean(R.styleable.AutofitTextView_sizeToFit, sizeToFit);
+ minTextSize = ta.getDimensionPixelSize(R.styleable.AutofitTextView_minTextSize,
+ minTextSize);
+ precision = ta.getFloat(R.styleable.AutofitTextView_precision, precision);
+ ta.recycle();
+ }
+
+ mPaint = new TextPaint();
+ setSizeToFit(sizeToFit);
+ setRawTextSize(super.getTextSize());
+ setRawMinTextSize(minTextSize);
+ setPrecision(precision);
+ }
+
+ // Getters and Setters
+
+ /**
+ * @return whether or not the text will be automatically resized to fit its constraints.
+ */
+ public boolean isSizeToFit() {
+ return mSizeToFit;
+ }
+
+ /**
+ * Sets the property of this field (singleLine, to automatically resize the text to fit its constraints.
+ */
+ public void setSizeToFit() {
+ setSizeToFit(true);
+ }
+
+ /**
+ * If true, the text will automatically be resized to fit its constraints; if false, it will
+ * act like a normal TextView.
+ *
+ * @param sizeToFit
+ */
+ public void setSizeToFit(boolean sizeToFit) {
+ mSizeToFit = sizeToFit;
+ refitText();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public float getTextSize() {
+ return mMaxTextSize;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setTextSize(int unit, float size) {
+ Context context = getContext();
+ Resources r = Resources.getSystem();
+
+ if (context != null) {
+ r = context.getResources();
+ }
+
+ setRawTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()));
+ }
+
+ private void setRawTextSize(float size) {
+ if (size != mMaxTextSize) {
+ mMaxTextSize = size;
+ refitText();
+ }
+ }
+
+ /**
+ * @return the minimum size (in pixels) of the text size in this AutofitTextView
+ */
+ public float getMinTextSize() {
+ return mMinTextSize;
+ }
+
+ /**
+ * Set the minimum text size to a given unit and value. See TypedValue for the possible
+ * dimension units.
+ *
+ * @param unit The desired dimension unit.
+ * @param minSize The desired size in the given units.
+ *
+ * @attr ref me.grantland.R.styleable#AutofitTextView_minTextSize
+ */
+ public void setMinTextSize(int unit, float minSize) {
+ Context context = getContext();
+ Resources r = Resources.getSystem();
+
+ if (context != null) {
+ r = context.getResources();
+ }
+
+ setRawMinTextSize(TypedValue.applyDimension(unit, minSize, r.getDisplayMetrics()));
+ }
+
+ /**
+ * Set the minimum text size to the given value, interpreted as "scaled pixel" units. This size
+ * is adjusted based on the current density and user font size preference.
+ *
+ * @param minSize The scaled pixel size.
+ *
+ * @attr ref me.grantland.R.styleable#AutofitTextView_minTextSize
+ */
+ public void setMinTextSize(int minSize) {
+ setMinTextSize(TypedValue.COMPLEX_UNIT_SP, minSize);
+ }
+
+ private void setRawMinTextSize(float minSize) {
+ if (minSize != mMinTextSize) {
+ mMinTextSize = minSize;
+ refitText();
+ }
+ }
+
+ /**
+ * @return the amount of precision used to calculate the correct text size to fit within it's
+ * bounds.
+ */
+ public float getPrecision() {
+ return mPrecision;
+ }
+
+ /**
+ * Set the amount of precision used to calculate the correct text size to fit within it's
+ * bounds. Lower precision is more precise and takes more time.
+ *
+ * @param precision The amount of precision.
+ */
+ public void setPrecision(float precision) {
+ if (precision != mPrecision) {
+ mPrecision = precision;
+ refitText();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setLines(int lines) {
+ super.setLines(lines);
+ mMaxLines = lines;
+ refitText();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getMaxLines() {
+ return mMaxLines;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setMaxLines(int maxLines) {
+ super.setMaxLines(maxLines);
+ if (maxLines != mMaxLines) {
+ mMaxLines = maxLines;
+ refitText();
+ }
+ }
+
+ /**
+ * Re size the font so the specified text fits in the text box assuming the text box is the
+ * specified width.
+ */
+ private void refitText() {
+ if (!mSizeToFit) {
+ return;
+ }
+
+ if (mMaxLines <= 0) {
+ // Don't auto-size since there's no limit on lines.
+ return;
+ }
+
+ CharSequence text = getText();
+ TransformationMethod method = getTransformationMethod();
+ if (method != null) {
+ text = method.getTransformation(text, this);
+ }
+ int targetWidth = getWidth() - getPaddingLeft() - getPaddingRight();
+ if (targetWidth > 0) {
+ Context context = getContext();
+ Resources r = Resources.getSystem();
+ DisplayMetrics displayMetrics;
+
+ float size = mMaxTextSize;
+ float high = size;
+ float low = 0;
+
+ if (context != null) {
+ r = context.getResources();
+ }
+ displayMetrics = r.getDisplayMetrics();
+
+ mPaint.set(getPaint());
+ mPaint.setTextSize(size);
+
+ if ((mMaxLines == 1 && mPaint.measureText(text, 0, text.length()) > targetWidth)
+ || getLineCount(text, mPaint, size, targetWidth, displayMetrics) > mMaxLines) {
+ size = getTextSize(text, mPaint, targetWidth, mMaxLines, low, high, mPrecision,
+ displayMetrics);
+ }
+
+ if (size < mMinTextSize) {
+ size = mMinTextSize;
+ }
+
+ super.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+ }
+ }
+
+ /**
+ * Recursive binary search to find the best size for the text
+ */
+ private static float getTextSize(CharSequence text, TextPaint paint,
+ float targetWidth, int maxLines,
+ float low, float high, float precision,
+ DisplayMetrics displayMetrics) {
+ float mid = (low + high) / 2.0f;
+ int lineCount = 1;
+ StaticLayout layout = null;
+
+ paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, mid,
+ displayMetrics));
+
+ if (maxLines != 1) {
+ layout = new StaticLayout(text, paint, (int)targetWidth, Layout.Alignment.ALIGN_NORMAL,
+ 1.0f, 0.0f, true);
+ lineCount = layout.getLineCount();
+ }
+
+ if (SPEW) Log.d(TAG, "low=" + low + " high=" + high + " mid=" + mid +
+ " target=" + targetWidth + " maxLines=" + maxLines + " lineCount=" + lineCount);
+
+ if (lineCount > maxLines) {
+ return getTextSize(text, paint, targetWidth, maxLines, low, mid, precision,
+ displayMetrics);
+ }
+ else if (lineCount < maxLines) {
+ return getTextSize(text, paint, targetWidth, maxLines, mid, high, precision,
+ displayMetrics);
+ }
+ else {
+ float maxLineWidth = 0;
+ if (maxLines == 1) {
+ maxLineWidth = paint.measureText(text, 0, text.length());
+ } else {
+ for (int i = 0; i < lineCount; i++) {
+ if (layout.getLineWidth(i) > maxLineWidth) {
+ maxLineWidth = layout.getLineWidth(i);
+ }
+ }
+ }
+
+ if ((high - low) < precision) {
+ return low;
+ } else if (maxLineWidth > targetWidth) {
+ return getTextSize(text, paint, targetWidth, maxLines, low, mid, precision,
+ displayMetrics);
+ } else if (maxLineWidth < targetWidth) {
+ return getTextSize(text, paint, targetWidth, maxLines, mid, high, precision,
+ displayMetrics);
+ } else {
+ return mid;
+ }
+ }
+ }
+
+ private static int getLineCount(CharSequence text, TextPaint paint, float size, float width,
+ DisplayMetrics displayMetrics) {
+ paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, size,
+ displayMetrics));
+ StaticLayout layout = new StaticLayout(text, paint, (int)width,
+ Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true);
+ return layout.getLineCount();
+ }
+
+ @Override
+ protected void onTextChanged(final CharSequence text, final int start,
+ final int lengthBefore, final int lengthAfter) {
+ super.onTextChanged(text, start, lengthBefore, lengthAfter);
+ refitText();
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ if (w != oldw) {
+ refitText();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index a368796bd..d0fb5c45a 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -42,11 +42,11 @@ public class BubbleTextView extends TextView {
private static SparseArray<Theme> sPreloaderThemes = new SparseArray<>(2);
- private static final float SHADOW_LARGE_RADIUS = 4.0f;
- private static final float SHADOW_SMALL_RADIUS = 1.75f;
- private static final float SHADOW_Y_OFFSET = 2.0f;
- private static final int SHADOW_LARGE_COLOUR = 0xDD000000;
- private static final int SHADOW_SMALL_COLOUR = 0xCC000000;
+ public static final float SHADOW_LARGE_RADIUS = 4.0f;
+ public static final float SHADOW_SMALL_RADIUS = 1.75f;
+ public static final float SHADOW_Y_OFFSET = 2.0f;
+ public static final int SHADOW_LARGE_COLOUR = 0xDD000000;
+ public static final int SHADOW_SMALL_COLOUR = 0xCC000000;
static final float PADDING_V = 3.0f;
private HolographicOutlineHelper mOutlineHelper;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8de95e9ff..d2ba9d28e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -70,6 +70,8 @@ import android.os.Message;
import android.os.StrictMode;
import android.os.SystemClock;
import android.speech.RecognizerIntent;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
@@ -297,6 +299,9 @@ public class Launcher extends Activity
OverviewSettingsPanel mOverviewSettingsPanel;
private View mAllAppsButton;
+ protected RecyclerView mAppDrawer;
+ private AppDrawerListAdapter mAppDrawerAdapter;
+ private AppDrawerScrubber mScrubber;
protected SearchDropTargetBar mSearchDropTargetBar;
private AppsCustomizeTabHost mAppsCustomizeTabHost;
@@ -596,6 +601,26 @@ public class Launcher extends Activity
protected void populateCustomContentContainer() {
}
+ private void initializeScrubber() {
+ if (mScrubber == null) {
+ mScrubber = new AppDrawerScrubber(this);
+ int scrubberPadding = getResources()
+ .getDimensionPixelSize(R.dimen.scrubber_bottom_padding);
+ mScrubber.setPadding(0, 0, 0, scrubberPadding);
+ mScrubber.setGravity(Gravity.BOTTOM);
+ mScrubber.setSource(mAppDrawer);
+ }
+ }
+
+ public void updateScrubber() {
+ mScrubber.updateSections();
+ }
+
+ public void initializeAdapter() {
+ mAppDrawerAdapter = new AppDrawerListAdapter(this);
+ mAppDrawerAdapter.notifyDataSetChanged();
+ }
+
/**
* Invoked by subclasses to signal a change to the {@link #addCustomContentToLeft} value to
* ensure the custom content page is added or removed if necessary.
@@ -1644,6 +1669,9 @@ public class Launcher extends Activity
mAppsCustomizeTabHost.findViewById(R.id.apps_customize_pane_content);
mAppsCustomizeContent.setup(this, dragController);
+ // Setup AppDrawer
+ setupAppDrawer();
+
// Setup the drag controller (drop targets have to be added in reverse order in priority)
dragController.setDragScoller(mWorkspace);
dragController.setScrollView(mDragLayer);
@@ -1669,6 +1697,22 @@ public class Launcher extends Activity
}
}
+ private void setupAppDrawer() {
+ if (mAppDrawer == null) {
+ FrameLayout view = (FrameLayout) findViewById(R.id.app_drawer_container);
+ mAppDrawer = new RecyclerView(this);
+ mAppDrawer.setLayoutManager(new LinearLayoutManager(this));
+ if (mAppDrawerAdapter == null) {
+ initializeAdapter();
+ }
+ mAppDrawer.setHasFixedSize(true);
+ mAppDrawer.setAdapter(mAppDrawerAdapter);
+ view.addView(mAppDrawer);
+ initializeScrubber();
+ view.addView(mScrubber);
+ }
+ }
+
/**
* Sets the all apps button. This method is called from {@link Hotseat}.
*/
@@ -3205,7 +3249,7 @@ public class Launcher extends Activity
return false;
}
- boolean startActivitySafely(View v, Intent intent, Object tag) {
+ public boolean startActivitySafely(View v, Intent intent, Object tag) {
boolean success = false;
if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
@@ -3594,7 +3638,13 @@ public class Launcher extends Activity
final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
final View fromView = mWorkspace;
- final AppsCustomizeTabHost toView = mAppsCustomizeTabHost;
+ final View toView;
+
+ if (contentType == AppsCustomizePagedView.ContentType.Applications) {
+ toView = (FrameLayout) findViewById(R.id.app_drawer_container);
+ } else {
+ toView = mAppsCustomizeTabHost;
+ }
final ArrayList<View> layerViews = new ArrayList<View>();
@@ -3616,20 +3666,18 @@ public class Launcher extends Activity
final AppsCustomizePagedView content = (AppsCustomizePagedView)
toView.findViewById(R.id.apps_customize_pane_content);
- final View page = content.getPageAt(content.getCurrentPage());
+ final View page = content != null ? content.getPageAt(content.getCurrentPage()) : null;
final View revealView = toView.findViewById(R.id.fake_page);
final float initialPanelAlpha = 1f;
final boolean isWidgetTray = contentType == AppsCustomizePagedView.ContentType.Widgets;
- if (isWidgetTray) {
- revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
- } else {
- revealView.setBackground(res.getDrawable(R.drawable.quantum_panel));
- }
+ revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
// Hide the real page background, and swap in the fake one
- content.setPageBackgroundsVisible(false);
+ if (content != null) {
+ content.setPageBackgroundsVisible(false);
+ }
revealView.setVisibility(View.VISIBLE);
// We need to hide this view as the animation start will be posted.
revealView.setAlpha(0);
@@ -3695,11 +3743,13 @@ public class Launcher extends Activity
}
View pageIndicators = toView.findViewById(R.id.apps_customize_page_indicator);
- pageIndicators.setAlpha(0.01f);
- ObjectAnimator indicatorsAlpha =
- ObjectAnimator.ofFloat(pageIndicators, "alpha", 1f);
- indicatorsAlpha.setDuration(revealDuration);
- mStateAnimation.play(indicatorsAlpha);
+ if (pageIndicators != null) {
+ pageIndicators.setAlpha(0.01f);
+ ObjectAnimator indicatorsAlpha =
+ ObjectAnimator.ofFloat(pageIndicators, "alpha", 1f);
+ indicatorsAlpha.setDuration(revealDuration);
+ mStateAnimation.play(indicatorsAlpha);
+ }
if (material) {
final View allApps = getAllAppsButton();
@@ -3737,7 +3787,9 @@ public class Launcher extends Activity
if (page != null) {
page.setLayerType(View.LAYER_TYPE_NONE, null);
}
- content.setPageBackgroundsVisible(true);
+ if (content != null) {
+ content.setPageBackgroundsVisible(true);
+ }
// Hide the search bar
if (mSearchDropTargetBar != null) {
@@ -3830,7 +3882,14 @@ public class Launcher extends Activity
final float scaleFactor = (float)
res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
- final View fromView = mAppsCustomizeTabHost;
+ final View fromView;
+
+ if (mAppsCustomizeContent.getContentType() != AppsCustomizePagedView.ContentType.Widgets) {
+ fromView = (FrameLayout) findViewById(R.id.app_drawer_container);
+ } else {
+ fromView = mAppsCustomizeTabHost;
+ }
+
final View toView = mWorkspace;
Animator workspaceAnim = null;
final ArrayList<View> layerViews = new ArrayList<View>();
@@ -3856,10 +3915,10 @@ public class Launcher extends Activity
final AppsCustomizePagedView content = (AppsCustomizePagedView)
fromView.findViewById(R.id.apps_customize_pane_content);
- final View page = content.getPageAt(content.getNextPage());
+ final View page = content != null ? content.getPageAt(content.getNextPage()) : null;
// We need to hide side pages of the Apps / Widget tray to avoid some ugly edge cases
- int count = content.getChildCount();
+ int count = content != null ? content.getChildCount() : 0;
for (int i = 0; i < count; i++) {
View child = content.getChildAt(i);
if (child != page) {
@@ -3872,15 +3931,12 @@ public class Launcher extends Activity
// don't perform all these no-op animations. In particularly, this was causing
// the all-apps button to pop in and out.
if (fromView.getVisibility() == View.VISIBLE) {
- AppsCustomizePagedView.ContentType contentType = content.getContentType();
+ AppsCustomizePagedView.ContentType contentType =
+ mAppsCustomizeContent.getContentType();
final boolean isWidgetTray =
contentType == AppsCustomizePagedView.ContentType.Widgets;
- if (isWidgetTray) {
- revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
- } else {
- revealView.setBackground(res.getDrawable(R.drawable.quantum_panel));
- }
+ revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
int width = revealView.getMeasuredWidth();
int height = revealView.getMeasuredHeight();
@@ -3888,7 +3944,9 @@ public class Launcher extends Activity
// Hide the real page background, and swap in the fake one
revealView.setVisibility(View.VISIBLE);
- content.setPageBackgroundsVisible(false);
+ if (content != null) {
+ content.setPageBackgroundsVisible(false);
+ }
final View allAppsButton = getAllAppsButton();
revealView.setTranslationY(0);
@@ -3957,12 +4015,14 @@ public class Launcher extends Activity
}
View pageIndicators = fromView.findViewById(R.id.apps_customize_page_indicator);
- pageIndicators.setAlpha(1f);
- ObjectAnimator indicatorsAlpha =
- LauncherAnimUtils.ofFloat(pageIndicators, "alpha", 0f);
- indicatorsAlpha.setDuration(revealDuration);
- indicatorsAlpha.setInterpolator(new DecelerateInterpolator(1.5f));
- mStateAnimation.play(indicatorsAlpha);
+ if (pageIndicators != null) {
+ pageIndicators.setAlpha(1f);
+ ObjectAnimator indicatorsAlpha =
+ LauncherAnimUtils.ofFloat(pageIndicators, "alpha", 0f);
+ indicatorsAlpha.setDuration(revealDuration);
+ indicatorsAlpha.setInterpolator(new DecelerateInterpolator(1.5f));
+ mStateAnimation.play(indicatorsAlpha);
+ }
width = revealView.getMeasuredWidth();
@@ -4011,9 +4071,11 @@ public class Launcher extends Activity
if (page != null) {
page.setLayerType(View.LAYER_TYPE_NONE, null);
}
- content.setPageBackgroundsVisible(true);
+ if (content != null) {
+ content.setPageBackgroundsVisible(true);
+ }
// Unhide side pages
- int count = content.getChildCount();
+ int count = content != null ? content.getChildCount() : 0;
for (int i = 0; i < count; i++) {
View child = content.getChildAt(i);
child.setVisibility(View.VISIBLE);
@@ -4025,7 +4087,9 @@ public class Launcher extends Activity
page.setTranslationY(0);
page.setAlpha(1);
}
- content.setCurrentPage(content.getNextPage());
+ if (content != null) {
+ content.setCurrentPage(content.getNextPage());
+ }
mAppsCustomizeContent.updateCurrentPageScroll();
}
@@ -4676,6 +4740,7 @@ public class Launcher extends Activity
if (!LauncherAppState.isDisableAllApps() &&
addedApps != null && mAppsCustomizeContent != null) {
mAppsCustomizeContent.addApps(addedApps);
+ mAppDrawerAdapter.addApps(addedApps);
}
}
@@ -5059,6 +5124,9 @@ public class Launcher extends Activity
LauncherModel.getSortedWidgetsAndShortcuts(this));
}
} else {
+ if (mAppDrawerAdapter != null) {
+ mAppDrawerAdapter.setApps(apps);
+ }
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.setApps(apps);
mAppsCustomizeContent.onPackagesUpdated(
@@ -5089,6 +5157,7 @@ public class Launcher extends Activity
if (!LauncherAppState.isDisableAllApps() &&
mAppsCustomizeContent != null) {
mAppsCustomizeContent.updateApps(apps);
+ mAppDrawerAdapter.updateApps(apps);
}
}
@@ -5168,6 +5237,7 @@ public class Launcher extends Activity
if (!LauncherAppState.isDisableAllApps() &&
mAppsCustomizeContent != null) {
mAppsCustomizeContent.removeApps(appInfos);
+ mAppDrawerAdapter.removeApps(appInfos);
}
}
@@ -5425,16 +5495,24 @@ public class Launcher extends Activity
if (mWorkspace != null) mWorkspace.setAlpha(1f);
if (mHotseat != null) mHotseat.setAlpha(1f);
if (mPageIndicators != null) mPageIndicators.setAlpha(1f);
- if (mSearchDropTargetBar != null) mSearchDropTargetBar.showSearchBar(false);
+ showSearch();
}
void hideWorkspaceSearchAndHotseat() {
if (mWorkspace != null) mWorkspace.setAlpha(0f);
if (mHotseat != null) mHotseat.setAlpha(0f);
if (mPageIndicators != null) mPageIndicators.setAlpha(0f);
+ hideSearch();
+ }
+
+ void hideSearch() {
if (mSearchDropTargetBar != null) mSearchDropTargetBar.hideSearchBar(false);
}
+ void showSearch() {
+ if (mSearchDropTargetBar != null) mSearchDropTargetBar.showSearchBar(false);
+ }
+
public ItemInfo createAppDragInfo(Intent appLaunchIntent) {
// Called from search suggestion, not supported in other profiles.
final UserHandleCompat myUser = UserHandleCompat.myUserHandle();
diff --git a/src/com/android/launcher3/PagedViewIcon.java b/src/com/android/launcher3/PagedViewIcon.java
new file mode 100644
index 000000000..660251e31
--- /dev/null
+++ b/src/com/android/launcher3/PagedViewIcon.java
@@ -0,0 +1,134 @@
+/*
+ * 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.launcher3;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Region;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.widget.TextView;
+
+/**
+ * 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 {
+ /** A simple callback interface to allow a PagedViewIcon to notify when it has been pressed */
+ public static interface PressedCallback {
+ void iconPressed(PagedViewIcon icon);
+ }
+
+ @SuppressWarnings("unused")
+ private static final String TAG = "PagedViewIcon";
+ static final float PRESS_ALPHA = 0.4f;
+
+ private PagedViewIcon.PressedCallback mPressedCallback;
+ private boolean mLockDrawableState = false;
+
+ private Bitmap mIcon;
+
+ 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);
+ }
+
+ public void onFinishInflate() {
+ super.onFinishInflate();
+
+ // Ensure we are using the right text size
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+ setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
+ }
+
+ public void applyFromApplicationInfo(AppInfo info, boolean scaleUp,
+ PagedViewIcon.PressedCallback cb) {
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+
+ mIcon = info.iconBitmap;
+ mPressedCallback = cb;
+ Drawable icon = Utilities.createIconDrawable(mIcon);
+ icon.setBounds(0, 0, grid.allAppsIconSizePx, grid.allAppsIconSizePx);
+ setCompoundDrawables(null, icon, null, null);
+ setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
+ setText(info.title);
+ setTag(info);
+ }
+
+ public void lockDrawableState() {
+ mLockDrawableState = true;
+ }
+
+ public void resetDrawableState() {
+ mLockDrawableState = false;
+ post(new Runnable() {
+ @Override
+ public void run() {
+ refreshDrawableState();
+ }
+ });
+ }
+
+ protected void drawableStateChanged() {
+ super.drawableStateChanged();
+
+ // We keep in the pressed state until resetDrawableState() is called to reset the press
+ // feedback
+ if (isPressed()) {
+ setAlpha(PRESS_ALPHA);
+ if (mPressedCallback != null) {
+ mPressedCallback.iconPressed(this);
+ }
+ } else if (!mLockDrawableState) {
+ setAlpha(1f);
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ // If text is transparent, don't draw any shadow
+ if (getCurrentTextColor() == getResources().getColor(android.R.color.transparent)) {
+ getPaint().clearShadowLayer();
+ super.draw(canvas);
+ return;
+ }
+
+ // We enhance the shadow by drawing the shadow twice
+ getPaint().setShadowLayer(BubbleTextView.SHADOW_LARGE_RADIUS, 0.0f,
+ BubbleTextView.SHADOW_Y_OFFSET, BubbleTextView.SHADOW_LARGE_COLOUR);
+ super.draw(canvas);
+ canvas.save(Canvas.CLIP_SAVE_FLAG);
+ canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
+ getScrollX() + getWidth(),
+ getScrollY() + getHeight(), Region.Op.INTERSECT);
+ getPaint().setShadowLayer(BubbleTextView.SHADOW_SMALL_RADIUS, 0.0f, 0.0f,
+ BubbleTextView.SHADOW_SMALL_COLOUR);
+ super.draw(canvas);
+ canvas.restore();
+ }
+}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index addd74cfc..fcd4b8587 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -41,6 +41,7 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
import android.os.Build;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
@@ -558,4 +559,11 @@ public final class Utilities {
}
return null;
}
+
+ public static float convertDpToPixel(float dp, Context context){
+ Resources resources = context.getResources();
+ DisplayMetrics metrics = resources.getDisplayMetrics();
+ float px = dp * (metrics.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT);
+ return px;
+ }
}