resolve merge conflicts of 40dfa3bf6 to ub-launcher3-master
Test: I solemnly swear I tested this conflict resolution.
Change-Id: Iee500631827ebc48c63c203fdcd0de888adcb135
diff --git a/Android.mk b/Android.mk
index 713d082..6cb40c5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -31,6 +31,7 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
$(call all-java-files-under, src_config) \
+ $(call all-java-files-under, src_flags) \
$(call all-proto-files-under, protos)
LOCAL_RESOURCE_DIR := \
diff --git a/build.gradle b/build.gradle
index 51ac5a1..d6dbbab 100644
--- a/build.gradle
+++ b/build.gradle
@@ -38,7 +38,7 @@
sourceSets {
main {
res.srcDirs = ['res']
- java.srcDirs = ['src', 'src_config']
+ java.srcDirs = ['src', 'src_flags']
manifest.srcFile 'AndroidManifest-common.xml'
proto.srcDirs 'protos/'
}
@@ -90,6 +90,7 @@
remove java
javanano {
option "java_package=launcher_log.proto|com.android.launcher3.userevent.nano"
+ option "java_package=launcher_dump.proto|com.android.launcher3.model.nano"
option "enum_style=java"
}
}
diff --git a/protos/launcher_dump.proto b/protos/launcher_dump.proto
new file mode 100644
index 0000000..dc8fbda
--- /dev/null
+++ b/protos/launcher_dump.proto
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+syntax = "proto2";
+
+option java_package = "com.android.launcher3.model";
+option java_outer_classname = "LauncherDumpProto";
+
+package model;
+
+message DumpTarget {
+ enum Type {
+ NONE = 0;
+ ITEM = 1;
+ CONTAINER = 2;
+ }
+
+ optional Type type = 1;
+ optional int32 page_id = 2;
+ optional int32 grid_x = 3;
+ optional int32 grid_y = 4;
+
+ // For container types only
+ optional ContainerType container_type = 5;
+
+ // For item types only
+ optional ItemType item_type = 6;
+
+ optional string package_name = 7; // All ItemTypes except UNKNOWN type
+ optional string component = 8; // All ItemTypes except UNKNOWN type
+ optional string item_id = 9; // For Pinned Shortcuts and appWidgetId
+
+ optional int32 span_x = 10 [default = 1];// Used for ItemType.WIDGET
+ optional int32 span_y = 11 [default = 1];// Used for ItemType.WIDGET
+ optional UserType user_type = 12;
+}
+
+// Used to define what type of item a Target would represent.
+enum ItemType {
+ UNKNOWN_ITEMTYPE = 0; // Launcher specific items
+ APP_ICON = 1; // Regular app icons
+ WIDGET = 2; // Elements from AppWidgetManager
+ SHORTCUT = 3; // ShortcutManager
+}
+
+// Used to define what type of container a Target would represent.
+enum ContainerType {
+ UNKNOWN_CONTAINERTYPE = 0;
+ WORKSPACE = 1;
+ HOTSEAT = 2;
+ FOLDER = 3;
+}
+
+// Used to define what type of control a Target would represent.
+enum UserType {
+ DEFAULT = 0;
+ WORK = 1;
+}
+
+// Main message;
+message LauncherImpression {
+ repeated DumpTarget targets = 1;
+}
diff --git a/res/drawable-v26/adaptive_icon_drawable_wrapper.xml b/res/drawable-v26/adaptive_icon_drawable_wrapper.xml
index 7ad8e2c..2d78b69 100644
--- a/res/drawable-v26/adaptive_icon_drawable_wrapper.xml
+++ b/res/drawable-v26/adaptive_icon_drawable_wrapper.xml
@@ -15,7 +15,7 @@
limitations under the License.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
- <background android:color="#FFE0E0E0"/>
+ <background android:drawable="@color/legacy_icon_background"/>
<foreground>
<com.android.launcher3.graphics.FixedScaleDrawable />
</foreground>
diff --git a/res/drawable/ic_setting.xml b/res/drawable/ic_setting.xml
index e89c158..b0009c5 100644
--- a/res/drawable/ic_setting.xml
+++ b/res/drawable/ic_setting.xml
@@ -19,6 +19,6 @@
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path
- android:fillColor="@color/workspace_icon_text_color"
+ android:fillColor="?android:attr/textColorPrimary"
android:pathData="M38.86 25.95c.08-.64 .14-1.29 .14-1.95s-.06-1.31-.14-1.95l4.23-3.31c.38-.3 .49-.84 .24-1.28l-4-6.93c-.25-.43-.77-.61-1.22-.43l-4.98 2.01c-1.03-.79-2.16-1.46-3.38-1.97L29 4.84c-.09-.47-.5-.84-1-.84h-8c-.5 0-.91 .37-.99 .84l-.75 5.3c-1.22 .51-2.35 1.17-3.38 1.97L9.9 10.1c-.45-.17-.97 0-1.22 .43l-4 6.93c-.25 .43-.14 .97 .24 1.28l4.22 3.31C9.06 22.69 9 23.34 9 24s.06 1.31 .14 1.95l-4.22 3.31c-.38 .3-.49 .84-.24 1.28l4 6.93c.25 .43 .77 .61 1.22 .43l4.98-2.01c1.03 .79 2.16 1.46 3.38 1.97l.75 5.3c.08 .47 .49 .84 .99 .84h8c.5 0 .91-.37 .99-.84l.75-5.3c1.22-.51 2.35-1.17 3.38-1.97l4.98 2.01c.45 .17 .97 0 1.22-.43l4-6.93c.25-.43 .14-.97-.24-1.28l-4.22-3.31zM24 31c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/>
</vector>
diff --git a/res/drawable/ic_wallpaper.xml b/res/drawable/ic_wallpaper.xml
index b7fcfbf..30f6d1a 100644
--- a/res/drawable/ic_wallpaper.xml
+++ b/res/drawable/ic_wallpaper.xml
@@ -19,6 +19,6 @@
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path
- android:fillColor="@color/workspace_icon_text_color"
+ android:fillColor="?android:attr/textColorPrimary"
android:pathData="M8 8h14V4H8C5.79 4 4 5.79 4 8v14h4V8zm12 18l-8 10h24l-6-8-4.06 5.42L20 26zm14-9c0-1.66-1.34-3-3-3s-3 1.34-3 3 1.34 3 3 3 3-1.34 3-3zm6-13H26v4h14v14h4V8c0-2.21-1.79-4-4-4zm0 36H26v4h14c2.21 0 4-1.79 4-4V26h-4v14zM8 26H4v14c0 2.21 1.79 4 4 4h14v-4H8V26z"/>
</vector>
diff --git a/res/drawable/ic_widget.xml b/res/drawable/ic_widget.xml
index 97706e3..6c1469d 100644
--- a/res/drawable/ic_widget.xml
+++ b/res/drawable/ic_widget.xml
@@ -19,6 +19,6 @@
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path
- android:fillColor="@color/workspace_icon_text_color"
+ android:fillColor="?android:attr/textColorPrimary"
android:pathData="M26 26v16h16V26H26zM6 42h16V26H6v16zM6 6v16h16V6H6zm27.31-2.63L22 14.69 33.31 26l11.31-11.31L33.31 3.37z"/>
</vector>
diff --git a/res/interpolator/folder_closing_interpolator.xml b/res/interpolator/folder_closing_interpolator.xml
new file mode 100644
index 0000000..e6e012e
--- /dev/null
+++ b/res/interpolator/folder_closing_interpolator.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2017, 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.
+*/
+-->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0.8"
+ android:controlY1="0"
+ android:controlX2="0.8"
+ android:controlY2="1"/>
diff --git a/res/interpolator/folder_opening_interpolator.xml b/res/interpolator/folder_opening_interpolator.xml
new file mode 100644
index 0000000..b95d454
--- /dev/null
+++ b/res/interpolator/folder_opening_interpolator.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2017, 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.
+*/
+-->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0.2"
+ android:controlY1="0"
+ android:controlX2="0"
+ android:controlY2="1"/>
diff --git a/res/interpolator/folder_preview_item_closing_interpolator.xml b/res/interpolator/folder_preview_item_closing_interpolator.xml
new file mode 100644
index 0000000..d8ddb0c
--- /dev/null
+++ b/res/interpolator/folder_preview_item_closing_interpolator.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2017, 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.
+*/
+-->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0.5"
+ android:controlY1="0"
+ android:controlX2="1"
+ android:controlY2="0"/>
diff --git a/res/interpolator/folder_preview_item_opening_interpolator.xml b/res/interpolator/folder_preview_item_opening_interpolator.xml
new file mode 100644
index 0000000..dcf0157
--- /dev/null
+++ b/res/interpolator/folder_preview_item_opening_interpolator.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2017, 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.
+*/
+-->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0"
+ android:controlY1="1"
+ android:controlX2="0"
+ android:controlY2="1"/>
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index ef0dfdc..6c1b1d3 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -35,6 +35,7 @@
<!-- The workspace contains 5 screens of cells -->
<!-- DO NOT CHANGE THE ID -->
<com.android.launcher3.Workspace
+ android:theme="@style/HomeScreenElementTheme"
android:id="@+id/workspace"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -59,6 +60,7 @@
<com.android.launcher3.pageindicators.PageIndicatorCaretLandscape
android:id="@+id/page_indicator"
+ android:theme="@style/HomeScreenElementTheme"
android:layout_width="@dimen/dynamic_grid_page_indicator_height"
android:layout_height="@dimen/dynamic_grid_page_indicator_height"
android:layout_gravity="bottom|left"/>
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index dd981dd..1fdf546 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -36,6 +36,7 @@
<!-- The workspace contains 5 screens of cells -->
<!-- DO NOT CHANGE THE ID -->
<com.android.launcher3.Workspace
+ android:theme="@style/HomeScreenElementTheme"
android:id="@+id/workspace"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 06cb550..9ef3394 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -35,6 +35,7 @@
<!-- The workspace contains 5 screens of cells -->
<!-- DO NOT CHANGE THE ID -->
<com.android.launcher3.Workspace
+ android:theme="@style/HomeScreenElementTheme"
android:layout_gravity="center"
android:id="@+id/workspace"
android:layout_width="match_parent"
diff --git a/res/layout/drop_target_bar_horz.xml b/res/layout/drop_target_bar_horz.xml
index ee22d1e..fcbb54e 100644
--- a/res/layout/drop_target_bar_horz.xml
+++ b/res/layout/drop_target_bar_horz.xml
@@ -17,6 +17,7 @@
<com.android.launcher3.DropTargetBar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ android:theme="@style/HomeScreenElementTheme"
android:layout_width="match_parent"
android:layout_height="@dimen/dynamic_grid_drop_target_size"
android:layout_gravity="center_horizontal|top"
diff --git a/res/layout/drop_target_bar_vert.xml b/res/layout/drop_target_bar_vert.xml
index 10b1d7c..54d9dfe 100644
--- a/res/layout/drop_target_bar_vert.xml
+++ b/res/layout/drop_target_bar_vert.xml
@@ -15,6 +15,7 @@
limitations under the License.
-->
<com.android.launcher3.DropTargetBar
+ android:theme="@style/HomeScreenElementTheme"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/dynamic_grid_drop_target_size"
android:orientation="vertical"
diff --git a/res/layout/hotseat.xml b/res/layout/hotseat.xml
index f5b5bbf..582a83f 100644
--- a/res/layout/hotseat.xml
+++ b/res/layout/hotseat.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<com.android.launcher3.Hotseat
+ android:theme="@style/HomeScreenElementTheme"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto">
<com.android.launcher3.CellLayout
diff --git a/res/layout/overview_panel.xml b/res/layout/overview_panel.xml
index 2091721..78a0f15 100644
--- a/res/layout/overview_panel.xml
+++ b/res/layout/overview_panel.xml
@@ -14,8 +14,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ android:theme="@style/HomeScreenElementTheme"
launcher:layout_ignoreInsets="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/res/layout/page_indicator.xml b/res/layout/page_indicator.xml
index 2e1b57f..e29e5b1 100644
--- a/res/layout/page_indicator.xml
+++ b/res/layout/page_indicator.xml
@@ -16,6 +16,7 @@
<com.android.launcher3.pageindicators.PageIndicatorLineCaret
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:theme="@style/HomeScreenElementTheme"
android:layout_width="match_parent"
android:layout_height="@dimen/dynamic_grid_page_indicator_height">
<ImageView
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 8d16653..0beee97 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -33,10 +33,8 @@
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dvaput dodirnite i zadržite da biste izabrali vidžet ili koristite prilagođene radnje."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"širina od %1$d i visina od %2$d"</string>
- <!-- no translation found for add_item_request_drag_hint (8662194377800507270) -->
- <skip />
- <!-- no translation found for place_automatically (1502491650329146581) -->
- <skip />
+ <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Dodirnite i zadržite da biste postavili na početni ekran"</string>
+ <string name="place_automatically" msgid="1502491650329146581">"Postavi automatski"</string>
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pretražite aplikacije"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Aplikacije se učitavaju..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nije pronađena nijedna aplikacija za „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
@@ -77,6 +75,8 @@
<string name="allow_rotation_title" msgid="7728578836261442095">"Dozvoli rotaciju početnog ekrana"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Kada se telefon rotira"</string>
<string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Aktuelno podešavanje prikaza ne dozvoljava rotaciju"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Dodaj ikonu na početni ekran"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Za nove aplikacije"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
<string name="abandoned_search" msgid="891119232568284442">"Pretraži"</string>
diff --git a/res/values-be-rBY/strings.xml b/res/values-be-rBY/strings.xml
index f1b3aee..db06470 100644
--- a/res/values-be-rBY/strings.xml
+++ b/res/values-be-rBY/strings.xml
@@ -33,10 +33,8 @@
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"Дакраніцеся двойчы і ўтрымлівайце, каб выбраць віджэт або выкарыстоўваць карыстальніцкія дзеянні."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Шырына: %1$d, вышыня: %2$d"</string>
- <!-- no translation found for add_item_request_drag_hint (8662194377800507270) -->
- <skip />
- <!-- no translation found for place_automatically (1502491650329146581) -->
- <skip />
+ <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Дакраніцеся і ўтрымлівайце, каб размясціць на галоўным экране"</string>
+ <string name="place_automatically" msgid="1502491650329146581">"Размясціць аўтаматычна"</string>
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Пошук у Праграмах"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Ідзе загрузка праграм…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Праграм, якія адпавядаюць запыту \"<xliff:g id="QUERY">%1$s</xliff:g>\", не знойдзена"</string>
@@ -77,6 +75,8 @@
<string name="allow_rotation_title" msgid="7728578836261442095">"Дазволіць паварот галоўнага экрана"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Пры павароце тэлефона"</string>
<string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Бягучая налада дысплэя не прадугледжвае паварот"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Дадаць значок на Галоўны экран"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Для новых праграм"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Невядома"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Выдаліць"</string>
<string name="abandoned_search" msgid="891119232568284442">"Шукаць"</string>
diff --git a/res/values-bs-rBA/strings.xml b/res/values-bs-rBA/strings.xml
index 4a658d2..db207dc 100644
--- a/res/values-bs-rBA/strings.xml
+++ b/res/values-bs-rBA/strings.xml
@@ -33,10 +33,8 @@
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dvaput dodirnite & i držite da biste uzeli vidžet ili koristite prilagođene radnje."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Širina %1$d, visina %2$d"</string>
- <!-- no translation found for add_item_request_drag_hint (8662194377800507270) -->
- <skip />
- <!-- no translation found for place_automatically (1502491650329146581) -->
- <skip />
+ <string name="add_item_request_drag_hint" msgid="8662194377800507270">"Dodirnite i držite da postavite na početni ekran"</string>
+ <string name="place_automatically" msgid="1502491650329146581">"Postavi automatski"</string>
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pretraži aplikacije"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Aplikacije se učitavaju…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nije pronađena nijedna aplikacija koja odgovara upitu \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -77,6 +75,8 @@
<string name="allow_rotation_title" msgid="7728578836261442095">"Dozvoli rotiranje početnog ekrana"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Kada se telefon zarotira"</string>
<string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Trenutne postavke ekrana ne dozvoljavaju rotiranje"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Dodajte ikonu na početni ekran"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Za nove aplikacije"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
<string name="abandoned_search" msgid="891119232568284442">"Traži"</string>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 83a44d7..455729c 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -25,13 +25,8 @@
<color name="focused_background">#80c6c5c5</color>
- <color name="workspace_icon_text_color">#FFF</color>
- <color name="workspace_edge_effect_color">#FFFFFFFF</color>
-
<color name="default_shadow_color_no_alpha">#FF000000</color>
- <color name="outline_color">#FFFFFFFF</color>
-
<color name="spring_loaded_panel_color">#40FFFFFF</color>
<color name="spring_loaded_highlighted_panel_border_color">#FFF</color>
@@ -41,4 +36,5 @@
<color name="notification_color_beneath">#E0E0E0</color> <!-- Gray 300 -->
<color name="divider_color">@color/notification_color_beneath</color>
<color name="icon_background">#E0E0E0</color> <!-- Gray 300 -->
+ <color name="legacy_icon_background">#FFFFFF</color>
</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 745bce3..4c19dec 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -58,6 +58,7 @@
<integer name="config_folderExpandDuration">120</integer>
<integer name="config_materialFolderExpandDuration">200</integer>
<integer name="config_materialFolderExpandStagger">60</integer>
+ <integer name="config_folderDelay">30</integer>
<!-- The distance at which the animation should take the max duration -->
<integer name="config_dropAnimMaxDist">800</integer>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 8a46e83..26ddb9c 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -29,6 +29,17 @@
<style name="LauncherTheme" parent="@style/BaseLauncherTheme"></style>
+ <!--
+ Theme overrides to element on homescreen, i.e., which are drawn on top on wallpaper.
+ Various foreground colors are overridden to be white so that they are properly visible on
+ various wallpapers
+ -->
+ <style name="HomeScreenElementTheme" parent="@style/LauncherTheme">
+ <item name="android:colorEdgeEffect">@android:color/white</item>
+ <item name="android:textColorPrimary">@android:color/white</item>
+ <item name="android:textColorSecondary">@android:color/white</item>
+ </style>
+
<!-- Theme for the widget container. Overridden on API 26. -->
<style name="WidgetContainerTheme" parent="@android:style/Theme.DeviceDefault.Settings">
<item name="android:colorEdgeEffect">?android:attr/textColorSecondaryInverse</item>
@@ -76,7 +87,6 @@
<!-- Icon displayed on the worksapce -->
<style name="BaseIcon.Workspace">
<item name="customShadows">true</item>
- <item name="android:textColor">@color/workspace_icon_text_color</item>
<item name="android:shadowRadius">2.0</item>
<item name="android:shadowColor">#B0000000</item>
</style>
@@ -86,7 +96,7 @@
<item name="android:drawablePadding">7.5dp</item>
<item name="android:paddingLeft">16dp</item>
<item name="android:paddingRight">16dp</item>
- <item name="android:textColor">@color/workspace_icon_text_color</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textSize">@dimen/drop_target_text_size</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">end</item>
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java
index 5b42cad..d7f0180 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/AllAppsList.java
@@ -18,10 +18,15 @@
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
+import android.os.Process;
import android.os.UserHandle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -69,7 +74,7 @@
if (!mAppFilter.shouldShowApp(info.componentName)) {
return;
}
- if (findActivity(data, info.componentName, info.user)) {
+ if (findAppInfo(info.componentName, info.user) != null) {
return;
}
mIconCache.getTitleAndIcon(info, activityInfo, true /* useLowResIcon */);
@@ -78,6 +83,25 @@
added.add(info);
}
+ public void addPromiseApp(Context context,
+ PackageInstallerCompat.PackageInstallInfo installInfo) {
+ ApplicationInfo applicationInfo = LauncherAppsCompat.getInstance(context)
+ .getApplicationInfo(installInfo.packageName, 0, Process.myUserHandle());
+ // only if not yet installed
+ if (applicationInfo == null) {
+ PromiseAppInfo info = new PromiseAppInfo(installInfo);
+ mIconCache.getTitleAndIcon(info, info.usingLowResIcon);
+ data.add(info);
+ added.add(info);
+ }
+ }
+
+ public void removePromiseApp(AppInfo appInfo) {
+ // the <em>removed</em> list is handled by the caller
+ // so not adding it here
+ data.remove(appInfo);
+ }
+
public void clear() {
data.clear();
// TODO: do we clear these too?
@@ -169,9 +193,7 @@
// Find enabled activities and add them to the adapter
// Also updates existing activities with new labels/icons
for (final LauncherActivityInfo info : matches) {
- AppInfo applicationInfo = findApplicationInfoLocked(
- info.getComponentName().getPackageName(), user,
- info.getComponentName().getClassName());
+ AppInfo applicationInfo = findAppInfo(info.getComponentName(), user);
if (applicationInfo == null) {
add(new AppInfo(context, info, user), info);
} else {
@@ -208,28 +230,14 @@
}
/**
- * Returns whether <em>apps</em> contains <em>component</em>.
+ * Find an AppInfo object for the given componentName
+ *
+ * @return the corresponding AppInfo or null
*/
- private static boolean findActivity(ArrayList<AppInfo> apps, ComponentName component,
- UserHandle user) {
- final int N = apps.size();
- for (int i = 0; i < N; i++) {
- final AppInfo info = apps.get(i);
- if (info.user.equals(user) && info.componentName.equals(component)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Find an ApplicationInfo object for the given packageName and className.
- */
- private AppInfo findApplicationInfoLocked(String packageName, UserHandle user,
- String className) {
+ private @Nullable AppInfo findAppInfo(@NonNull ComponentName componentName,
+ @NonNull UserHandle user) {
for (AppInfo info: data) {
- if (user.equals(info.user) && packageName.equals(info.componentName.getPackageName())
- && className.equals(info.componentName.getClassName())) {
+ if (componentName.equals(info.componentName) && user.equals(info.user)) {
return info;
}
}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index bad7018..add0185 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -39,7 +39,7 @@
import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.badge.BadgeInfo;
import com.android.launcher3.badge.BadgeRenderer;
-import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.FolderIconPreviewVerifier;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.graphics.HolographicOutlineHelper;
import com.android.launcher3.graphics.PreloadIconDrawable;
@@ -180,6 +180,10 @@
// Verify high res immediately
verifyHighRes();
+ if (info instanceof PromiseAppInfo) {
+ PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
+ applyProgressLevel(promiseAppInfo.level);
+ }
applyBadgeState(info, false /* animate */);
}
@@ -475,27 +479,36 @@
((info.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
info.getInstallProgress() : 0)) : 100;
- setContentDescription(progressLevel > 0 ?
- getContext().getString(R.string.app_downloading_title, info.title,
- NumberFormat.getPercentInstance().format(progressLevel * 0.01)) :
- getContext().getString(R.string.app_waiting_download_title, info.title));
+ PreloadIconDrawable preloadDrawable = applyProgressLevel(progressLevel);
+ if (preloadDrawable != null && promiseStateChanged) {
+ preloadDrawable.maybePerformFinishedAnimation();
+ }
+ }
+ }
+
+ public PreloadIconDrawable applyProgressLevel(int progressLevel) {
+ if (getTag() instanceof ItemInfoWithIcon) {
+ ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
+ setContentDescription(progressLevel > 0
+ ? getContext().getString(R.string.app_downloading_title, info.title,
+ NumberFormat.getPercentInstance().format(progressLevel * 0.01))
+ : getContext().getString(R.string.app_waiting_download_title, info.title));
if (mIcon != null) {
final PreloadIconDrawable preloadDrawable;
if (mIcon instanceof PreloadIconDrawable) {
preloadDrawable = (PreloadIconDrawable) mIcon;
+ preloadDrawable.setLevel(progressLevel);
} else {
preloadDrawable = DrawableFactory.get(getContext())
.newPendingIcon(info.iconBitmap, getContext());
+ preloadDrawable.setLevel(progressLevel);
setIcon(preloadDrawable);
}
-
- preloadDrawable.setLevel(progressLevel);
- if (promiseStateChanged) {
- preloadDrawable.maybePerformFinishedAnimation();
- }
+ return preloadDrawable;
}
}
+ return null;
}
public void applyBadgeState(ItemInfo itemInfo, boolean animate) {
@@ -547,7 +560,9 @@
applyFromApplicationInfo((AppInfo) info);
} else if (info instanceof ShortcutInfo) {
applyFromShortcutInfo((ShortcutInfo) info);
- if ((info.rank < FolderIcon.NUM_ITEMS_IN_PREVIEW) && (info.container >= 0)) {
+ FolderIconPreviewVerifier verifier =
+ new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
+ if (verifier.isItemInPreview(info.rank) && (info.container >= 0)) {
View folderIcon =
mLauncher.getWorkspace().getHomescreenIconByItemId(info.container);
if (folderIcon != null) {
@@ -587,6 +602,10 @@
.isEmpty();
}
+ public int getIconSize() {
+ return mIconSize;
+ }
+
/**
* Interface to be implemented by the grand parent to allow click shadow effect.
*/
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 8179dad..c0946a0 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -51,12 +51,13 @@
import com.android.launcher3.accessibility.FolderAccessibilityHelper;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.anim.PropertyListBuilder;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.util.CellAndSpan;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.ParcelableSparseArray;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import java.lang.annotation.Retention;
@@ -235,7 +236,7 @@
for (int i = 0; i < mDragOutlines.length; i++) {
mDragOutlines[i] = new Rect(-1, -1, -1, -1);
}
- mDragOutlinePaint.setColor(getResources().getColor(R.color.outline_color));
+ mDragOutlinePaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
// When dragging things around the home screens, we show a green outline of
// where the item will land. The outlines gradually fade out, leaving a trail
@@ -568,7 +569,7 @@
try {
dispatchRestoreInstanceState(states);
} catch (IllegalArgumentException ex) {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw ex;
}
// Mismatched viewId / viewType preventing restore. Skip restore on production builds.
diff --git a/src/com/android/launcher3/DeferredHandler.java b/src/com/android/launcher3/DeferredHandler.java
deleted file mode 100644
index a43ab67..0000000
--- a/src/com/android/launcher3/DeferredHandler.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.MessageQueue;
-
-import com.android.launcher3.util.Thunk;
-
-import java.util.LinkedList;
-
-/**
- * Queue of things to run on a looper thread. Items posted with {@link #post} will not
- * be actually enqued on the handler until after the last one has run, to keep from
- * starving the thread.
- *
- * This class is fifo.
- */
-public class DeferredHandler {
- @Thunk LinkedList<Runnable> mQueue = new LinkedList<>();
- private MessageQueue mMessageQueue = Looper.myQueue();
- private Impl mHandler = new Impl();
-
- @Thunk class Impl extends Handler implements MessageQueue.IdleHandler {
- public void handleMessage(Message msg) {
- Runnable r;
- synchronized (mQueue) {
- if (mQueue.size() == 0) {
- return;
- }
- r = mQueue.removeFirst();
- }
- r.run();
- synchronized (mQueue) {
- scheduleNextLocked();
- }
- }
-
- public boolean queueIdle() {
- handleMessage(null);
- return false;
- }
- }
-
- private class IdleRunnable implements Runnable {
- Runnable mRunnable;
-
- IdleRunnable(Runnable r) {
- mRunnable = r;
- }
-
- public void run() {
- mRunnable.run();
- }
- }
-
- public DeferredHandler() {
- }
-
- /** Schedule runnable to run after everything that's on the queue right now. */
- public void post(Runnable runnable) {
- synchronized (mQueue) {
- mQueue.add(runnable);
- if (mQueue.size() == 1) {
- scheduleNextLocked();
- }
- }
- }
-
- /** Schedule runnable to run when the queue goes idle. */
- public void postIdle(final Runnable runnable) {
- post(new IdleRunnable(runnable));
- }
-
- public void cancelAll() {
- synchronized (mQueue) {
- mQueue.clear();
- }
- }
-
- /** Runs all queued Runnables from the calling thread. */
- public void flush() {
- LinkedList<Runnable> queue = new LinkedList<>();
- synchronized (mQueue) {
- queue.addAll(mQueue);
- mQueue.clear();
- }
- for (Runnable r : queue) {
- r.run();
- }
- }
-
- void scheduleNextLocked() {
- if (mQueue.size() > 0) {
- Runnable peek = mQueue.getFirst();
- if (peek instanceof IdleRunnable) {
- mMessageQueue.addIdleHandler(mHandler);
- } else {
- mHandler.sendEmptyMessage(1);
- }
- }
- }
-}
-
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index b36734b..fe7acda 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -22,7 +22,7 @@
import android.view.View;
import android.view.ViewGroup;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderPagedView;
import com.android.launcher3.util.FocusLogic;
@@ -93,7 +93,7 @@
}
if (!(v.getParent() instanceof ShortcutAndWidgetContainer)) {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw new IllegalStateException("Parent of the focused item is not supported.");
} else {
return false;
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 1f473a2..a5552aa 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -437,9 +437,10 @@
* Updates {@param application} only if a valid entry is found.
*/
public synchronized void updateTitleAndIcon(AppInfo application) {
+ boolean usePackageIcon = application instanceof PromiseAppInfo;
CacheEntry entry = cacheLocked(application.componentName,
Provider.<LauncherActivityInfo>of(null),
- application.user, false, application.usingLowResIcon);
+ application.user, usePackageIcon, application.usingLowResIcon);
if (entry.icon != null && !isDefaultIcon(entry.icon, application.user)) {
applyCacheEntry(entry, application);
}
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index 34adf47..2f61a01 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -60,6 +60,12 @@
*/
public static boolean startDetailsActivityForInfo(
ItemInfo info, Launcher launcher, DropTargetResultCallback callback) {
+ if (info instanceof PromiseAppInfo) {
+ PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
+ launcher.startActivity(promiseAppInfo.getMarketIntent());
+ return true;
+ }
+
boolean result = false;
ComponentName componentName = null;
if (info instanceof AppInfo) {
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 9e214d1..d224615 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -28,7 +28,6 @@
import android.view.WindowManager;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.util.Thunk;
import org.xmlpull.v1.XmlPullParser;
@@ -317,7 +316,7 @@
}
public int getAllAppsButtonRank() {
- if (ProviderConfig.IS_DOGFOOD_BUILD && FeatureFlags.NO_ALL_APPS_ICON) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && FeatureFlags.NO_ALL_APPS_ICON) {
throw new IllegalAccessError("Accessing all apps rank when all-apps is disabled");
}
return numHotseatIcons / 2;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 2dba83d..5682c1a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -65,6 +65,7 @@
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
import android.view.KeyboardShortcutInfo;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
@@ -91,7 +92,6 @@
import com.android.launcher3.compat.PinItemRequestCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
@@ -1425,8 +1425,8 @@
* @return A View inflated from layoutResId.
*/
public View createShortcut(ViewGroup parent, ShortcutInfo info) {
- BubbleTextView favorite = (BubbleTextView) getLayoutInflater().inflate(R.layout.app_icon,
- parent, false);
+ BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.app_icon, parent, false);
favorite.applyFromShortcutInfo(info);
favorite.setCompoundDrawablePadding(mDeviceProfile.iconDrawablePaddingPx);
favorite.setOnClickListener(this);
@@ -2464,7 +2464,13 @@
private void startAppShortcutOrInfoActivity(View v) {
ItemInfo item = (ItemInfo) v.getTag();
- Intent intent = item.getIntent();
+ Intent intent;
+ if (item instanceof PromiseAppInfo) {
+ PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
+ intent = promiseAppInfo.getMarketIntent();
+ } else {
+ intent = item.getIntent();
+ }
if (intent == null) {
throw new IllegalArgumentException("Input must have a valid intent");
}
@@ -3391,7 +3397,7 @@
Object tag = v.getTag();
String desc = "Collision while binding workspace item: " + item
+ ". Collides with " + tag;
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw (new RuntimeException(desc));
} else {
Log.d(TAG, desc);
@@ -3769,6 +3775,22 @@
}
@Override
+ public void bindPromiseAppProgressUpdated(final PromiseAppInfo app) {
+ Runnable r = new Runnable() {
+ public void run() {
+ bindPromiseAppProgressUpdated(app);
+ }
+ };
+ if (waitUntilResume(r)) {
+ return;
+ }
+
+ if (mAppsView != null) {
+ mAppsView.updatePromiseAppProgress(app);
+ }
+ }
+
+ @Override
public void bindWidgetsRestored(final ArrayList<LauncherAppWidgetInfo> widgets) {
Runnable r = new Runnable() {
public void run() {
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 180c202..27ccabe 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -26,7 +26,7 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dynamicui.ExtractionUtils;
import com.android.launcher3.util.ConfigMonitor;
import com.android.launcher3.util.Preconditions;
@@ -37,7 +37,7 @@
public class LauncherAppState {
- public static final boolean PROFILE_STARTUP = ProviderConfig.IS_DOGFOOD_BUILD;
+ public static final boolean PROFILE_STARTUP = FeatureFlags.IS_DOGFOOD_BUILD;
// We do not need any synchronization for this variable as its only written on UI thread.
private static LauncherAppState INSTANCE;
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 5a41eeb..58fb117 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -25,7 +25,9 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
+import android.content.pm.PackageInstaller;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
@@ -44,10 +46,11 @@
import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dynamicui.ExtractionUtils;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.FolderIconPreviewVerifier;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.AddWorkspaceItemsTask;
@@ -71,6 +74,7 @@
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.LooperIdleLock;
import com.android.launcher3.util.ManagedProfileHeuristic;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
@@ -109,9 +113,9 @@
private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
private static final long INVALID_SCREEN_ID = -1L;
+ private final MainThreadExecutor mUiExecutor = new MainThreadExecutor();
@Thunk final LauncherAppState mApp;
@Thunk final Object mLock = new Object();
- @Thunk DeferredHandler mHandler = new DeferredHandler();
@Thunk LoaderTask mLoaderTask;
@Thunk boolean mIsLoaderTaskRunning;
@Thunk boolean mHasLoaderCompletedOnce;
@@ -127,6 +131,11 @@
// our monitoring of the package manager provides all updates and we never
// need to do a requery. This is only ever touched from the loader thread.
private boolean mModelLoaded;
+ public boolean isModelLoaded() {
+ synchronized (mLock) {
+ return mModelLoaded && mLoaderTask == null;
+ }
+ }
/**
* Set of runnables to be called on the background thread after the workspace binding
@@ -186,6 +195,7 @@
ArrayList<ItemInfo> addAnimated,
ArrayList<AppInfo> addedApps);
public void bindAppsUpdated(ArrayList<AppInfo> apps);
+ public void bindPromiseAppProgressUpdated(PromiseAppInfo app);
public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated,
ArrayList<ShortcutInfo> removed, UserHandle user);
public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
@@ -212,17 +222,6 @@
mUserManager = UserManagerCompat.getInstance(context);
}
- /** Runs the specified runnable immediately if called from the main thread, otherwise it is
- * posted on the main thread handler. */
- private void runOnMainThread(Runnable r) {
- if (sWorkerThread.getThreadId() == Process.myTid()) {
- // If we are on the worker thread, post onto the main handler
- mHandler.post(r);
- } else {
- r.run();
- }
- }
-
/** Runs the specified runnable immediately if called from the worker thread, otherwise it is
* posted on the worker thread handler. */
private static void runOnWorkerThread(Runnable r) {
@@ -372,8 +371,6 @@
public void initialize(Callbacks callbacks) {
synchronized (mLock) {
Preconditions.assertUIThread();
- // Remove any queued UI runnables
- mHandler.cancelAll();
mCallbacks = new WeakReference<>(callbacks);
}
}
@@ -537,11 +534,11 @@
if (mCallbacks != null && mCallbacks.get() != null) {
final Callbacks oldCallbacks = mCallbacks.get();
// Clear any pending bind-runnables from the synchronized load process.
- runOnMainThread(new Runnable() {
- public void run() {
- oldCallbacks.clearPendingBinds();
- }
- });
+ mUiExecutor.execute(new Runnable() {
+ public void run() {
+ oldCallbacks.clearPendingBinds();
+ }
+ });
// If there is already one running, tell it to stop.
stopLoaderLocked();
@@ -579,6 +576,25 @@
screensUri, null, null, null, LauncherSettings.WorkspaceScreens.SCREEN_RANK));
}
+ public void onInstallSessionCreated(final PackageInstallInfo sessionInfo) {
+ enqueueModelUpdateTask(new ExtendedModelTask() {
+ @Override
+ public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ apps.addPromiseApp(app.getContext(), sessionInfo);
+ if (!apps.added.isEmpty()) {
+ final ArrayList<AppInfo> arrayList = new ArrayList<>(apps.added);
+ apps.added.clear();
+ scheduleCallbackTask(new CallbackTask() {
+ @Override
+ public void execute(Callbacks callbacks) {
+ callbacks.bindAppsAdded(null, null, null, arrayList);
+ }
+ });
+ }
+ }
+ });
+ }
+
/**
* Runnable for the thread that loads the contents of the launcher:
* - workspace icons
@@ -592,7 +608,6 @@
@Thunk boolean mIsLoadingAndBindingWorkspace;
private boolean mStopped;
- @Thunk boolean mLoadAndBindStepFinished;
LoaderTask(Context context, int pageToBindFirst) {
mContext = context;
@@ -604,34 +619,10 @@
// This way we don't start loading all apps until the workspace has settled
// down.
synchronized (LoaderTask.this) {
- final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-
- mHandler.postIdle(new Runnable() {
- public void run() {
- synchronized (LoaderTask.this) {
- mLoadAndBindStepFinished = true;
- if (DEBUG_LOADERS) {
- Log.d(TAG, "done with previous binding step");
- }
- LoaderTask.this.notify();
- }
- }
- });
-
- while (!mStopped && !mLoadAndBindStepFinished) {
- try {
- // Just in case mFlushingWorkerThread changes but we aren't woken up,
- // wait no longer than 1sec at a time
- this.wait(1000);
- } catch (InterruptedException ex) {
- // Ignore
- }
- }
- if (DEBUG_LOADERS) {
- Log.d(TAG, "waited "
- + (SystemClock.uptimeMillis()-workspaceWaitTime)
- + "ms for previous step to finish binding");
- }
+ LooperIdleLock idleLock = new LooperIdleLock(this, Looper.getMainLooper());
+ // Just in case mFlushingWorkerThread changes but we aren't woken up,
+ // wait no longer than 1sec at a time
+ while (!mStopped && idleLock.awaitLocked(1000));
}
}
@@ -654,15 +645,6 @@
}
}
- // XXX: Throw an exception if we are already loading (since we touch the worker thread
- // data structures, we can't allow any other thread to touch that data, but because
- // this call is synchronous, we can get away with not locking).
-
- // The LauncherModel is static in the LauncherAppState and mHandler may have queued
- // operations from the previous activity. We need to ensure that all queued operations
- // are executed before any synchronous binding work is done.
- mHandler.flush();
-
// Divide the set of loaded items into those that we are binding synchronously, and
// everything else that is to be bound normally (asynchronously).
bindWorkspace(synchronousBindPage);
@@ -690,6 +672,7 @@
}
try {
+ long now = 0;
if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
// Set to false in bindWorkspace()
mIsLoadingAndBindingWorkspace = true;
@@ -700,8 +683,12 @@
bindWorkspace(mPageToBindFirst);
// Take a break
- if (DEBUG_LOADERS) Log.d(TAG, "step 1 completed, wait for idle");
+ if (DEBUG_LOADERS) {
+ Log.d(TAG, "step 1 completed, wait for idle");
+ now = SystemClock.uptimeMillis();
+ }
waitForIdle();
+ if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms");
verifyNotStopped();
// second step
@@ -713,8 +700,12 @@
updateIconCache();
// Take a break
- if (DEBUG_LOADERS) Log.d(TAG, "step 2 completed, wait for idle");
+ if (DEBUG_LOADERS) {
+ Log.d(TAG, "step 2 completed, wait for idle");
+ now = SystemClock.uptimeMillis();
+ }
waitForIdle();
+ if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms");
verifyNotStopped();
// third step
@@ -883,6 +874,8 @@
Intent intent;
String targetPkg;
+ FolderIconPreviewVerifier verifier =
+ new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
while (!mStopped && c.moveToNext()) {
try {
if (c.user == null) {
@@ -1007,7 +1000,7 @@
}
boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() &&
- c.getInt(rankIndex) >= FolderIcon.NUM_ITEMS_IN_PREVIEW;
+ !verifier.isItemInPreview(c.getInt(rankIndex));
if (c.restoreFlag != 0) {
// Already verified above that user is same as default user
@@ -1256,17 +1249,23 @@
}
}
- // Sort all the folder items and make sure the first 3 items are high resolution.
+ FolderIconPreviewVerifier verifier =
+ new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
+ // Sort the folder items and make sure all items in the preview are high resolution.
for (FolderInfo folder : sBgDataModel.folders) {
Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
- int pos = 0;
+ verifier.setFolderInfo(folder);
+
+ int numItemsInPreview = 0;
for (ShortcutInfo info : folder.contents) {
- if (info.usingLowResIcon &&
- info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+ if (info.usingLowResIcon
+ && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+ && verifier.isItemInPreview(info.rank)) {
mIconCache.getTitleAndIcon(info, false);
+ numItemsInPreview++;
}
- pos ++;
- if (pos >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+
+ if (numItemsInPreview >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
break;
}
}
@@ -1391,7 +1390,7 @@
return Utilities.longCompare(lhs.screenId, rhs.screenId);
}
default:
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw new RuntimeException("Unexpected container type when " +
"sorting workspace items.");
}
@@ -1416,7 +1415,7 @@
}
}
};
- runOnMainThread(r);
+ mUiExecutor.execute(r);
}
private void bindWorkspaceItems(final Callbacks oldCallbacks,
@@ -1522,11 +1521,11 @@
}
}
};
- runOnMainThread(r);
+ mUiExecutor.execute(r);
bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
- Executor mainExecutor = new DeferredMainThreadExecutor();
+ Executor mainExecutor = mUiExecutor;
// Load items on the current page.
bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, mainExecutor);
@@ -1536,7 +1535,7 @@
// This ensures that the first screen is immediately visible (eg. during rotation)
// In case of !validFirstPage, bind all pages one after other.
final Executor deferredExecutor =
- validFirstPage ? new ViewOnDrawExecutor(mHandler) : mainExecutor;
+ validFirstPage ? new ViewOnDrawExecutor(mUiExecutor) : mainExecutor;
mainExecutor.execute(new Runnable() {
@Override
@@ -1596,7 +1595,7 @@
}
}
};
- runOnMainThread(r);
+ mUiExecutor.execute(r);
}
}
@@ -1646,7 +1645,7 @@
}
}
};
- runOnMainThread(r);
+ mUiExecutor.execute(r);
}
private void loadAllApps() {
@@ -1694,29 +1693,40 @@
heuristic.processUserApps(apps);
}
};
- runOnMainThread(new Runnable() {
+ mUiExecutor.execute(new Runnable() {
- @Override
- public void run() {
- // Check isLoadingWorkspace on the UI thread, as it is updated on
- // the UI thread.
- if (mIsLoadingAndBindingWorkspace) {
- synchronized (mBindCompleteRunnables) {
- mBindCompleteRunnables.add(r);
- }
- } else {
- runOnWorkerThread(r);
- }
- }
- });
+ @Override
+ public void run() {
+ // Check isLoadingWorkspace on the UI thread, as it is updated on
+ // the UI thread.
+ if (mIsLoadingAndBindingWorkspace) {
+ synchronized (mBindCompleteRunnables) {
+ mBindCompleteRunnables.add(r);
+ }
+ } else {
+ runOnWorkerThread(r);
+ }
+ }
+ });
}
}
+
+ if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
+ // get all active sessions and add them to the all apps list
+ PackageInstallerCompat installer = PackageInstallerCompat.getInstance(mContext);
+ for (PackageInstaller.SessionInfo info : installer.getAllVerifiedSessions()) {
+ mBgAllAppsList.addPromiseApp(mContext,
+ PackageInstallInfo.fromInstallingState(info));
+ }
+ }
+
// Huh? Shouldn't this be inside the Runnable below?
final ArrayList<AppInfo> added = mBgAllAppsList.added;
mBgAllAppsList.added = new ArrayList<AppInfo>();
+
// Post callback on main thread
- mHandler.post(new Runnable() {
+ mUiExecutor.execute(new Runnable() {
public void run() {
final long bindTime = SystemClock.uptimeMillis();
@@ -1770,7 +1780,7 @@
}
}
};
- runOnMainThread(r);
+ mUiExecutor.execute(r);
}
/**
@@ -1821,12 +1831,12 @@
public static abstract class BaseModelUpdateTask implements Runnable {
private LauncherModel mModel;
- private DeferredHandler mUiHandler;
+ private Executor mUiExecutor;
/* package private */
void init(LauncherModel model) {
mModel = model;
- mUiHandler = mModel.mHandler;
+ mUiExecutor = mModel.mUiExecutor;
}
@Override
@@ -1849,7 +1859,7 @@
*/
public final void scheduleCallbackTask(final CallbackTask task) {
final Callbacks callbacks = mModel.getCallback();
- mUiHandler.post(new Runnable() {
+ mUiExecutor.execute(new Runnable() {
public void run() {
Callbacks cb = mModel.getCallback();
if (callbacks == cb && cb != null) {
@@ -1894,7 +1904,7 @@
private void bindWidgetsModel(final Callbacks callbacks) {
final MultiHashMap<PackageItemInfo, WidgetItem> widgets
= mBgWidgetsModel.getWidgetsMap().clone();
- mHandler.post(new Runnable() {
+ mUiExecutor.execute(new Runnable() {
@Override
public void run() {
Callbacks cb = getCallback();
@@ -1951,14 +1961,6 @@
}
}
- @Thunk class DeferredMainThreadExecutor implements Executor {
-
- @Override
- public void execute(Runnable command) {
- runOnMainThread(command);
- }
- }
-
/**
* @return the looper for the worker thread which can be used to start background tasks.
*/
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index a750406..33a3fd7 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -53,7 +53,6 @@
import com.android.launcher3.LauncherSettings.WorkspaceScreens;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dynamicui.ExtractionUtils;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.provider.LauncherDbUtils;
@@ -63,6 +62,8 @@
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Thunk;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
@@ -74,7 +75,7 @@
private static final int DATABASE_VERSION = 28;
- public static final String AUTHORITY = ProviderConfig.AUTHORITY;
+ public static final String AUTHORITY = (BuildConfig.APPLICATION_ID + ".settings").intern();
static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
@@ -85,9 +86,21 @@
protected DatabaseHelper mOpenHelper;
+ /**
+ * $ adb shell dumpsys activity provider com.android.launcher3
+ */
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
+ if (appState == null || !appState.getModel().isModelLoaded()) {
+ return;
+ }
+ appState.getModel().dumpState("", fd, writer, args);
+ }
+
@Override
public boolean onCreate() {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
Log.d(TAG, "Launcher process started");
}
mListenerHandler = new Handler(mListenerWrapper);
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index af2c102..e8e0eb2 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -22,8 +22,6 @@
import android.os.Bundle;
import android.provider.BaseColumns;
-import com.android.launcher3.config.ProviderConfig;
-
/**
* Settings related utilities.
*/
@@ -101,7 +99,7 @@
* The content:// style URL for this table
*/
public static final Uri CONTENT_URI = Uri.parse("content://" +
- ProviderConfig.AUTHORITY + "/" + TABLE_NAME);
+ LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
/**
* The rank of this screen -- ie. how it is ordered relative to the other screens.
@@ -121,7 +119,7 @@
* The content:// style URL for this table
*/
public static final Uri CONTENT_URI = Uri.parse("content://" +
- ProviderConfig.AUTHORITY + "/" + TABLE_NAME);
+ LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
/**
* The content:// style URL for a given row, identified by its id.
@@ -131,7 +129,7 @@
* @return The unique content URL for the specified row.
*/
public static Uri getContentUri(long id) {
- return Uri.parse("content://" + ProviderConfig.AUTHORITY +
+ return Uri.parse("content://" + LauncherProvider.AUTHORITY +
"/" + TABLE_NAME + "/" + id);
}
@@ -280,7 +278,7 @@
public static final class Settings {
public static final Uri CONTENT_URI = Uri.parse("content://" +
- ProviderConfig.AUTHORITY + "/settings");
+ LauncherProvider.AUTHORITY + "/settings");
public static final String METHOD_CLEAR_EMPTY_DB_FLAG = "clear_empty_db_flag";
public static final String METHOD_WAS_EMPTY_DB_CREATED = "get_empty_db_flag";
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 39c466d..f5af979 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -32,7 +32,7 @@
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimationLayerSet;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.CircleRevealOutlineProvider;
+import com.android.launcher3.anim.CircleRevealOutlineProvider;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.WidgetsContainerView;
diff --git a/src/com/android/launcher3/MainThreadExecutor.java b/src/com/android/launcher3/MainThreadExecutor.java
index 4ca0a59..5094682 100644
--- a/src/com/android/launcher3/MainThreadExecutor.java
+++ b/src/com/android/launcher3/MainThreadExecutor.java
@@ -18,14 +18,14 @@
import android.os.Looper;
-import com.android.launcher3.util.LooperExecuter;
+import com.android.launcher3.util.LooperExecutor;
/**
* An executor service that executes its tasks on the main thread.
*
* Shutting down this executor is not supported.
*/
-public class MainThreadExecutor extends LooperExecuter {
+public class MainThreadExecutor extends LooperExecutor {
public MainThreadExecutor() {
super(Looper.getMainLooper());
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index fb6a611..31e3dda 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -50,6 +50,7 @@
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.util.LauncherEdgeEffect;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -226,11 +227,10 @@
mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density);
setOnHierarchyChangeListener(this);
setWillNotDraw(false);
- }
- protected void setEdgeGlowColor(int color) {
- mEdgeGlowLeft.setColor(color);
- mEdgeGlowRight.setColor(color);
+ int edgeEffectColor = Themes.getAttrColor(getContext(), android.R.attr.colorEdgeEffect);
+ mEdgeGlowLeft.setColor(edgeEffectColor);
+ mEdgeGlowRight.setColor(edgeEffectColor);
}
protected void setDefaultInterpolator(Interpolator interpolator) {
diff --git a/src/com/android/launcher3/PromiseAppInfo.java b/src/com/android/launcher3/PromiseAppInfo.java
new file mode 100644
index 0000000..1749dde
--- /dev/null
+++ b/src/com/android/launcher3/PromiseAppInfo.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 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.Intent;
+import android.support.annotation.NonNull;
+
+import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.util.PackageManagerHelper;
+
+public class PromiseAppInfo extends AppInfo {
+
+ public int level = 0;
+
+ public PromiseAppInfo(@NonNull PackageInstallerCompat.PackageInstallInfo installInfo) {
+ componentName = installInfo.componentName;
+ intent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_LAUNCHER)
+ .setComponent(componentName)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ }
+
+ @Override
+ public ShortcutInfo makeShortcut() {
+ ShortcutInfo shortcut = new ShortcutInfo(this);
+ shortcut.setInstallProgress(level);
+ // We need to update the component name when the apk is installed
+ shortcut.status |= ShortcutInfo.FLAG_AUTOINTALL_ICON;
+ // Since the user is manually placing it on homescreen, it should not be auto-removed later
+ shortcut.status |= ShortcutInfo.FLAG_RESTORE_STARTED;
+ return shortcut;
+ }
+
+ public Intent getMarketIntent() {
+ return PackageManagerHelper.getMarketIntent(componentName.getPackageName());
+ }
+}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 207a7d4..17ad2b0 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -51,7 +51,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
@@ -570,7 +570,7 @@
try {
c.close();
} catch (IOException e) {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
Log.d(TAG, "Error closing", e);
}
}
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 0e91062..5e859c7 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -420,19 +420,20 @@
float shadowBlur = res.getDimension(R.dimen.widget_preview_shadow_blur);
float keyShadowDistance = res.getDimension(R.dimen.widget_preview_key_shadow_distance);
float corner = res.getDimension(R.dimen.widget_preview_corner_radius);
+ int shadowColor = ColorUtils.setAlphaComponent(
+ res.getColor(R.color.default_shadow_color_no_alpha),
+ ShadowGenerator.AMBIENT_SHADOW_ALPHA);
RectF bounds = new RectF(shadowBlur, shadowBlur,
width - shadowBlur, height - shadowBlur - keyShadowDistance);
p.setColor(Color.WHITE);
// Key shadow
- p.setShadowLayer(shadowBlur, 0, keyShadowDistance,
- ShadowGenerator.KEY_SHADOW_ALPHA << 24);
+ p.setShadowLayer(shadowBlur, 0, keyShadowDistance, shadowColor);
c.drawRoundRect(bounds, corner, corner, p);
// Ambient shadow
- p.setShadowLayer(shadowBlur, 0, 0,
- ColorUtils.setAlphaComponent(Color.BLACK, ShadowGenerator.AMBIENT_SHADOW_ALPHA));
+ p.setShadowLayer(shadowBlur, 0, 0, shadowColor);
c.drawRoundRect(bounds, corner, corner, p);
p.clearShadowLayer();
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 7562dd8..8f8d32c 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -43,6 +43,7 @@
import android.util.Log;
import android.util.Property;
import android.util.SparseArray;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
@@ -64,7 +65,6 @@
import com.android.launcher3.badge.FolderBadgeInfo;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
@@ -528,8 +528,6 @@
// Set the wallpaper dimensions when Launcher starts up
setWallpaperDimension();
-
- setEdgeGlowColor(getResources().getColor(R.color.workspace_edge_effect_color));
}
@Override
@@ -622,7 +620,7 @@
if (qsb == null) {
// In transposed layout, we add the QSB in the Grid. As workspace does not touch the
// edges, we do not need a full width QSB.
- qsb = mLauncher.getLayoutInflater().inflate(
+ qsb = LayoutInflater.from(getContext()).inflate(
mLauncher.getDeviceProfile().isVerticalBarLayout()
? R.layout.qsb_container : R.layout.qsb_blocker_view,
firstPage, false);
@@ -708,7 +706,7 @@
// Inflate the cell layout, but do not add it automatically so that we can get the newly
// created CellLayout.
- CellLayout newScreen = (CellLayout) mLauncher.getLayoutInflater().inflate(
+ CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
R.layout.workspace_screen, this, false /* attachToRoot */);
newScreen.setOnLongClickListener(mLongClickListener);
newScreen.setOnClickListener(mLauncher);
@@ -726,7 +724,7 @@
public void createCustomContentContainer() {
CellLayout customScreen = (CellLayout)
- mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, this, false);
+ LayoutInflater.from(getContext()).inflate(R.layout.workspace_screen, this, false);
customScreen.disableDragTarget();
customScreen.disableJailContent();
@@ -2619,7 +2617,7 @@
CellLayout parentCell = getParentCellLayoutForView(cell);
if (parentCell != null) {
parentCell.removeView(cell);
- } else if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw new NullPointerException("mDragInfo.cell has null parent");
}
addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
@@ -2952,7 +2950,7 @@
ItemInfo item = d.dragInfo;
if (item == null) {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw new NullPointerException("DragObject has null info");
}
return;
@@ -3609,7 +3607,7 @@
mDragInfo.container, mDragInfo.screenId);
if (cellLayout != null) {
cellLayout.onDropChild(mDragInfo.cell);
- } else if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw new RuntimeException("Invalid state: cellLayout == null in "
+ "Workspace#onDropCompleted. Please file a bug. ");
};
@@ -3635,7 +3633,7 @@
CellLayout parentCell = getParentCellLayoutForView(v);
if (parentCell != null) {
parentCell.removeView(v);
- } else if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
// When an app is uninstalled using the drop target, we wait until resume to remove
// the icon. We also remove all the corresponding items from the workspace at
// {@link Launcher#bindComponentsRemoved}. That call can come before or after
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index cc5fa8c..7219141 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -37,6 +37,7 @@
import com.android.launcher3.AppInfo;
import com.android.launcher3.BaseContainerView;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
@@ -45,6 +46,7 @@
import com.android.launcher3.Insettable;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
+import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
@@ -158,6 +160,17 @@
mSearchBarController.refreshSearchResult();
}
+ public void updatePromiseAppProgress(PromiseAppInfo app) {
+ int childCount = mAppsRecyclerView.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = mAppsRecyclerView.getChildAt(i);
+ if (child instanceof BubbleTextView && child.getTag() == app) {
+ BubbleTextView bubbleTextView = (BubbleTextView) child;
+ bubbleTextView.applyProgressLevel(app.level);
+ }
+ }
+ }
+
/**
* Removes some apps from the list.
*/
diff --git a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
index a1ff822..7d70a3e 100644
--- a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
+++ b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
@@ -210,7 +210,9 @@
for (RecyclerView.ViewHolder viewHolder : mTrackedFastScrollViews) {
int pos = viewHolder.getAdapterPosition();
boolean isActive = false;
- if (mCurrentFastScrollSection != null && pos > -1) {
+ if (mCurrentFastScrollSection != null
+ && pos > RecyclerView.NO_POSITION
+ && pos < mApps.getAdapterItems().size()) {
AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(pos);
isActive = item != null &&
mCurrentFastScrollSection.equals(item.sectionName) &&
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index f5cf7ef..f291a80 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -24,7 +24,7 @@
import com.android.launcher3.AppInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.compat.AlphabeticIndexCompat;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.discovery.AppDiscoveryAppInfo;
import com.android.launcher3.discovery.AppDiscoveryItem;
import com.android.launcher3.discovery.AppDiscoveryUpdateState;
@@ -440,7 +440,7 @@
if (info != null) {
mPredictedApps.add(info);
} else {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
Log.e(TAG, "Predicted app not found: " + ck);
}
}
diff --git a/src/com/android/launcher3/util/CircleRevealOutlineProvider.java b/src/com/android/launcher3/anim/CircleRevealOutlineProvider.java
similarity index 97%
rename from src/com/android/launcher3/util/CircleRevealOutlineProvider.java
rename to src/com/android/launcher3/anim/CircleRevealOutlineProvider.java
index 9fe5147..9fb6b49 100644
--- a/src/com/android/launcher3/util/CircleRevealOutlineProvider.java
+++ b/src/com/android/launcher3/anim/CircleRevealOutlineProvider.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.launcher3.util;
+package com.android.launcher3.anim;
public class CircleRevealOutlineProvider extends RevealOutlineAnimation {
diff --git a/src/com/android/launcher3/anim/PillHeightRevealOutlineProvider.java b/src/com/android/launcher3/anim/PillHeightRevealOutlineProvider.java
index be1e2d6..679e8e3 100644
--- a/src/com/android/launcher3/anim/PillHeightRevealOutlineProvider.java
+++ b/src/com/android/launcher3/anim/PillHeightRevealOutlineProvider.java
@@ -18,8 +18,6 @@
import android.graphics.Rect;
-import com.android.launcher3.util.PillRevealOutlineProvider;
-
/**
* Extension of {@link PillRevealOutlineProvider} which only changes the height of the pill.
* For now, we assume the height is added/removed from the bottom.
diff --git a/src/com/android/launcher3/util/PillRevealOutlineProvider.java b/src/com/android/launcher3/anim/PillRevealOutlineProvider.java
similarity index 98%
rename from src/com/android/launcher3/util/PillRevealOutlineProvider.java
rename to src/com/android/launcher3/anim/PillRevealOutlineProvider.java
index a57d69f..450f9db 100644
--- a/src/com/android/launcher3/util/PillRevealOutlineProvider.java
+++ b/src/com/android/launcher3/anim/PillRevealOutlineProvider.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.launcher3.util;
+package com.android.launcher3.anim;
import android.graphics.Rect;
import android.view.ViewOutlineProvider;
diff --git a/src/com/android/launcher3/util/RevealOutlineAnimation.java b/src/com/android/launcher3/anim/RevealOutlineAnimation.java
similarity index 96%
rename from src/com/android/launcher3/util/RevealOutlineAnimation.java
rename to src/com/android/launcher3/anim/RevealOutlineAnimation.java
index 4560477..51d00d9 100644
--- a/src/com/android/launcher3/util/RevealOutlineAnimation.java
+++ b/src/com/android/launcher3/anim/RevealOutlineAnimation.java
@@ -1,4 +1,4 @@
-package com.android.launcher3.util;
+package com.android.launcher3.anim;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -83,4 +83,8 @@
public void getOutline(View v, Outline outline) {
outline.setRoundRect(mOutline, mOutlineRadius);
}
+
+ public float getRadius() {
+ return mOutlineRadius;
+ }
}
diff --git a/src/com/android/launcher3/anim/RoundedRectRevealOutlineProvider.java b/src/com/android/launcher3/anim/RoundedRectRevealOutlineProvider.java
new file mode 100644
index 0000000..9c09477
--- /dev/null
+++ b/src/com/android/launcher3/anim/RoundedRectRevealOutlineProvider.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 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.anim;
+
+import android.graphics.Rect;
+
+/**
+ * A {@link RevealOutlineAnimation} that provides an outline that interpolates between two radii
+ * and two {@link Rect}s.
+ *
+ * An example usage of this provider is an outline that starts out as a circle and ends
+ * as a rounded rectangle.
+ */
+public class RoundedRectRevealOutlineProvider extends RevealOutlineAnimation {
+ private final float mStartRadius;
+ private final float mEndRadius;
+
+ private final Rect mStartRect;
+ private final Rect mEndRect;
+
+ public RoundedRectRevealOutlineProvider(float startRadius, float endRadius, Rect startRect,
+ Rect endRect) {
+ mStartRadius = startRadius;
+ mEndRadius = endRadius;
+ mStartRect = startRect;
+ mEndRect = endRect;
+ }
+
+ @Override
+ public boolean shouldRemoveElevationDuringAnimation() {
+ return false;
+ }
+
+ @Override
+ public void setProgress(float progress) {
+ mOutlineRadius = (1 - progress) * mStartRadius + progress * mEndRadius;
+
+ mOutline.left = (int) ((1 - progress) * mStartRect.left + progress * mEndRect.left);
+ mOutline.top = (int) ((1 - progress) * mStartRect.top + progress * mEndRect.top);
+ mOutline.right = (int) ((1 - progress) * mStartRect.right + progress * mEndRect.right);
+ mOutline.bottom = (int) ((1 - progress) * mStartRect.bottom + progress * mEndRect.bottom);
+ }
+}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
index c7fe0ce..112cca5 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -16,9 +16,13 @@
package com.android.launcher3.compat;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageInstaller;
+import android.support.annotation.NonNull;
import java.util.HashMap;
+import java.util.List;
public abstract class PackageInstallerCompat {
@@ -46,19 +50,34 @@
public abstract void onStop();
public static final class PackageInstallInfo {
+ public final ComponentName componentName;
public final String packageName;
+ public final int state;
+ public final int progress;
- public int state;
- public int progress;
-
- public PackageInstallInfo(String packageName) {
- this.packageName = packageName;
+ private PackageInstallInfo(@NonNull PackageInstaller.SessionInfo info) {
+ this.state = STATUS_INSTALLING;
+ this.packageName = info.getAppPackageName();
+ this.componentName = new ComponentName(packageName, "");
+ this.progress = (int) (info.getProgress() * 100f);
}
public PackageInstallInfo(String packageName, int state, int progress) {
- this.packageName = packageName;
this.state = state;
+ this.packageName = packageName;
+ this.componentName = new ComponentName(packageName, "");
this.progress = progress;
}
+
+ public static PackageInstallInfo fromInstallingState(PackageInstaller.SessionInfo info) {
+ return new PackageInstallInfo(info);
+ }
+
+ public static PackageInstallInfo fromState(int state, String packageName) {
+ return new PackageInstallInfo(packageName, state, 0 /* progress */);
+ }
+
}
+
+ public abstract List<PackageInstaller.SessionInfo> getAllVerifiedSessions();
}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index b87582f..bbf1546 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -17,6 +17,7 @@
package com.android.launcher3.compat;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionCallback;
import android.content.pm.PackageInstaller.SessionInfo;
@@ -28,23 +29,31 @@
import com.android.launcher3.IconCache;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.Thunk;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
public class PackageInstallerCompatVL extends PackageInstallerCompat {
+ private static final boolean DEBUG = false;
+
@Thunk final SparseArray<String> mActiveSessions = new SparseArray<>();
@Thunk final PackageInstaller mInstaller;
private final IconCache mCache;
private final Handler mWorker;
+ private final Context mAppContext;
+ private final HashMap<String,Boolean> mSessionVerifiedMap = new HashMap<>();
PackageInstallerCompatVL(Context context) {
+ mAppContext = context.getApplicationContext();
mInstaller = context.getPackageManager().getPackageInstaller();
mCache = LauncherAppState.getInstance(context).getIconCache();
mWorker = new Handler(LauncherModel.getWorkerLooper());
-
mInstaller.registerSessionCallback(mCallback, mWorker);
}
@@ -52,7 +61,7 @@
public HashMap<String, Integer> updateAndGetActiveSessionCache() {
HashMap<String, Integer> activePackages = new HashMap<>();
UserHandle user = Process.myUserHandle();
- for (SessionInfo info : mInstaller.getAllSessions()) {
+ for (SessionInfo info : getAllVerifiedSessions()) {
addSessionInfoToCache(info, user);
if (info.getAppPackageName() != null) {
activePackages.put(info.getAppPackageName(), (int) (info.getProgress() * 100));
@@ -86,7 +95,14 @@
@Override
public void onCreated(int sessionId) {
- pushSessionDisplayToLauncher(sessionId);
+ SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
+ if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS && sessionInfo != null) {
+ LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+ if (app != null) {
+ app.getModel().onInstallSessionCreated(
+ PackageInstallInfo.fromInstallingState(sessionInfo));
+ }
+ }
}
@Override
@@ -97,18 +113,17 @@
mActiveSessions.remove(sessionId);
if (packageName != null) {
- sendUpdate(new PackageInstallInfo(packageName,
- success ? STATUS_INSTALLED : STATUS_FAILED, 0));
+ sendUpdate(PackageInstallInfo.fromState(
+ success ? STATUS_INSTALLED : STATUS_FAILED,
+ packageName));
}
}
@Override
public void onProgressChanged(int sessionId, float progress) {
- SessionInfo session = mInstaller.getSessionInfo(sessionId);
+ SessionInfo session = verify(mInstaller.getSessionInfo(sessionId));
if (session != null && session.getAppPackageName() != null) {
- sendUpdate(new PackageInstallInfo(session.getAppPackageName(),
- STATUS_INSTALLING,
- (int) (session.getProgress() * 100)));
+ sendUpdate(PackageInstallInfo.fromInstallingState(session));
}
}
@@ -120,16 +135,46 @@
pushSessionDisplayToLauncher(sessionId);
}
- private void pushSessionDisplayToLauncher(int sessionId) {
- SessionInfo session = mInstaller.getSessionInfo(sessionId);
+ private SessionInfo pushSessionDisplayToLauncher(int sessionId) {
+ SessionInfo session = verify(mInstaller.getSessionInfo(sessionId));
if (session != null && session.getAppPackageName() != null) {
+ mActiveSessions.put(sessionId, session.getAppPackageName());
addSessionInfoToCache(session, Process.myUserHandle());
LauncherAppState app = LauncherAppState.getInstanceNoCreate();
-
if (app != null) {
app.getModel().updateSessionDisplayInfo(session.getAppPackageName());
}
+ return session;
}
+ return null;
}
};
+
+ private PackageInstaller.SessionInfo verify(PackageInstaller.SessionInfo sessionInfo) {
+ if (sessionInfo == null || sessionInfo.getInstallerPackageName() == null) {
+ return null;
+ }
+ String pkg = sessionInfo.getInstallerPackageName();
+ synchronized (mSessionVerifiedMap) {
+ if (!mSessionVerifiedMap.containsKey(pkg)) {
+ LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mAppContext);
+ boolean hasSystemFlag = launcherApps.getApplicationInfo(pkg,
+ ApplicationInfo.FLAG_SYSTEM, Process.myUserHandle()) != null;
+ mSessionVerifiedMap.put(pkg, DEBUG || hasSystemFlag);
+ }
+ }
+ return mSessionVerifiedMap.get(pkg) ? sessionInfo : null;
+ }
+
+ @Override
+ public List<SessionInfo> getAllVerifiedSessions() {
+ List<SessionInfo> list = new ArrayList<>(mInstaller.getAllSessions());
+ Iterator<SessionInfo> it = list.iterator();
+ while (it.hasNext()) {
+ if (verify(it.next()) == null) {
+ it.remove();
+ }
+ }
+ return list;
+ }
}
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index 840fcf5..503c2ec 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -121,6 +121,11 @@
}
@Override
+ public float getIconSize() {
+ return mIconSize;
+ }
+
+ @Override
public int maxNumItems() {
return MAX_NUM_ITEMS_IN_PREVIEW;
}
@@ -129,24 +134,4 @@
public boolean clipToBackground() {
return true;
}
-
- @Override
- public List<View> getItemsToDisplay(Folder folder) {
- List<View> items = new ArrayList<>(folder.getItemsInReadingOrder());
- int numItems = items.size();
- if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION && numItems > MAX_NUM_ITEMS_IN_PREVIEW) {
- // We match the icons in the preview with the layout of the opened folder (b/27944225),
- // but we still need to figure out how we want to handle updating the preview when the
- // upper left quadrant changes.
- int appsPerRow = folder.mContent.getPageAt(0).getCountX();
- int appsToDelete = appsPerRow - MAX_NUM_ITEMS_PER_ROW;
-
- // We only display the upper left quadrant.
- while (appsToDelete > 0) {
- items.remove(MAX_NUM_ITEMS_PER_ROW);
- appsToDelete--;
- }
- }
- return items.subList(0, Math.min(numItems, MAX_NUM_ITEMS_IN_PREVIEW));
- }
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 3d2ffb4..8bbc99d 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
+import android.support.v4.os.BuildCompat;
import android.text.InputType;
import android.text.Selection;
import android.util.AttributeSet;
@@ -46,6 +47,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Alarm;
import com.android.launcher3.AppInfo;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
@@ -66,8 +68,8 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
+import com.android.launcher3.anim.AnimationLayerSet;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragLayer;
@@ -75,12 +77,13 @@
import com.android.launcher3.pageindicators.PageIndicatorDots;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.CircleRevealOutlineProvider;
+import com.android.launcher3.anim.CircleRevealOutlineProvider;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.List;
/**
* Represents a set of icons chosen by the user or generated by the system.
@@ -133,8 +136,10 @@
@Thunk final ArrayList<View> mItemsInReadingOrder = new ArrayList<View>();
+ private AnimatorSet mCurrentAnimator;
+
private final int mExpandDuration;
- private final int mMaterialExpandDuration;
+ public final int mMaterialExpandDuration;
private final int mMaterialExpandStagger;
protected final Launcher mLauncher;
@@ -510,51 +515,31 @@
mState = STATE_SMALL;
}
- /**
- * Opens the user folder described by the specified tag. The opening of the folder
- * is animated relative to the specified View. If the View is null, no animation
- * is played.
- */
- public void animateOpen() {
- Folder openFolder = getOpen(mLauncher);
- if (openFolder != null && openFolder != this) {
- // Close any open folder before opening a folder.
- openFolder.close(true);
+ private void startAnimation(final AnimatorSet a) {
+ if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
+ mCurrentAnimator.cancel();
}
-
- DragLayer dragLayer = mLauncher.getDragLayer();
- // Just verify that the folder hasn't already been added to the DragLayer.
- // There was a one-off crash where the folder had a parent already.
- if (getParent() == null) {
- dragLayer.addView(this);
- mDragController.addDropTarget(this);
- } else {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
- Log.e(TAG, "Opening folder (" + this + ") which already has a parent:"
- + getParent());
+ a.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mState = STATE_ANIMATING;
+ mCurrentAnimator = a;
}
- }
- mIsOpen = true;
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCurrentAnimator = null;
+ }
+ });
+ a.start();
+ }
- mContent.completePendingPageChanges();
- if (!mDragInProgress) {
- // Open on the first page.
- mContent.snapToPageImmediately(0);
- }
-
- // This is set to true in close(), but isn't reset to false until onDropCompleted(). This
- // leads to an inconsistent state if you drag out of the folder and drag back in without
- // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
- mDeleteFolderOnDropCompleted = false;
-
- final Runnable onCompleteRunnable;
+ private AnimatorSet getOpeningAnimator() {
prepareReveal();
- centerAboutIcon();
-
mFolderIcon.growAndFadeOut();
AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
+
int width = getFolderWidth();
int height = getFolderHeight();
@@ -596,24 +581,75 @@
anim.play(textAlpha);
anim.play(reveal);
- mContent.setLayerType(LAYER_TYPE_HARDWARE, null);
- mFooter.setLayerType(LAYER_TYPE_HARDWARE, null);
+ AnimationLayerSet layerSet = new AnimationLayerSet();
+ layerSet.addView(mContent);
+ layerSet.addView(mFooter);
+ anim.addListener(layerSet);
+
+ return anim;
+ }
+
+ /**
+ * Opens the user folder described by the specified tag. The opening of the folder
+ * is animated relative to the specified View. If the View is null, no animation
+ * is played.
+ */
+ public void animateOpen() {
+ Folder openFolder = getOpen(mLauncher);
+ if (openFolder != null && openFolder != this) {
+ // Close any open folder before opening a folder.
+ openFolder.close(true);
+ }
+
+ DragLayer dragLayer = mLauncher.getDragLayer();
+ // Just verify that the folder hasn't already been added to the DragLayer.
+ // There was a one-off crash where the folder had a parent already.
+ if (getParent() == null) {
+ dragLayer.addView(this);
+ mDragController.addDropTarget(this);
+ } else {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ Log.e(TAG, "Opening folder (" + this + ") which already has a parent:"
+ + getParent());
+ }
+ }
+
+ mIsOpen = true;
+
+ mContent.completePendingPageChanges();
+ if (!mDragInProgress) {
+ // Open on the first page.
+ mContent.snapToPageImmediately(0);
+ }
+
+ // This is set to true in close(), but isn't reset to false until onDropCompleted(). This
+ // leads to an inconsistent state if you drag out of the folder and drag back in without
+ // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
+ mDeleteFolderOnDropCompleted = false;
+
+ final Runnable onCompleteRunnable;
+ centerAboutIcon();
+
+ AnimatorSet anim = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
+ ? new FolderAnimationManager(this, true /* isOpening */).getAnimator()
+ : getOpeningAnimator();
onCompleteRunnable = new Runnable() {
@Override
public void run() {
- mContent.setLayerType(LAYER_TYPE_NONE, null);
- mFooter.setLayerType(LAYER_TYPE_NONE, null);
mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
}
};
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
+ if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+ mFolderIcon.setVisibility(INVISIBLE);
+ }
+
Utilities.sendCustomAccessibilityEvent(
Folder.this,
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
mContent.getAccessibilityDescription());
- mState = STATE_ANIMATING;
}
@Override
public void onAnimationEnd(Animator animation) {
@@ -659,7 +695,7 @@
}
mPageIndicator.stopAllAnimations();
- anim.start();
+ startAnimation(anim);
// Make sure the folder picks up the last drag move even if the finger doesn't move.
if (mDragController.isDragging()) {
@@ -697,7 +733,7 @@
mFolderName.dispatchBackKey();
}
- if (mFolderIcon != null) {
+ if (mFolderIcon != null && !FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
mFolderIcon.shrinkAndFadeIn(animate);
}
@@ -715,12 +751,24 @@
parent.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
+ private AnimatorSet getClosingAnimator() {
+ AnimatorSet animatorSet = LauncherAnimUtils.createAnimatorSet();
+ animatorSet.play(LauncherAnimUtils.ofViewAlphaAndScale(this, 0, 0.9f, 0.9f));
+
+ AnimationLayerSet layerSet = new AnimationLayerSet();
+ layerSet.addView(this);
+ animatorSet.addListener(layerSet);
+ animatorSet.setDuration(mExpandDuration);
+ return animatorSet;
+ }
+
private void animateClosed() {
- final ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(this, 0, 0.9f, 0.9f);
- oa.addListener(new AnimatorListenerAdapter() {
+ AnimatorSet a = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
+ ? new FolderAnimationManager(this, false /* isOpening */).getAnimator()
+ : getClosingAnimator();
+ a.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- setLayerType(LAYER_TYPE_NONE, null);
closeComplete(true);
}
@Override
@@ -729,12 +777,9 @@
Folder.this,
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
getContext().getString(R.string.folder_closed));
- mState = STATE_ANIMATING;
}
});
- oa.setDuration(mExpandDuration);
- setLayerType(LAYER_TYPE_HARDWARE, null);
- oa.start();
+ startAnimation(a);
}
private void closeComplete(boolean wasAnimated) {
@@ -745,10 +790,14 @@
}
mDragController.removeDropTarget(this);
clearFocus();
- if (wasAnimated) {
- mFolderIcon.requestFocus();
+ if (mFolderIcon != null) {
+ mFolderIcon.setVisibility(View.VISIBLE);
+ if (wasAnimated) {
+ mFolderIcon.requestFocus();
+ }
}
+
if (mRearrangeOnClose) {
rearrangeChildren();
mRearrangeOnClose = false;
@@ -1436,6 +1485,26 @@
return mItemsInReadingOrder;
}
+ public List<BubbleTextView> getItemsOnCurrentPage() {
+ ArrayList<View> allItems = getItemsInReadingOrder();
+ int currentPage = mContent.getCurrentPage();
+ int lastPage = mContent.getPageCount() - 1;
+ int totalItemsInFolder = allItems.size();
+ int itemsPerPage = mContent.itemsPerPage();
+ int numItemsOnCurrentPage = currentPage == lastPage
+ ? totalItemsInFolder - (itemsPerPage * currentPage)
+ : itemsPerPage;
+
+ int startIndex = currentPage * itemsPerPage;
+ int endIndex = startIndex + numItemsOnCurrentPage;
+
+ List<BubbleTextView> itemsOnCurrentPage = new ArrayList<>(numItemsOnCurrentPage);
+ for (int i = startIndex; i < endIndex; ++i) {
+ itemsOnCurrentPage.add((BubbleTextView) allItems.get(i));
+ }
+ return itemsOnCurrentPage;
+ }
+
public void onFocusChange(View v, boolean hasFocus) {
if (v == mFolderName) {
if (hasFocus) {
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
new file mode 100644
index 0000000..a62d1b7
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2017 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.folder;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
+import android.support.v4.graphics.ColorUtils;
+import android.util.Property;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.util.Themes;
+
+import java.util.List;
+
+/**
+ * Manages the opening and closing animations for a {@link Folder}.
+ *
+ * All of the animations are done in the Folder.
+ * ie. When the user taps on the FolderIcon, we immediately hide the FolderIcon and show the Folder
+ * in its place before starting the animation.
+ */
+public class FolderAnimationManager {
+
+ private Folder mFolder;
+ private FolderPagedView mContent;
+ private GradientDrawable mFolderBackground;
+
+ private FolderIcon mFolderIcon;
+ private FolderIcon.PreviewBackground mPreviewBackground;
+
+ private Context mContext;
+ private Launcher mLauncher;
+
+ private final boolean mIsOpening;
+
+ private final int mDuration;
+ private final int mDelay;
+
+ private final TimeInterpolator mOpeningInterpolator;
+ private final TimeInterpolator mClosingInterpolator;
+ private final TimeInterpolator mPreviewItemOpeningInterpolator;
+ private final TimeInterpolator mPreviewItemClosingInterpolator;
+
+ private final FolderIcon.PreviewItemDrawingParams mTmpParams =
+ new FolderIcon.PreviewItemDrawingParams(0, 0, 0, 0);
+
+ private static final Property<View, Float> SCALE_PROPERTY =
+ new Property<View, Float>(Float.class, "scale") {
+ @Override
+ public Float get(View view) {
+ return view.getScaleX();
+ }
+
+ @Override
+ public void set(View view, Float scale) {
+ view.setScaleX(scale);
+ view.setScaleY(scale);
+ }
+ };
+
+ private static final Property<List<BubbleTextView>, Integer> ITEMS_TEXT_COLOR_PROPERTY =
+ new Property<List<BubbleTextView>, Integer>(Integer.class, "textColor") {
+ @Override
+ public Integer get(List<BubbleTextView> items) {
+ return items.get(0).getCurrentTextColor();
+ }
+
+ @Override
+ public void set(List<BubbleTextView> items, Integer color) {
+ int size = items.size();
+
+ for (int i = 0; i < size; ++i) {
+ items.get(i).setTextColor(color);
+ }
+ }
+ };
+
+ public FolderAnimationManager(Folder folder, boolean isOpening) {
+ mFolder = folder;
+ mContent = folder.mContent;
+ mFolderBackground = (GradientDrawable) mFolder.getBackground();
+
+ mFolderIcon = folder.mFolderIcon;
+ mPreviewBackground = mFolderIcon.mBackground;
+
+ mContext = folder.getContext();
+ mLauncher = folder.mLauncher;
+
+ mIsOpening = isOpening;
+
+ mDuration = mFolder.mMaterialExpandDuration;
+ mDelay = mContext.getResources().getInteger(R.integer.config_folderDelay);
+
+ mOpeningInterpolator = AnimationUtils.loadInterpolator(mContext,
+ R.interpolator.folder_opening_interpolator);
+ mClosingInterpolator = AnimationUtils.loadInterpolator(mContext,
+ R.interpolator.folder_closing_interpolator);
+ mPreviewItemOpeningInterpolator = AnimationUtils.loadInterpolator(mContext,
+ R.interpolator.folder_preview_item_opening_interpolator);
+ mPreviewItemClosingInterpolator = AnimationUtils.loadInterpolator(mContext,
+ R.interpolator.folder_preview_item_closing_interpolator);
+ }
+
+
+ /**
+ * Prepares the Folder for animating between open / closed states.
+ */
+ public AnimatorSet getAnimator() {
+ final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams();
+ FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule();
+ final List<BubbleTextView> itemsInPreview = mFolderIcon.getItemsToDisplay();
+
+ // Match size/scale of icons in the preview
+ float previewScale = rule.scaleForItem(0, itemsInPreview.size());
+ float previewSize = rule.getIconSize() * previewScale;
+ float folderScale = previewSize / itemsInPreview.get(0).getIconSize();
+
+ final float initialScale = folderScale;
+ final float finalScale = 1f;
+ float scale = mIsOpening ? initialScale : finalScale;
+ mFolder.setScaleX(scale);
+ mFolder.setScaleY(scale);
+ mFolder.setPivotX(0);
+ mFolder.setPivotY(0);
+
+ // Match position of the FolderIcon
+ final Rect folderIconPos = new Rect();
+ float scaleRelativeToDragLayer = mLauncher.getDragLayer()
+ .getDescendantRectRelativeToSelf(mFolderIcon, folderIconPos);
+ folderScale *= scaleRelativeToDragLayer;
+
+ // We want to create a small X offset for the preview items, so that they follow their
+ // expected path to their final locations. ie. an icon should not move right, if it's final
+ // location is to its left. This value is arbitrarily defined.
+ final int nudgeOffsetX = (int) (previewSize / 2);
+
+ final int paddingOffsetX = (int) ((mFolder.getPaddingLeft() + mContent.getPaddingLeft())
+ * folderScale);
+ final int paddingOffsetY = (int) ((mFolder.getPaddingTop() + mContent.getPaddingTop())
+ * folderScale);
+
+ int initialX = folderIconPos.left + mFolderIcon.mBackground.getOffsetX() - paddingOffsetX
+ - nudgeOffsetX;
+ int initialY = folderIconPos.top + mFolderIcon.mBackground.getOffsetY() - paddingOffsetY;
+ final float xDistance = initialX - lp.x;
+ final float yDistance = initialY - lp.y;
+
+ // Set up the Folder background.
+ final int finalColor = Themes.getAttrColor(mContext, android.R.attr.colorPrimary);
+ final int initialColor =
+ ColorUtils.setAlphaComponent(finalColor, mPreviewBackground.getBackgroundAlpha());
+ mFolderBackground.setColor(mIsOpening ? initialColor : finalColor);
+
+ // Initialize the Folder items' text.
+ final List<BubbleTextView> items = mFolder.getItemsOnCurrentPage();
+ final int finalTextColor = Themes.getAttrColor(mContext, android.R.attr.textColorSecondary);
+ ITEMS_TEXT_COLOR_PROPERTY.set(items, mIsOpening ? Color.TRANSPARENT
+ : finalTextColor);
+
+ // Set up the reveal animation that clips the Folder.
+ float stroke = mPreviewBackground.getStrokeWidth();
+ int initialSize = (int) ((mFolderIcon.mBackground.getRadius() * 2 + stroke) / folderScale);
+ int totalOffsetX = paddingOffsetX + Math.round(nudgeOffsetX / folderScale);
+ int unscaledStroke = (int) Math.floor(stroke / folderScale);
+ Rect startRect = new Rect(totalOffsetX + unscaledStroke, unscaledStroke,
+ totalOffsetX + initialSize, initialSize);
+ Rect endRect = new Rect(0, 0, lp.width, lp.height);
+ float finalRadius = Utilities.pxFromDp(2, mContext.getResources().getDisplayMetrics());
+
+ // Create the animators.
+ AnimatorSet a = LauncherAnimUtils.createAnimatorSet();
+
+ play(a, getAnimator(mFolder, View.TRANSLATION_X, xDistance, 0f));
+ play(a, getAnimator(mFolder, View.TRANSLATION_Y, yDistance, 0f));
+ play(a, getAnimator(mFolder, SCALE_PROPERTY, initialScale, finalScale));
+ play(a, getAnimator(items, ITEMS_TEXT_COLOR_PROPERTY, Color.TRANSPARENT, finalTextColor));
+ play(a, getAnimator(mFolderBackground, "color", initialColor, finalColor));
+ play(a, new RoundedRectRevealOutlineProvider(initialSize / 2f, finalRadius, startRect,
+ endRect).createRevealAnimator(mFolder, !mIsOpening));
+
+ a.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ ITEMS_TEXT_COLOR_PROPERTY.set(items, finalTextColor);
+ mFolder.setTranslationX(0.0f);
+ mFolder.setTranslationY(0.0f);
+ mFolder.setScaleX(1f);
+ mFolder.setScaleY(1f);
+ }
+ });
+
+ // We set the interpolator on all current child animators here, because the preview item
+ // animators may use a different interpolator.
+ for (Animator animator : a.getChildAnimations()) {
+ animator.setInterpolator(mIsOpening ? mOpeningInterpolator : mClosingInterpolator);
+ }
+
+ addPreviewItemAnimatorsToSet(a, folderScale, nudgeOffsetX);
+ return a;
+ }
+
+ /**
+ * Animate the items that are displayed in the preview.
+ */
+ private void addPreviewItemAnimatorsToSet(AnimatorSet animatorSet, final float folderScale,
+ int nudgeOffsetX) {
+ FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule();
+ final List<BubbleTextView> itemsInPreview = mFolderIcon.getItemsToDisplay();
+ final int numItemsInPreview = itemsInPreview.size();
+
+ TimeInterpolator previewItemInterpolator = getPreviewItemInterpolator();
+
+ ShortcutAndWidgetContainer cwc = mContent.getPageAt(0).getShortcutsAndWidgets();
+ for (int i = 0; i < numItemsInPreview; ++i) {
+ final BubbleTextView btv = itemsInPreview.get(i);
+ CellLayout.LayoutParams btvLp = (CellLayout.LayoutParams) btv.getLayoutParams();
+
+ // Calculate the final values in the LayoutParams.
+ btvLp.isLockedToGrid = true;
+ cwc.setupLp(btv);
+
+ // Match scale of icons in the preview.
+ float previewScale = rule.scaleForItem(i, numItemsInPreview);
+ float previewSize = rule.getIconSize() * previewScale;
+ float iconScale = previewSize / itemsInPreview.get(i).getIconSize();
+
+ final float initialScale = iconScale / folderScale;
+ final float finalScale = 1f;
+ float scale = mIsOpening ? initialScale : finalScale;
+ btv.setScaleX(scale);
+ btv.setScaleY(scale);
+
+ // Match positions of the icons in the folder with their positions in the preview
+ rule.computePreviewItemDrawingParams(i, numItemsInPreview, mTmpParams);
+ // The PreviewLayoutRule assumes that the icon size takes up the entire width so we
+ // offset by the actual size.
+ int iconOffsetX = (int) ((btvLp.width - btv.getIconSize()) * iconScale) / 2;
+
+ final int previewPosX =
+ (int) ((mTmpParams.transX - iconOffsetX + nudgeOffsetX) / folderScale);
+ final int previewPosY = (int) (mTmpParams.transY / folderScale);
+
+ final float xDistance = previewPosX - btvLp.x;
+ final float yDistance = previewPosY - btvLp.y;
+
+ Animator translationX = getAnimator(btv, View.TRANSLATION_X, xDistance, 0f);
+ translationX.setInterpolator(previewItemInterpolator);
+ play(animatorSet, translationX);
+
+ Animator translationY = getAnimator(btv, View.TRANSLATION_Y, yDistance, 0f);
+ translationY.setInterpolator(previewItemInterpolator);
+ play(animatorSet, translationY);
+
+ Animator scaleAnimator = getAnimator(btv, SCALE_PROPERTY, initialScale, finalScale);
+ scaleAnimator.setInterpolator(previewItemInterpolator);
+ play(animatorSet, scaleAnimator);
+
+ if (mFolder.getItemCount() > FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+ // These delays allows the preview items to move as part of the Folder's motion,
+ // and its only necessary for large folders because of differing interpolators.
+ if (mIsOpening) {
+ translationX.setStartDelay(mDelay);
+ translationY.setStartDelay(mDelay);
+ scaleAnimator.setStartDelay(mDelay);
+ }
+ translationX.setDuration(translationX.getDuration() - mDelay);
+ translationY.setDuration(translationY.getDuration() - mDelay);
+ scaleAnimator.setDuration(scaleAnimator.getDuration() - mDelay);
+ }
+
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ // Necessary to initialize values here because of the start delay.
+ if (mIsOpening) {
+ btv.setTranslationX(xDistance);
+ btv.setTranslationY(yDistance);
+ btv.setScaleX(initialScale);
+ btv.setScaleY(initialScale);
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ btv.setTranslationX(0.0f);
+ btv.setTranslationY(0.0f);
+ btv.setScaleX(1f);
+ btv.setScaleY(1f);
+ }
+ });
+ }
+ }
+
+ private void play(AnimatorSet as, Animator a) {
+ a.setDuration(mDuration);
+ as.play(a);
+ }
+
+ private TimeInterpolator getPreviewItemInterpolator() {
+ if (mFolder.getItemCount() > FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+ // With larger folders, we want the preview items to reach their final positions faster
+ // (when opening) and later (when closing) so that they appear aligned with the rest of
+ // the folder items when they are both visible.
+ return mIsOpening ? mPreviewItemOpeningInterpolator : mPreviewItemClosingInterpolator;
+ }
+ return mIsOpening ? mOpeningInterpolator : mClosingInterpolator;
+ }
+
+ private Animator getAnimator(View view, Property property, float v1, float v2) {
+ return mIsOpening
+ ? ObjectAnimator.ofFloat(view, property, v1, v2)
+ : ObjectAnimator.ofFloat(view, property, v2, v1);
+ }
+
+ private Animator getAnimator(List<BubbleTextView> items, Property property, int v1, int v2) {
+ return mIsOpening
+ ? ObjectAnimator.ofArgb(items, property, v1, v2)
+ : ObjectAnimator.ofArgb(items, property, v2, v1);
+ }
+
+ private Animator getAnimator(GradientDrawable drawable, String property, int v1, int v2) {
+ return mIsOpening
+ ? ObjectAnimator.ofArgb(drawable, property, v1, v2)
+ : ObjectAnimator.ofArgb(drawable, property, v2, v1);
+ }
+}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index f216010..ced9c9e 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -125,6 +125,7 @@
private float mSlop;
+ FolderIconPreviewVerifier mPreviewVerifier;
private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
private ArrayList<PreviewItemDrawingParams> mDrawingParams = new ArrayList<PreviewItemDrawingParams>();
private Drawable mReferenceDrawable = null;
@@ -179,7 +180,8 @@
}
DeviceProfile grid = launcher.getDeviceProfile();
- FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
+ FolderIcon icon = (FolderIcon) LayoutInflater.from(group.getContext())
+ .inflate(resId, group, false);
icon.setClipToPadding(false);
icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
@@ -219,6 +221,7 @@
private void setFolder(Folder folder) {
mFolder = folder;
+ mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
updateItemDrawingParams(false);
}
@@ -405,6 +408,10 @@
mBadgeInfo = badgeInfo;
}
+ public PreviewLayoutRule getLayoutRule() {
+ return mPreviewLayoutRule;
+ }
+
/**
* Sets mBadgeScale to 1 or 0, animating if oldCount or newCount is 0
* (the badge is being added or removed).
@@ -822,6 +829,14 @@
};
animateScale(1f, 1f, onStart, onEnd);
}
+
+ public int getBackgroundAlpha() {
+ return (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
+ }
+
+ public float getStrokeWidth() {
+ return mStrokeWidth;
+ }
}
public void setFolderBackground(PreviewBackground bg) {
@@ -989,8 +1004,26 @@
return mFolderName.getVisibility() == VISIBLE;
}
+ public List<BubbleTextView> getItemsToDisplay() {
+ mPreviewVerifier.setFolderInfo(mFolder.getInfo());
+
+ List<BubbleTextView> itemsToDisplay = new ArrayList<>();
+ List<View> allItems = mFolder.getItemsInReadingOrder();
+ int numItems = allItems.size();
+ for (int rank = 0; rank < numItems; ++rank) {
+ if (mPreviewVerifier.isItemInPreview(rank)) {
+ itemsToDisplay.add((BubbleTextView) allItems.get(rank));
+ }
+
+ if (itemsToDisplay.size() == FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+ break;
+ }
+ }
+ return itemsToDisplay;
+ }
+
private void updateItemDrawingParams(boolean animate) {
- List<View> items = mPreviewLayoutRule.getItemsToDisplay(mFolder);
+ List<BubbleTextView> items = getItemsToDisplay();
int nItemsInPreview = items.size();
int prevNumItems = mDrawingParams.size();
@@ -1005,7 +1038,7 @@
for (int i = 0; i < mDrawingParams.size(); i++) {
PreviewItemDrawingParams p = mDrawingParams.get(i);
- p.drawable = ((TextView) items.get(i)).getCompoundDrawables()[1];
+ p.drawable = items.get(i).getCompoundDrawables()[1];
if (!animate || FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON) {
computePreviewItemDrawingParams(i, nItemsInPreview, p);
@@ -1174,8 +1207,8 @@
PreviewItemDrawingParams params);
void init(int availableSpace, int intrinsicIconSize, boolean rtl);
float scaleForItem(int index, int totalNumItems);
+ float getIconSize();
int maxNumItems();
boolean clipToBackground();
- List<View> getItemsToDisplay(Folder folder);
}
}
diff --git a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
new file mode 100644
index 0000000..de962b0
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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.folder;
+
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.config.FeatureFlags;
+
+/**
+ * Verifies whether an item in a Folder is displayed in the FolderIcon preview.
+ */
+public class FolderIconPreviewVerifier {
+
+ private final int mMaxGridCountX;
+ private final int mMaxGridCountY;
+ private final int mMaxItemsPerPage;
+ private final int[] mGridSize = new int[2];
+
+ private int mGridCountX;
+ private boolean mDisplayingUpperLeftQuadrant = false;
+
+ public FolderIconPreviewVerifier(InvariantDeviceProfile profile) {
+ mMaxGridCountX = profile.numFolderColumns;
+ mMaxGridCountY = profile.numFolderRows;
+ mMaxItemsPerPage = mMaxGridCountX * mMaxGridCountY;
+ }
+
+ public void setFolderInfo(FolderInfo info) {
+ int numItemsInFolder = info.contents.size();
+ mDisplayingUpperLeftQuadrant = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
+ && !FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON
+ && numItemsInFolder > FolderIcon.NUM_ITEMS_IN_PREVIEW;
+
+ if (mDisplayingUpperLeftQuadrant) {
+ FolderPagedView.calculateGridSize(info.contents.size(), 0, 0, mMaxGridCountX,
+ mMaxGridCountY, mMaxItemsPerPage, mGridSize);
+ mGridCountX = mGridSize[0];
+ }
+ }
+
+ public boolean isItemInPreview(int rank) {
+ if (mDisplayingUpperLeftQuadrant) {
+ // Returns true iff the icon is in the 2x2 upper left quadrant of the Folder.
+ int col = rank % mGridCountX;
+ int row = rank / mGridCountX;
+ return col < 2 && row < 2;
+ }
+ return rank < FolderIcon.NUM_ITEMS_IN_PREVIEW;
+ }
+}
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index eecce18..2a6007a 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -35,17 +35,14 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace.ItemOperator;
-import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.pageindicators.PageIndicator;
-import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -68,7 +65,7 @@
*/
private static final float SCROLL_HINT_FRACTION = 0.07f;
- private static final int[] sTempPosArray = new int[2];
+ private static final int[] sTmpArray = new int[2];
public final boolean mIsRtl;
@@ -108,7 +105,6 @@
mIsRtl = Utilities.isRtl(getResources());
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- setEdgeGlowColor(Themes.getAttrColor(context, android.R.attr.colorEdgeEffect));
mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
}
@@ -120,40 +116,58 @@
}
/**
- * Sets up the grid size such that {@param count} items can fit in the grid.
+ * Calculates the grid size such that {@param count} items can fit in the grid.
* The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while
* maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}.
*/
- private void setupContentDimensions(int count) {
- mAllocatedContentSize = count;
+ public static void calculateGridSize(int count, int countX, int countY, int maxCountX,
+ int maxCountY, int maxItemsPerPage, int[] out) {
boolean done;
- if (count >= mMaxItemsPerPage) {
- mGridCountX = mMaxCountX;
- mGridCountY = mMaxCountY;
+ int gridCountX = countX;
+ int gridCountY = countY;
+
+ if (count >= maxItemsPerPage) {
+ gridCountX = maxCountX;
+ gridCountY = maxCountY;
done = true;
} else {
done = false;
}
while (!done) {
- int oldCountX = mGridCountX;
- int oldCountY = mGridCountY;
- if (mGridCountX * mGridCountY < count) {
+ int oldCountX = gridCountX;
+ int oldCountY = gridCountY;
+ if (gridCountX * gridCountY < count) {
// Current grid is too small, expand it
- if ((mGridCountX <= mGridCountY || mGridCountY == mMaxCountY) && mGridCountX < mMaxCountX) {
- mGridCountX++;
- } else if (mGridCountY < mMaxCountY) {
- mGridCountY++;
+ if ((gridCountX <= gridCountY || gridCountY == maxCountY)
+ && gridCountX < maxCountX) {
+ gridCountX++;
+ } else if (gridCountY < maxCountY) {
+ gridCountY++;
}
- if (mGridCountY == 0) mGridCountY++;
- } else if ((mGridCountY - 1) * mGridCountX >= count && mGridCountY >= mGridCountX) {
- mGridCountY = Math.max(0, mGridCountY - 1);
- } else if ((mGridCountX - 1) * mGridCountY >= count) {
- mGridCountX = Math.max(0, mGridCountX - 1);
+ if (gridCountY == 0) gridCountY++;
+ } else if ((gridCountY - 1) * gridCountX >= count && gridCountY >= gridCountX) {
+ gridCountY = Math.max(0, gridCountY - 1);
+ } else if ((gridCountX - 1) * gridCountY >= count) {
+ gridCountX = Math.max(0, gridCountX - 1);
}
- done = mGridCountX == oldCountX && mGridCountY == oldCountY;
+ done = gridCountX == oldCountX && gridCountY == oldCountY;
}
+ out[0] = gridCountX;
+ out[1] = gridCountY;
+ }
+
+ /**
+ * Sets up the grid size such that {@param count} items can fit in the grid.
+ */
+ public void setupContentDimensions(int count) {
+ mAllocatedContentSize = count;
+ calculateGridSize(count, mGridCountX, mGridCountY, mMaxCountX, mMaxCountY, mMaxItemsPerPage,
+ sTmpArray);
+ mGridCountX = sTmpArray[0];
+ mGridCountY = sTmpArray[1];
+
// Update grid size
for (int i = getPageCount() - 1; i >= 0; i--) {
getPageAt(i).setGridSize(mGridCountX, mGridCountY);
@@ -314,6 +328,8 @@
int position = 0;
int newX, newY, rank;
+ FolderIconPreviewVerifier verifier = new FolderIconPreviewVerifier(
+ Launcher.getLauncher(getContext()).getDeviceProfile().inv);
rank = 0;
for (int i = 0; i < itemCount; i++) {
View v = list.size() > i ? list.get(i) : null;
@@ -346,7 +362,7 @@
currentPage.addViewToCellLayout(
v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true);
- if (rank < FolderIcon.NUM_ITEMS_IN_PREVIEW && v instanceof BubbleTextView) {
+ if (verifier.isItemInPreview(rank) && v instanceof BubbleTextView) {
((BubbleTextView) v).verifyHighRes();
}
}
@@ -400,12 +416,12 @@
public int findNearestArea(int pixelX, int pixelY) {
int pageIndex = getNextPage();
CellLayout page = getPageAt(pageIndex);
- page.findNearestArea(pixelX, pixelY, 1, 1, sTempPosArray);
+ page.findNearestArea(pixelX, pixelY, 1, 1, sTmpArray);
if (mFolder.isLayoutRtl()) {
- sTempPosArray[0] = page.getCountX() - sTempPosArray[0] - 1;
+ sTmpArray[0] = page.getCountX() - sTmpArray[0] - 1;
}
return Math.min(mAllocatedContentSize - 1,
- pageIndex * mMaxItemsPerPage + sTempPosArray[1] * mGridCountX + sTempPosArray[0]);
+ pageIndex * mMaxItemsPerPage + sTmpArray[1] * mGridCountX + sTmpArray[0]);
}
public boolean isFull() {
diff --git a/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
index 297203a..9c8c2ef 100644
--- a/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
@@ -87,6 +87,11 @@
}
@Override
+ public float getIconSize() {
+ return mBaselineIconSize;
+ }
+
+ @Override
public float scaleForItem(int index, int numItems) {
// Scale is determined by the position of the icon in the preview.
index = MAX_NUM_ITEMS_IN_PREVIEW - index - 1;
@@ -98,10 +103,4 @@
public boolean clipToBackground() {
return false;
}
-
- @Override
- public List<View> getItemsToDisplay(Folder folder) {
- List<View> items = folder.getItemsInReadingOrder();
- return items.subList(0, Math.min(items.size(), MAX_NUM_ITEMS_IN_PREVIEW));
- }
}
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index bb136f7..492d853 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -29,7 +29,7 @@
import com.android.launcher3.LauncherAppWidgetHostView;
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
/**
@@ -138,7 +138,7 @@
}
public final void generateDragOutline(Canvas canvas) {
- if (ProviderConfig.IS_DOGFOOD_BUILD && generatedDragOutline != null) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && generatedDragOutline != null) {
throw new RuntimeException("Drag outline generated twice");
}
diff --git a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
index c9873d9..b221828 100644
--- a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
+++ b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
@@ -31,7 +31,7 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.R;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import java.nio.ByteBuffer;
@@ -86,7 +86,7 @@
* bitmap.
*/
public void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas) {
- if (ProviderConfig.IS_DOGFOOD_BUILD && srcDst.getConfig() != Bitmap.Config.ALPHA_8) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && srcDst.getConfig() != Bitmap.Config.ALPHA_8) {
throw new RuntimeException("Outline blue is only supported on alpha bitmaps");
}
diff --git a/src/com/android/launcher3/logging/DumpTargetWrapper.java b/src/com/android/launcher3/logging/DumpTargetWrapper.java
new file mode 100644
index 0000000..2646a22
--- /dev/null
+++ b/src/com/android/launcher3/logging/DumpTargetWrapper.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2017 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.logging;
+
+import android.os.Process;
+import android.text.TextUtils;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.model.nano.LauncherDumpProto;
+import com.android.launcher3.model.nano.LauncherDumpProto.ContainerType;
+import com.android.launcher3.model.nano.LauncherDumpProto.DumpTarget;
+import com.android.launcher3.model.nano.LauncherDumpProto.ItemType;
+import com.android.launcher3.model.nano.LauncherDumpProto.UserType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class can be used when proto definition doesn't support nesting.
+ */
+public class DumpTargetWrapper {
+ DumpTarget node;
+ ArrayList<DumpTargetWrapper> children;
+
+ public DumpTargetWrapper() {
+ children = new ArrayList<>();
+ }
+
+ public DumpTargetWrapper(DumpTarget t) {
+ this();
+ node = t;
+ }
+
+ public DumpTargetWrapper(int containerType, int id) {
+ this();
+ node = newContainerTarget(containerType, id);
+ }
+
+ public DumpTargetWrapper(ItemInfo info) {
+ this();
+ node = newItemTarget(info);
+ }
+
+ public DumpTarget getDumpTarget() {
+ return node;
+ }
+
+ public void add(DumpTargetWrapper child) {
+ children.add(child);
+ }
+
+ public List<DumpTarget> getFlattenedList() {
+ ArrayList<DumpTarget> list = new ArrayList<>();
+ list.add(node);
+ if (!children.isEmpty()) {
+ for(DumpTargetWrapper t: children) {
+ list.addAll(t.getFlattenedList());
+ }
+ list.add(node); // add a delimiter empty object
+ }
+ return list;
+ }
+ public DumpTarget newItemTarget(ItemInfo info) {
+ DumpTarget dt = new DumpTarget();
+ dt.type = DumpTarget.Type.ITEM;
+
+ switch (info.itemType) {
+ case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+ dt.itemType = ItemType.APP_ICON;
+ break;
+ case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+ dt.itemType = ItemType.UNKNOWN_ITEMTYPE;
+ break;
+ case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+ dt.itemType = ItemType.WIDGET;
+ break;
+ case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+ dt.itemType = ItemType.SHORTCUT;
+ break;
+ }
+ return dt;
+ }
+
+ public DumpTarget newContainerTarget(int type, int id) {
+ DumpTarget dt = new DumpTarget();
+ dt.type = DumpTarget.Type.CONTAINER;
+ dt.containerType = type;
+ dt.pageId = id;
+ return dt;
+ }
+
+ public static String getDumpTargetStr(DumpTarget t) {
+ if (t == null){
+ return "";
+ }
+ switch (t.type) {
+ case LauncherDumpProto.DumpTarget.Type.ITEM:
+ return getItemStr(t);
+ case LauncherDumpProto.DumpTarget.Type.CONTAINER:
+ String str = LoggerUtils.getFieldName(t.containerType, ContainerType.class);
+ if (t.containerType == ContainerType.WORKSPACE) {
+ str += " id=" + t.pageId;
+ } else if (t.containerType == ContainerType.FOLDER) {
+ str += " grid(" + t.gridX + "," + t.gridY+ ")";
+ }
+ return str;
+ default:
+ return "UNKNOWN TARGET TYPE";
+ }
+ }
+
+ private static String getItemStr(DumpTarget t) {
+ String typeStr = LoggerUtils.getFieldName(t.itemType, ItemType.class);
+ if (!TextUtils.isEmpty(t.packageName)) {
+ typeStr += ", package=" + t.packageName;
+ }
+ if (!TextUtils.isEmpty(t.component)) {
+ typeStr += ", component=" + t.component;
+ }
+ return typeStr + ", grid(" + t.gridX + "," + t.gridY + "), span(" + t.spanX + "," + t.spanY
+ + "), pageIdx=" + t.pageId + " user=" + t.userType;
+ }
+
+ public DumpTarget writeToDumpTarget(ItemInfo info) {
+ node.component = info.getTargetComponent() == null? "":
+ info.getTargetComponent().flattenToString();
+ node.packageName = info.getIntent() == null? "": info.getIntent().getPackage();
+ node.gridX = info.cellX;
+ node.gridY = info.cellY;
+ node.spanX = info.spanX;
+ node.spanY = info.spanY;
+ node.userType = (info.user.equals(Process.myUserHandle()))? UserType.DEFAULT : UserType.WORK;
+ return node;
+ }
+}
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index ffb41b7..4c83e9a 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -6,9 +6,8 @@
import android.util.Log;
import android.util.Pair;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import java.io.BufferedReader;
import java.io.File;
@@ -40,7 +39,7 @@
private static File sLogsDirectory = null;
public static void setDir(File logsDir) {
- if (ProviderConfig.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE) {
synchronized (DATE_FORMAT) {
// If the target directory changes, stop any active thread.
if (sHandler != null && !logsDir.equals(sLogsDirectory)) {
@@ -77,7 +76,7 @@
}
public static void print(String tag, String msg, Exception e) {
- if (!ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (!FeatureFlags.IS_DOGFOOD_BUILD) {
return;
}
String out = String.format("%s %s %s", DATE_FORMAT.format(new Date()), tag, msg);
@@ -103,7 +102,7 @@
* @param out if not null, all the persisted logs are copied to the writer.
*/
public static void flushAll(PrintWriter out) throws InterruptedException {
- if (!ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (!FeatureFlags.IS_DOGFOOD_BUILD) {
return;
}
CountDownLatch latch = new CountDownLatch(1);
@@ -136,7 +135,7 @@
@Override
public boolean handleMessage(Message msg) {
- if (sLogsDirectory == null || !ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (sLogsDirectory == null || !FeatureFlags.IS_DOGFOOD_BUILD) {
return true;
}
switch (msg.what) {
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index c13e8b3..499fdc7 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -1,5 +1,21 @@
+/*
+ * Copyright (C) 2016 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.logging;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.SparseArray;
import android.view.View;
@@ -27,7 +43,7 @@
private static final ArrayMap<Class, SparseArray<String>> sNameCache = new ArrayMap<>();
private static final String UNKNOWN = "UNKNOWN";
- private static String getFieldName(int value, Class c) {
+ public static String getFieldName(int value, Class c) {
SparseArray<String> cache;
synchronized (sNameCache) {
cache = sNameCache.get(c);
@@ -68,8 +84,13 @@
case Target.Type.CONTROL:
return getFieldName(t.controlType, ControlType.class);
case Target.Type.CONTAINER:
- return getFieldName(t.containerType, ContainerType.class)
- + " id=" + t.pageIndex;
+ String str = getFieldName(t.containerType, ContainerType.class);
+ if (t.containerType == ContainerType.WORKSPACE) {
+ str += " id=" + t.pageIndex;
+ } else if (t.containerType == ContainerType.FOLDER) {
+ str += " grid(" + t.gridX + "," + t.gridY+ ")";
+ }
+ return str;
default:
return "UNKNOWN TARGET TYPE";
}
@@ -86,10 +107,8 @@
if (t.intentHash != 0) {
typeStr += ", intentHash=" + t.intentHash;
}
- if (t.spanX != 0) {
- typeStr += ", spanX=" + t.spanX;
- }
- return typeStr + ", grid=(" + t.gridX + "," + t.gridY + "), id=" + t.pageIndex;
+ return typeStr + ", grid(" + t.gridX + "," + t.gridY + "), span(" + t.spanX + "," + t.spanY
+ + "), pageIdx=" + t.pageIndex;
}
public static Target newItemTarget(View v) {
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 90e4531..04ca247 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -29,8 +29,7 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.ProviderConfig;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
@@ -61,7 +60,7 @@
private static final String TAG = "UserEvent";
private static final boolean IS_VERBOSE =
- ProviderConfig.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(LogConfig.USEREVENT);
+ FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(LogConfig.USEREVENT);
private static UserEventDispatcher sInstance;
private static final Object LOCK = new Object();
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 9696054..edc64e5 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -33,6 +33,7 @@
import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.Provider;
@@ -140,7 +141,7 @@
* the workspace has been loaded. We identify a shortcut by its intent.
*/
protected boolean shortcutExists(BgDataModel dataModel, Intent intent, UserHandle user) {
- final String intentWithPkg, intentWithoutPkg;
+ final String compPkgName, intentWithPkg, intentWithoutPkg;
if (intent == null) {
// Skip items with null intents
return true;
@@ -148,19 +149,21 @@
if (intent.getComponent() != null) {
// If component is not null, an intent with null package will produce
// the same result and should also be a match.
- String packageName = intent.getComponent().getPackageName();
+ compPkgName = intent.getComponent().getPackageName();
if (intent.getPackage() != null) {
intentWithPkg = intent.toUri(0);
intentWithoutPkg = new Intent(intent).setPackage(null).toUri(0);
} else {
- intentWithPkg = new Intent(intent).setPackage(packageName).toUri(0);
+ intentWithPkg = new Intent(intent).setPackage(compPkgName).toUri(0);
intentWithoutPkg = intent.toUri(0);
}
} else {
+ compPkgName = null;
intentWithPkg = intent.toUri(0);
intentWithoutPkg = intent.toUri(0);
}
+ boolean isLauncherAppTarget = Utilities.isLauncherAppTarget(intent);
synchronized (dataModel) {
for (ItemInfo item : dataModel.itemsIdMap) {
if (item instanceof ShortcutInfo) {
@@ -172,6 +175,16 @@
if (intentWithPkg.equals(s) || intentWithoutPkg.equals(s)) {
return true;
}
+
+ // checking for existing promise icon with same package name
+ if (isLauncherAppTarget
+ && info.isPromise()
+ && info.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)
+ && info.getTargetComponent() != null
+ && compPkgName != null
+ && compPkgName.equals(info.getTargetComponent().getPackageName())) {
+ return true;
+ }
}
}
}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 6b64087..930c854 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -24,19 +24,25 @@
import com.android.launcher3.FolderInfo;
import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.logging.DumpTargetWrapper;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.model.nano.LauncherDumpProto;
+import com.android.launcher3.model.nano.LauncherDumpProto.ContainerType;
+import com.android.launcher3.model.nano.LauncherDumpProto.DumpTarget;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.MultiHashMap;
+import com.google.protobuf.nano.MessageNano;
import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -102,21 +108,31 @@
deepShortcutMap.clear();
}
- // TODO: current dump is very cryptic and hard to understand. Make it more legible.
- public synchronized void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
- writer.println(prefix + "Data Model:");
- for (int i = 0; i < workspaceScreens.size(); i++) {
- writer.println(prefix + "\tIndex of workspaceScreens:" + workspaceScreens.get(i).toString());
+ public synchronized void dump(String prefix, FileDescriptor fd, PrintWriter writer,
+ String[] args) {
+ if (args.length > 0 && TextUtils.equals(args[0], "--proto")) {
+ dumpProto(prefix, fd, writer, args);
+ return;
}
+ writer.println(prefix + "Data Model:");
+ writer.print(prefix + " ---- workspace screens: ");
+ for (int i = 0; i < workspaceScreens.size(); i++) {
+ writer.print(" " + workspaceScreens.get(i).toString());
+ }
+ writer.println();
+ writer.println(prefix + " ---- workspace items ");
for (int i = 0; i < workspaceItems.size(); i++) {
writer.println(prefix + '\t' + workspaceItems.get(i).toString());
}
+ writer.println(prefix + " ---- appwidget items ");
for (int i = 0; i < appWidgets.size(); i++) {
writer.println(prefix + '\t' + appWidgets.get(i).toString());
}
+ writer.println(prefix + " ---- folder items ");
for (int i = 0; i< folders.size(); i++) {
writer.println(prefix + '\t' + folders.valueAt(i).toString());
}
+ writer.println(prefix + " ---- items id map ");
for (int i = 0; i< itemsIdMap.size(); i++) {
writer.println(prefix + '\t' + itemsIdMap.valueAt(i).toString());
}
@@ -133,6 +149,88 @@
}
}
+ private synchronized void dumpProto(String prefix, FileDescriptor fd, PrintWriter writer,
+ String[] args) {
+
+ // Add top parent nodes. (L1)
+ DumpTargetWrapper hotseat = new DumpTargetWrapper(ContainerType.HOTSEAT, 0);
+ LongArrayMap<DumpTargetWrapper> workspaces = new LongArrayMap<>();
+ for (int i = 0; i < workspaceScreens.size(); i++) {
+ workspaces.put(new Long(workspaceScreens.get(i)),
+ new DumpTargetWrapper(ContainerType.WORKSPACE, i));
+ }
+ DumpTargetWrapper dtw;
+ // Add non leaf / non top nodes (L2)
+ for (int i = 0; i < folders.size(); i++) {
+ FolderInfo fInfo = folders.valueAt(i);
+ dtw = new DumpTargetWrapper(ContainerType.FOLDER, folders.size());
+ dtw.writeToDumpTarget(fInfo);
+ for(ShortcutInfo sInfo: fInfo.contents) {
+ DumpTargetWrapper child = new DumpTargetWrapper(sInfo);
+ child.writeToDumpTarget(sInfo);
+ dtw.add(child);
+ }
+ if (fInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+ hotseat.add(dtw);
+ } else if (fInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ workspaces.get(new Long(fInfo.screenId)).add(dtw);
+ }
+ }
+ // Add leaf nodes (L3): *Info
+ for (int i = 0; i < workspaceItems.size(); i++) {
+ ItemInfo info = workspaceItems.get(i);
+ if (info instanceof FolderInfo) {
+ continue;
+ }
+ dtw = new DumpTargetWrapper(info);
+ dtw.writeToDumpTarget(info);
+ if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+ hotseat.add(dtw);
+ } else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ workspaces.get(new Long(info.screenId)).add(dtw);
+ }
+ }
+ for (int i = 0; i < appWidgets.size(); i++) {
+ ItemInfo info = appWidgets.get(i);
+ dtw = new DumpTargetWrapper(info);
+ dtw.writeToDumpTarget(info);
+ if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+ hotseat.add(dtw);
+ } else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ workspaces.get(new Long(info.screenId)).add(dtw);
+ }
+ }
+
+
+ // Traverse target wrapper
+ ArrayList<DumpTarget> targetList = new ArrayList<>();
+ targetList.addAll(hotseat.getFlattenedList());
+ for (int i = 0; i < workspaces.size(); i++) {
+ targetList.addAll(workspaces.valueAt(i).getFlattenedList());
+ }
+
+ if (args.length > 1 && TextUtils.equals(args[1], "--debug")) {
+ for (int i = 0; i < targetList.size(); i++) {
+ writer.println(prefix + DumpTargetWrapper.getDumpTargetStr(targetList.get(i)));
+ }
+ return;
+ } else {
+ LauncherDumpProto.LauncherImpression proto = new LauncherDumpProto.LauncherImpression();
+ proto.targets = new DumpTarget[targetList.size()];
+ for (int i = 0; i < targetList.size(); i++) {
+ proto.targets[i] = targetList.get(i);
+ }
+ FileOutputStream fos = new FileOutputStream(fd);
+ try {
+
+ fos.write(MessageNano.toByteArray(proto));
+ Log.d(TAG, MessageNano.toByteArray(proto).length + "Bytes");
+ } catch (IOException e) {
+ Log.e(TAG, "Exception writing dumpsys --proto", e);
+ }
+ }
+ }
+
public synchronized void removeItem(Context context, ItemInfo... items) {
removeItem(context, Arrays.asList(items));
}
@@ -142,7 +240,7 @@
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
folders.remove(item.id);
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
for (ItemInfo info : itemsIdMap) {
if (info.container == item.id) {
// We are deleting a folder which still contains items that
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 4931dca..032ed78 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -34,7 +34,7 @@
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.LooperExecuter;
+import com.android.launcher3.util.LooperExecutor;
import java.util.ArrayList;
import java.util.Arrays;
@@ -55,7 +55,7 @@
public ModelWriter(Context context, BgDataModel dataModel, boolean hasVerticalHotseat) {
mContext = context;
mBgDataModel = dataModel;
- mWorkerExecutor = new LooperExecuter(LauncherModel.getWorkerLooper());
+ mWorkerExecutor = new LooperExecutor(LauncherModel.getWorkerLooper());
mHasVerticalHotseat = hasVerticalHotseat;
}
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 5d04325..76b90a8 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -18,15 +18,18 @@
import android.content.ComponentName;
import com.android.launcher3.AllAppsList;
+import com.android.launcher3.AppInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
+import java.util.ArrayList;
import java.util.HashSet;
/**
@@ -47,6 +50,44 @@
return;
}
+ synchronized (apps) {
+ PromiseAppInfo updated = null;
+ final ArrayList<AppInfo> removed = new ArrayList<>();
+ for (int i=0; i < apps.size(); i++) {
+ final AppInfo appInfo = apps.get(i);
+ final ComponentName tgtComp = appInfo.getTargetComponent();
+ if (tgtComp != null && tgtComp.getPackageName().equals(mInstallInfo.packageName)) {
+ if (appInfo instanceof PromiseAppInfo) {
+ final PromiseAppInfo promiseAppInfo = (PromiseAppInfo) appInfo;
+ if (mInstallInfo.state == PackageInstallerCompat.STATUS_INSTALLING) {
+ promiseAppInfo.level = mInstallInfo.progress;
+ updated = promiseAppInfo;
+ } else if (mInstallInfo.state == PackageInstallerCompat.STATUS_FAILED) {
+ apps.removePromiseApp(appInfo);
+ removed.add(appInfo);
+ }
+ }
+ }
+ }
+ if (updated != null) {
+ final PromiseAppInfo updatedPromiseApp = updated;
+ scheduleCallbackTask(new CallbackTask() {
+ @Override
+ public void execute(Callbacks callbacks) {
+ callbacks.bindPromiseAppProgressUpdated(updatedPromiseApp);
+ }
+ });
+ }
+ if (!removed.isEmpty()) {
+ scheduleCallbackTask(new CallbackTask() {
+ @Override
+ public void execute(Callbacks callbacks) {
+ callbacks.bindAppInfosRemoved(removed);
+ }
+ });
+ }
+ }
+
synchronized (dataModel) {
final HashSet<ItemInfo> updates = new HashSet<>();
for (ItemInfo info : dataModel.itemsIdMap) {
@@ -56,7 +97,6 @@
if (si.isPromise() && (cn != null)
&& mInstallInfo.packageName.equals(cn.getPackageName())) {
si.setInstallProgress(mInstallInfo.progress);
-
if (mInstallInfo.state == PackageInstallerCompat.STATUS_FAILED) {
// Mark this info as broken.
si.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index ee7186a..87b63ff 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -18,9 +18,8 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
+import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
@@ -31,7 +30,6 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings;
@@ -40,10 +38,12 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.ManagedProfileHeuristic;
+import com.android.launcher3.util.PackageManagerHelper;
import java.util.ArrayList;
import java.util.Arrays;
@@ -94,6 +94,9 @@
for (int i = 0; i < N; i++) {
if (DEBUG) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
iconCache.updateIconsForPkg(packages[i], mUser);
+ if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
+ appsList.removePackage(packages[i], Process.myUserHandle());
+ }
appsList.addPackage(context, packages[i], mUser);
}
@@ -224,15 +227,12 @@
if (si.isPromise() && mOp == OP_ADD) {
if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
// Auto install icon
- PackageManager pm = context.getPackageManager();
- ResolveInfo matched = pm.resolveActivity(
- new Intent(Intent.ACTION_MAIN)
- .setComponent(cn).addCategory(Intent.CATEGORY_LAUNCHER),
- PackageManager.MATCH_DEFAULT_ONLY);
- if (matched == null) {
+ LauncherAppsCompat launcherApps
+ = LauncherAppsCompat.getInstance(context);
+ if (!launcherApps.isActivityEnabledForProfile(cn, mUser)) {
// Try to find the best match activity.
- Intent intent = pm.getLaunchIntentForPackage(
- cn.getPackageName());
+ Intent intent = new PackageManagerHelper(context)
+ .getAppLaunchIntent(cn.getPackageName(), mUser);
if (intent != null) {
cn = intent.getComponent();
appInfo = addedOrUpdatedApps.get(cn);
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 95c54f7..d2adbde 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -3,9 +3,7 @@
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
@@ -19,8 +17,7 @@
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.ShortcutConfigActivityInfo;
-import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.Preconditions;
@@ -79,7 +76,7 @@
}
setWidgetsAndShortcuts(widgetsAndShortcuts, context);
} catch (Exception e) {
- if (!ProviderConfig.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(e)) {
+ if (!FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(e)) {
// the returned value may be incomplete and will not be refreshed until the next
// time Launcher starts.
// TODO: after figuring out a repro step, introduce a dirty bit to check when
diff --git a/src/com/android/launcher3/pageindicators/CaretDrawable.java b/src/com/android/launcher3/pageindicators/CaretDrawable.java
index 0a00e24..32e57a9 100644
--- a/src/com/android/launcher3/pageindicators/CaretDrawable.java
+++ b/src/com/android/launcher3/pageindicators/CaretDrawable.java
@@ -46,7 +46,7 @@
final int strokeWidth = res.getDimensionPixelSize(R.dimen.all_apps_caret_stroke_width);
final int shadowSpread = res.getDimensionPixelSize(R.dimen.all_apps_caret_shadow_spread);
- mCaretPaint.setColor(res.getColor(R.color.workspace_icon_text_color));
+ mCaretPaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
mCaretPaint.setAntiAlias(true);
mCaretPaint.setStrokeWidth(strokeWidth);
mCaretPaint.setStyle(Paint.Style.STROKE);
diff --git a/src/com/android/launcher3/popup/PopupItemView.java b/src/com/android/launcher3/popup/PopupItemView.java
index b839d99..eeece88 100644
--- a/src/com/android/launcher3/popup/PopupItemView.java
+++ b/src/com/android/launcher3/popup/PopupItemView.java
@@ -36,7 +36,7 @@
import com.android.launcher3.LogAccelerateInterpolator;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.util.PillRevealOutlineProvider;
+import com.android.launcher3.anim.PillRevealOutlineProvider;
/**
* An abstract {@link FrameLayout} that supports animating an item's content
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
index b0482f8..3e4cd01 100644
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -37,6 +37,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherSettings.Settings;
@@ -46,7 +47,6 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.GridSizeMigrationTask;
import com.android.launcher3.util.LongArrayMap;
@@ -112,7 +112,7 @@
screenOps.add(ContentProviderOperation.newInsert(
LauncherSettings.WorkspaceScreens.CONTENT_URI).withValues(v).build());
}
- mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY, screenOps);
+ mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, screenOps);
importWorkspaceItems(allScreens.get(0), screenIdMap);
GridSizeMigrationTask.markForMigration(mContext, mMaxGridSizeX, mMaxGridSizeY, mHotseatSize);
@@ -289,7 +289,7 @@
}
if (insertOperations.size() >= BATCH_INSERT_SIZE) {
- mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
+ mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
insertOperations);
insertOperations.clear();
}
@@ -300,7 +300,7 @@
throw new Exception("Insufficient data");
}
if (!insertOperations.isEmpty()) {
- mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
+ mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
insertOperations);
insertOperations.clear();
}
@@ -319,7 +319,7 @@
mHotseatSize = (int) hotseatItems.keyAt(hotseatItems.size() - 1) + 1;
if (!insertOperations.isEmpty()) {
- mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
+ mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
insertOperations);
}
}
diff --git a/src/com/android/launcher3/util/LooperExecuter.java b/src/com/android/launcher3/util/LooperExecutor.java
similarity index 94%
rename from src/com/android/launcher3/util/LooperExecuter.java
rename to src/com/android/launcher3/util/LooperExecutor.java
index 4db999b..5b7c20b 100644
--- a/src/com/android/launcher3/util/LooperExecuter.java
+++ b/src/com/android/launcher3/util/LooperExecutor.java
@@ -25,11 +25,11 @@
/**
* Extension of {@link AbstractExecutorService} which executed on a provided looper.
*/
-public class LooperExecuter extends AbstractExecutorService {
+public class LooperExecutor extends AbstractExecutorService {
private final Handler mHandler;
- public LooperExecuter(Looper looper) {
+ public LooperExecutor(Looper looper) {
mHandler = new Handler(looper);
}
diff --git a/src/com/android/launcher3/util/LooperIdleLock.java b/src/com/android/launcher3/util/LooperIdleLock.java
new file mode 100644
index 0000000..35cac14
--- /dev/null
+++ b/src/com/android/launcher3/util/LooperIdleLock.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 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.util;
+
+import android.os.Looper;
+import android.os.MessageQueue;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * Utility class to block execution until the UI looper is idle.
+ */
+public class LooperIdleLock implements MessageQueue.IdleHandler, Runnable {
+
+ private final Object mLock;
+
+ private boolean mIsLocked;
+
+ public LooperIdleLock(Object lock, Looper looper) {
+ mLock = lock;
+ mIsLocked = true;
+ if (Utilities.ATLEAST_MARSHMALLOW) {
+ looper.getQueue().addIdleHandler(this);
+ } else {
+ // Looper.myQueue() only gives the current queue. Move the execution to the UI thread
+ // so that the IdleHandler is attached to the correct message queue.
+ new LooperExecutor(looper).execute(this);
+ }
+ }
+
+ @Override
+ public void run() {
+ Looper.myQueue().addIdleHandler(this);
+ }
+
+ @Override
+ public boolean queueIdle() {
+ synchronized (mLock) {
+ mIsLocked = false;
+ mLock.notify();
+ }
+ return false;
+ }
+
+ public boolean awaitLocked(long ms) {
+ if (mIsLocked) {
+ try {
+ // Just in case mFlushingWorkerThread changes but we aren't woken up,
+ // wait no longer than 1sec at a time
+ mLock.wait(ms);
+ } catch (InterruptedException ex) {
+ // Ignore
+ }
+ }
+ return mIsLocked;
+ }
+}
diff --git a/src/com/android/launcher3/util/Preconditions.java b/src/com/android/launcher3/util/Preconditions.java
index 89353e1..7ab0d31 100644
--- a/src/com/android/launcher3/util/Preconditions.java
+++ b/src/com/android/launcher3/util/Preconditions.java
@@ -19,7 +19,7 @@
import android.os.Looper;
import com.android.launcher3.LauncherModel;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
/**
* A set of utility methods for thread verification.
@@ -27,25 +27,25 @@
public class Preconditions {
public static void assertNotNull(Object o) {
- if (ProviderConfig.IS_DOGFOOD_BUILD && o == null) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && o == null) {
throw new IllegalStateException();
}
}
public static void assertWorkerThread() {
- if (ProviderConfig.IS_DOGFOOD_BUILD && !isSameLooper(LauncherModel.getWorkerLooper())) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(LauncherModel.getWorkerLooper())) {
throw new IllegalStateException();
}
}
public static void assertUIThread() {
- if (ProviderConfig.IS_DOGFOOD_BUILD && !isSameLooper(Looper.getMainLooper())) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(Looper.getMainLooper())) {
throw new IllegalStateException();
}
}
public static void assertNonUiThread() {
- if (ProviderConfig.IS_DOGFOOD_BUILD && isSameLooper(Looper.getMainLooper())) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && isSameLooper(Looper.getMainLooper())) {
throw new IllegalStateException();
}
}
diff --git a/src/com/android/launcher3/util/SQLiteCacheHelper.java b/src/com/android/launcher3/util/SQLiteCacheHelper.java
index 1ff6293..5344416 100644
--- a/src/com/android/launcher3/util/SQLiteCacheHelper.java
+++ b/src/com/android/launcher3/util/SQLiteCacheHelper.java
@@ -10,7 +10,7 @@
import android.util.Log;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
/**
* An extension of {@link SQLiteOpenHelper} with utility methods for a single table cache DB.
@@ -19,7 +19,7 @@
public abstract class SQLiteCacheHelper {
private static final String TAG = "SQLiteCacheHelper";
- private static final boolean NO_ICON_CACHE = ProviderConfig.IS_DOGFOOD_BUILD &&
+ private static final boolean NO_ICON_CACHE = FeatureFlags.IS_DOGFOOD_BUILD &&
Utilities.isPropertyEnabled(LogConfig.MEMORY_ONLY_ICON_CACHE);
private final String mTableName;
diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
index 9bd2882..4cb6ca8 100644
--- a/src/com/android/launcher3/util/ViewOnDrawExecutor.java
+++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
@@ -16,12 +16,10 @@
package com.android.launcher3.util;
-import android.util.Log;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewTreeObserver.OnDrawListener;
-import com.android.launcher3.DeferredHandler;
import com.android.launcher3.Launcher;
import java.util.ArrayList;
@@ -34,7 +32,7 @@
OnAttachStateChangeListener {
private final ArrayList<Runnable> mTasks = new ArrayList<>();
- private final DeferredHandler mHandler;
+ private final Executor mExecutor;
private Launcher mLauncher;
private View mAttachedView;
@@ -43,8 +41,8 @@
private boolean mLoadAnimationCompleted;
private boolean mFirstDrawCompleted;
- public ViewOnDrawExecutor(DeferredHandler handler) {
- mHandler = handler;
+ public ViewOnDrawExecutor(Executor executor) {
+ mExecutor = executor;
}
public void attachTo(Launcher launcher) {
@@ -92,7 +90,7 @@
// Post the pending tasks after both onDraw and onLoadAnimationCompleted have been called.
if (mLoadAnimationCompleted && mFirstDrawCompleted && !mCompleted) {
for (final Runnable r : mTasks) {
- mHandler.post(r);
+ mExecutor.execute(r);
}
markCompleted();
}
diff --git a/src_config/com/android/launcher3/config/ProviderConfig.java b/src_config/com/android/launcher3/BuildConfig.java
similarity index 67%
rename from src_config/com/android/launcher3/config/ProviderConfig.java
rename to src_config/com/android/launcher3/BuildConfig.java
index 491fa65..4df75a1 100644
--- a/src_config/com/android/launcher3/config/ProviderConfig.java
+++ b/src_config/com/android/launcher3/BuildConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.launcher3.config;
+package com.android.launcher3;
-public class ProviderConfig {
-
- public static final String AUTHORITY = "com.android.launcher3.settings".intern();
-
- public static final boolean IS_DOGFOOD_BUILD = true;
+/**
+ * Config file used by Make. This file is automatically generated when using gradle.
+ */
+public class BuildConfig {
+ public static final String APPLICATION_ID = "com.android.launcher3";
}
diff --git a/src_config/com/android/launcher3/config/FeatureFlags.java b/src_flags/com/android/launcher3/config/FeatureFlags.java
similarity index 90%
rename from src_config/com/android/launcher3/config/FeatureFlags.java
rename to src_flags/com/android/launcher3/config/FeatureFlags.java
index 80eebec..4b3e2e4 100644
--- a/src_config/com/android/launcher3/config/FeatureFlags.java
+++ b/src_flags/com/android/launcher3/config/FeatureFlags.java
@@ -20,6 +20,9 @@
* Defines a set of flags used to control various launcher behaviors
*/
public final class FeatureFlags {
+
+ public static final boolean IS_DOGFOOD_BUILD = true;
+
private FeatureFlags() {}
// Custom flags go below this
@@ -28,12 +31,13 @@
public static boolean LAUNCHER3_USE_SYSTEM_DRAG_DRIVER = true;
public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false;
public static boolean LAUNCHER3_ALL_APPS_PULL_UP = true;
- public static boolean LAUNCHER3_NEW_FOLDER_ANIMATION = false;
+ public static boolean LAUNCHER3_NEW_FOLDER_ANIMATION = true;
// When enabled allows to use any point on the fast scrollbar to start dragging.
public static boolean LAUNCHER3_DIRECT_SCROLL = true;
// When enabled while all-apps open, the soft input will be set to adjust resize .
public static boolean LAUNCHER3_UPDATE_SOFT_INPUT_MODE = false;
-
+ // When enabled the promise icon is visible in all apps while installation an app.
+ public static boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = true;
// Feature flag to enable moving the QSB on the 0th screen of the workspace.
public static final boolean QSB_ON_FIRST_SCREEN = true;
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
index d0ba907..883be5a 100644
--- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
+++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
@@ -10,9 +10,9 @@
import android.util.Pair;
import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.Provider;
@@ -178,6 +178,6 @@
v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
}
- getMockContentResolver().applyBatch(ProviderConfig.AUTHORITY, ops);
+ getMockContentResolver().applyBatch(LauncherProvider.AUTHORITY, ops);
}
}
diff --git a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
index b9944db..13e0986 100644
--- a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -16,7 +16,6 @@
import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppFilter;
import com.android.launcher3.AppInfo;
-import com.android.launcher3.DeferredHandler;
import com.android.launcher3.IconCache;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
@@ -24,7 +23,7 @@
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.BaseModelUpdateTask;
import com.android.launcher3.LauncherModel.Callbacks;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.LauncherProvider;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.Provider;
import com.android.launcher3.util.TestLauncherProvider;
@@ -36,6 +35,7 @@
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
+import java.util.concurrent.Executor;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Mockito.atLeast;
@@ -64,7 +64,7 @@
public Callbacks callbacks;
public BaseModelUpdateTaskTestCase() {
- super(TestLauncherProvider.class, ProviderConfig.AUTHORITY);
+ super(TestLauncherProvider.class, LauncherProvider.AUTHORITY);
}
@Override
@@ -102,14 +102,14 @@
f.setAccessible(true);
f.set(task, mockModel);
- DeferredHandler mockHandler = mock(DeferredHandler.class);
- f = BaseModelUpdateTask.class.getDeclaredField("mUiHandler");
+ Executor mockExecutor = mock(Executor.class);
+ f = BaseModelUpdateTask.class.getDeclaredField("mUiExecutor");
f.setAccessible(true);
- f.set(task, mockHandler);
+ f.set(task, mockExecutor);
task.execute(appState, bgDataModel, allAppsList);
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
- verify(mockHandler, atLeast(0)).post(captor.capture());
+ verify(mockExecutor, atLeast(0)).execute(captor.capture());
return captor.getAllValues();
}
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
index fc7fe48..fd62d36 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -1,7 +1,6 @@
package com.android.launcher3.model;
import android.content.ContentValues;
-import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Point;
@@ -10,9 +9,9 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.model.GridSizeMigrationTask.MultiStepMigrationTask;
import com.android.launcher3.util.TestLauncherProvider;
@@ -40,7 +39,7 @@
private InvariantDeviceProfile mIdp;
public GridSizeMigrationTaskTest() {
- super(TestLauncherProvider.class, ProviderConfig.AUTHORITY);
+ super(TestLauncherProvider.class, LauncherProvider.AUTHORITY);
}
@Override
diff --git a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
index d655562..ed893c4 100644
--- a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -21,9 +21,8 @@
}
private PackageInstallStateChangedTask newTask(String pkg, int progress) {
- PackageInstallInfo installInfo = new PackageInstallInfo(pkg);
- installInfo.progress = progress;
- installInfo.state = PackageInstallerCompat.STATUS_INSTALLING;
+ int state = PackageInstallerCompat.STATUS_INSTALLING;
+ PackageInstallInfo installInfo = new PackageInstallInfo(pkg, state, progress);
return new PackageInstallStateChangedTask(installInfo);
}
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index df2b662..97f7b50 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -39,13 +39,12 @@
import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.ui.LauncherInstrumentationTestCase;
import com.android.launcher3.util.ContentWriter;
-import com.android.launcher3.util.LooperExecuter;
+import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetHostViewLoader;
import java.util.Set;
import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
@@ -340,7 +339,7 @@
* Blocks the current thread until all the jobs in the main worker thread are complete.
*/
private void waitUntilLoaderIdle() throws Exception {
- new LooperExecuter(LauncherModel.getWorkerLooper())
+ new LooperExecutor(LauncherModel.getWorkerLooper())
.submit(new Runnable() {
@Override
public void run() { }