Merge "Replacing overview panel icons with vector drawables" into ub-launcher3-calgary
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index eb7ea0c..3a3a28d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -49,7 +49,16 @@
<uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" />
<uses-permission android:name="com.android.launcher3.permission.WRITE_SETTINGS" />
- <application>
+ <application
+ android:backupAgent="com.android.launcher3.LauncherBackupAgent"
+ android:fullBackupOnly="true"
+ android:fullBackupContent="@xml/backupscheme"
+ android:hardwareAccelerated="true"
+ android:icon="@mipmap/ic_launcher_home"
+ android:label="@string/app_name"
+ android:largeHeap="@bool/config_largeHeap"
+ android:restoreAnyVersion="true"
+ android:supportsRtl="true" >
<!--
Main launcher activity. When extending only change the name, and keep all the
diff --git a/res/drawable/focusable_view_bg.xml b/res/drawable/focusable_view_bg.xml
deleted file mode 100644
index e156513..0000000
--- a/res/drawable/focusable_view_bg.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item android:state_focused="true">
- <shape android:shape="rectangle">
- <solid android:color="@color/focused_background" />
- </shape>
- </item>
-
-</selector>
\ No newline at end of file
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 3a361e2..d193e2f 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -30,11 +30,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <com.android.launcher3.FocusIndicatorView
- android:id="@+id/focus_indicator"
- android:layout_width="52dp"
- android:layout_height="52dp" />
-
<!-- The workspace contains 5 screens of cells -->
<!-- DO NOT CHANGE THE ID -->
<com.android.launcher3.Workspace
@@ -51,12 +46,8 @@
android:layout_gravity="right" />
<include
- android:id="@+id/app_info_drop_target_bar"
- layout="@layout/drop_target_bar_vert_info" />
-
- <include
- android:id="@+id/search_drop_target_bar"
- layout="@layout/drop_target_bar_vert_search" />
+ android:id="@+id/drop_target_bar"
+ layout="@layout/drop_target_bar_vert" />
<include layout="@layout/overview_panel"
android:id="@+id/overview_panel"
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index 4576e4d..527ed54 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -31,11 +31,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <com.android.launcher3.FocusIndicatorView
- android:id="@+id/focus_indicator"
- android:layout_width="52dp"
- android:layout_height="52dp" />
-
<!-- The workspace contains 5 screens of cells -->
<!-- DO NOT CHANGE THE ID -->
<com.android.launcher3.Workspace
@@ -60,15 +55,11 @@
<com.android.launcher3.pageindicators.PageIndicatorLine
android:id="@+id/page_indicator"
android:layout_width="match_parent"
- android:layout_height="1dp" />
+ android:layout_height="@dimen/dynamic_grid_page_indicator_height" />
<include
- android:id="@+id/app_info_drop_target_bar"
- layout="@layout/drop_target_bar_horz_info" />
-
- <include
- android:id="@+id/search_drop_target_bar"
- layout="@layout/drop_target_bar_horz_search" />
+ android:id="@+id/drop_target_bar"
+ layout="@layout/drop_target_bar_horz" />
<include layout="@layout/widgets_view"
android:id="@+id/widgets_view"
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 0f755d8..184e688 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -30,11 +30,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <com.android.launcher3.FocusIndicatorView
- android:id="@+id/focus_indicator"
- android:layout_width="52dp"
- android:layout_height="52dp" />
-
<!-- The workspace contains 5 screens of cells -->
<!-- DO NOT CHANGE THE ID -->
<com.android.launcher3.Workspace
@@ -51,12 +46,8 @@
android:layout_height="match_parent" />
<include
- android:id="@+id/app_info_drop_target_bar"
- layout="@layout/drop_target_bar_horz_info" />
-
- <include
- android:id="@+id/search_drop_target_bar"
- layout="@layout/drop_target_bar_horz_search" />
+ android:id="@+id/drop_target_bar"
+ layout="@layout/drop_target_bar_horz" />
<include layout="@layout/overview_panel"
android:id="@+id/overview_panel"
@@ -67,7 +58,7 @@
<com.android.launcher3.pageindicators.PageIndicatorLine
android:id="@+id/page_indicator"
android:layout_width="match_parent"
- android:layout_height="1dp" />
+ android:layout_height="@dimen/dynamic_grid_page_indicator_height" />
<include layout="@layout/widgets_view"
android:id="@+id/widgets_view"
diff --git a/res/layout/all_apps_icon.xml b/res/layout/all_apps_icon.xml
index bb95c5f..3836fed 100644
--- a/res/layout/all_apps_icon.xml
+++ b/res/layout/all_apps_icon.xml
@@ -24,6 +24,5 @@
android:paddingTop="@dimen/all_apps_icon_top_bottom_padding"
android:paddingBottom="@dimen/all_apps_icon_top_bottom_padding"
android:focusable="true"
- android:background="@drawable/focusable_view_bg"
launcher:iconDisplay="all_apps" />
diff --git a/res/layout/all_apps_prediction_bar_icon.xml b/res/layout/all_apps_prediction_bar_icon.xml
index f15aeaf..295b0b7 100644
--- a/res/layout/all_apps_prediction_bar_icon.xml
+++ b/res/layout/all_apps_prediction_bar_icon.xml
@@ -24,6 +24,5 @@
android:paddingTop="@dimen/all_apps_prediction_icon_top_padding"
android:paddingBottom="@dimen/all_apps_prediction_icon_bottom_padding"
android:focusable="true"
- android:background="@drawable/focusable_view_bg"
launcher:iconDisplay="all_apps" />
diff --git a/res/layout/all_apps_search_market.xml b/res/layout/all_apps_search_market.xml
index ef5e76c..4bdca99 100644
--- a/res/layout/all_apps_search_market.xml
+++ b/res/layout/all_apps_search_market.xml
@@ -24,6 +24,7 @@
android:fontFamily="sans-serif-medium"
android:textSize="14sp"
android:textColor="@color/launcher_accent_color"
+ android:text="@string/all_apps_search_market_message"
android:textAllCaps="true"
android:focusable="true"
android:background="?android:selectableItemBackground" />
diff --git a/res/layout/drop_target_bar_horz.xml b/res/layout/drop_target_bar_horz.xml
new file mode 100644
index 0000000..ee22d1e
--- /dev/null
+++ b/res/layout/drop_target_bar_horz.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.DropTargetBar
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dynamic_grid_drop_target_size"
+ android:layout_gravity="center_horizontal|top"
+ android:focusable="false">
+
+ <FrameLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1" >
+
+ <!-- Delete target -->
+
+ <com.android.launcher3.DeleteDropTarget
+ launcher:hideParentOnDisable="true"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:id="@+id/delete_target_text"
+ style="@style/DropTargetButton"
+ android:text="@string/remove_drop_target_label" />
+ </FrameLayout>
+
+ <FrameLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1" >
+
+ <!-- App Info -->
+
+ <com.android.launcher3.InfoDropTarget
+ launcher:hideParentOnDisable="true"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:id="@+id/info_target_text"
+ style="@style/DropTargetButton"
+ android:text="@string/app_info_drop_target_label" />
+ </FrameLayout>
+
+ <FrameLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1" >
+
+ <!-- Uninstall target -->
+
+ <com.android.launcher3.UninstallDropTarget
+ launcher:hideParentOnDisable="true"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:id="@+id/uninstall_target_text"
+ style="@style/DropTargetButton"
+ android:text="@string/uninstall_drop_target_label" />
+ </FrameLayout>
+
+</com.android.launcher3.DropTargetBar>
\ No newline at end of file
diff --git a/res/layout/drop_target_bar_horz_info.xml b/res/layout/drop_target_bar_horz_info.xml
deleted file mode 100644
index 92a9b22..0000000
--- a/res/layout/drop_target_bar_horz_info.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-<com.android.launcher3.AppInfoDropTargetBar xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="48dp"
- android:layout_gravity="center_horizontal|bottom"
- android:focusable="false" >
-
- <FrameLayout
- android:id="@+id/drag_target_bar"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <!-- Info target -->
-
- <com.android.launcher3.InfoDropTarget
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:gravity="center"
- android:id="@+id/info_target_text"
- style="@style/DropTargetButton"
- android:text="@string/app_info_drop_target_label" />
- </FrameLayout>
-</com.android.launcher3.AppInfoDropTargetBar>
\ No newline at end of file
diff --git a/res/layout/drop_target_bar_horz_search.xml b/res/layout/drop_target_bar_horz_search.xml
deleted file mode 100644
index 7997801..0000000
--- a/res/layout/drop_target_bar_horz_search.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.launcher3.SearchDropTargetBar xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center_horizontal|top"
- android:focusable="false">
-
- <!-- Drag specific targets container -->
-
- <LinearLayout
- android:id="@+id/drag_target_bar"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center" >
-
- <FrameLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1" >
-
- <!-- Delete target -->
-
- <com.android.launcher3.DeleteDropTarget
- launcher:hideParentOnDisable="true"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:gravity="center"
- android:id="@+id/delete_target_text"
- style="@style/DropTargetButton"
- android:text="@string/remove_drop_target_label" />
- </FrameLayout>
-
- <FrameLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1" >
-
- <!-- Uninstall target -->
-
- <com.android.launcher3.UninstallDropTarget
- launcher:hideParentOnDisable="true"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:gravity="center"
- android:id="@+id/uninstall_target_text"
- style="@style/DropTargetButton"
- android:text="@string/uninstall_drop_target_label" />
- </FrameLayout>
- </LinearLayout>
-
-</com.android.launcher3.SearchDropTargetBar>
\ No newline at end of file
diff --git a/res/layout/drop_target_bar_vert.xml b/res/layout/drop_target_bar_vert.xml
new file mode 100644
index 0000000..10b1d7c
--- /dev/null
+++ b/res/layout/drop_target_bar_vert.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<com.android.launcher3.DropTargetBar
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/dynamic_grid_drop_target_size"
+ android:orientation="vertical"
+ android:layout_height="match_parent"
+ android:layout_gravity="left"
+ android:focusable="false"
+ android:paddingTop="@dimen/vert_drop_target_vertical_gap" >
+
+ <!-- Delete target -->
+ <com.android.launcher3.DeleteDropTarget
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dynamic_grid_drop_target_size"
+ android:gravity="center"
+ android:paddingLeft="@dimen/vert_drop_target_horizontal_gap"
+ android:paddingRight="@dimen/vert_drop_target_horizontal_gap"
+ android:id="@+id/delete_target_text"
+ android:textColor="@android:color/white" />
+
+ <!-- Uninstall target -->
+ <com.android.launcher3.UninstallDropTarget
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dynamic_grid_drop_target_size"
+ android:gravity="center"
+ android:paddingLeft="@dimen/vert_drop_target_horizontal_gap"
+ android:paddingRight="@dimen/vert_drop_target_horizontal_gap"
+ android:id="@+id/uninstall_target_text"
+ android:textColor="@android:color/white"
+ android:layout_marginTop="@dimen/vert_drop_target_vertical_gap"/>
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <!-- App Info -->
+ <com.android.launcher3.InfoDropTarget
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dynamic_grid_drop_target_size"
+ android:gravity="center"
+ android:paddingLeft="@dimen/vert_drop_target_horizontal_gap"
+ android:paddingRight="@dimen/vert_drop_target_horizontal_gap"
+ android:id="@+id/info_target_text"
+ android:textColor="@android:color/white"
+ android:layout_marginBottom="64dp"/>
+
+</com.android.launcher3.DropTargetBar>
\ No newline at end of file
diff --git a/res/layout/drop_target_bar_vert_info.xml b/res/layout/drop_target_bar_vert_info.xml
deleted file mode 100644
index da33d1a..0000000
--- a/res/layout/drop_target_bar_vert_info.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-<com.android.launcher3.AppInfoDropTargetBar xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="48dp"
- android:layout_height="match_parent"
- android:focusable="false" >
-
- <FrameLayout
- android:id="@+id/drag_target_bar"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <!-- Info target -->
- <com.android.launcher3.InfoDropTarget
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_gravity="center_horizontal|bottom"
- android:gravity="center"
- android:paddingLeft="14dp"
- android:paddingRight="14dp"
- android:textColor="@android:color/white"
- android:id="@+id/info_target_text" />
- </FrameLayout>
-
-</com.android.launcher3.AppInfoDropTargetBar>
\ No newline at end of file
diff --git a/res/layout/drop_target_bar_vert_search.xml b/res/layout/drop_target_bar_vert_search.xml
deleted file mode 100644
index d5e41df..0000000
--- a/res/layout/drop_target_bar_vert_search.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.launcher3.SearchDropTargetBar xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="48dp"
- android:layout_height="match_parent"
- android:focusable="false">
-
- <!-- Drag specific targets container -->
-
- <LinearLayout
- android:id="@+id/drag_target_bar"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:layout_gravity="center"
- android:paddingTop="20dp">
-
- <!-- Delete target -->
- <com.android.launcher3.DeleteDropTarget
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:gravity="center"
- android:paddingLeft="14dp"
- android:paddingRight="14dp"
- android:id="@+id/delete_target_text"
- android:textColor="@android:color/white"
- android:layout_marginBottom="10dp" />
-
- <!-- Uninstall target -->
- <com.android.launcher3.UninstallDropTarget
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_gravity="center"
- android:gravity="center"
- android:paddingLeft="14dp"
- android:paddingRight="14dp"
- android:id="@+id/uninstall_target_text"
- android:textColor="@android:color/white"
- android:layout_marginTop="10dp"/>
- </LinearLayout>
-
-</com.android.launcher3.SearchDropTargetBar>
\ No newline at end of file
diff --git a/res/layout/qsb_container.xml b/res/layout/qsb_container.xml
new file mode 100644
index 0000000..55c7390
--- /dev/null
+++ b/res/layout/qsb_container.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<com.android.launcher3.QsbContainerView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/qsb_container"
+ android:padding="0dp" >
+
+ <fragment
+ android:name="com.android.launcher3.QsbContainerView$QsbFragment"
+ android:layout_width="match_parent"
+ android:tag="qsb_view"
+ android:layout_height="match_parent"/>
+</com.android.launcher3.QsbContainerView>
\ No newline at end of file
diff --git a/res/layout/qsb_default_view.xml b/res/layout/qsb_default_view.xml
new file mode 100644
index 0000000..82bdea5
--- /dev/null
+++ b/res/layout/qsb_default_view.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:layout_margin="16dp"
+ android:layout_gravity="center_vertical"
+ android:background="@drawable/quantum_panel_shape"
+ android:elevation="2dp"
+ android:orientation="horizontal">
+
+ <TextView
+ android:layout_width="0dp"
+ android:id="@+id/btn_qsb_search"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center_vertical"
+ android:paddingStart="16dp"
+ android:text="@string/abandoned_search"
+ android:textColor="@color/quantum_panel_text_color"
+ android:textAppearance="?android:textAppearanceMedium"
+ android:clickable="true"
+ android:background="?android:attr/selectableItemBackground" />
+ <ImageView
+ android:layout_width="48dp"
+ android:id="@+id/btn_qsb_setup"
+ android:clickable="true"
+ android:visibility="gone"
+ android:layout_height="match_parent"
+ android:src="@drawable/ic_setting"
+ android:tint="@color/quantum_panel_text_color"
+ android:contentDescription="@string/gadget_setup_text"
+ android:padding="8dp"
+ android:background="?android:attr/selectableItemBackground" />
+ </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml
index 8a1b7d0..d950750 100644
--- a/res/layout/user_folder.xml
+++ b/res/layout/user_folder.xml
@@ -22,27 +22,14 @@
android:elevation="5dp"
android:orientation="vertical" >
- <FrameLayout
- android:id="@+id/folder_content_wrapper"
+ <com.android.launcher3.folder.FolderPagedView
+ android:id="@+id/folder_content"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <!-- Actual size of the indicator doesn't matter as it is scaled to match the view size -->
-
- <com.android.launcher3.FocusIndicatorView
- android:id="@+id/focus_indicator"
- android:layout_width="20dp"
- android:layout_height="20dp" />
-
- <com.android.launcher3.folder.FolderPagedView
- android:id="@+id/folder_content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingLeft="4dp"
- android:paddingRight="4dp"
- android:paddingTop="8dp"
- launcher:pageIndicator="@+id/folder_page_indicator" />
- </FrameLayout>
+ android:layout_height="match_parent"
+ android:paddingLeft="4dp"
+ android:paddingRight="4dp"
+ android:paddingTop="8dp"
+ launcher:pageIndicator="@+id/folder_page_indicator" />
<LinearLayout
android:id="@+id/folder_footer"
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index de1316e..bb6bd76 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -22,27 +22,14 @@
android:elevation="5dp"
android:orientation="vertical" >
- <FrameLayout
- android:id="@+id/folder_content_wrapper"
+ <com.android.launcher3.folder.FolderPagedView
+ android:id="@+id/folder_content"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <!-- Actual size of the indicator doesn't matter as it is scaled to match the view size -->
-
- <com.android.launcher3.FocusIndicatorView
- android:id="@+id/focus_indicator"
- android:layout_width="20dp"
- android:layout_height="20dp" />
-
- <com.android.launcher3.folder.FolderPagedView
- android:id="@+id/folder_content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:paddingTop="8dp"
- launcher:pageIndicator="@+id/folder_page_indicator" />
- </FrameLayout>
+ android:layout_height="match_parent"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:paddingTop="8dp"
+ launcher:pageIndicator="@+id/folder_page_indicator" />
<LinearLayout
android:id="@+id/folder_footer"
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index ed5ad77..e25fd4e 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Deursoek programme …"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Laai tans programme …"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Geen programme gevind wat met \"<xliff:g id="QUERY">%1$s</xliff:g>\" ooreenstem nie"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Gaan na <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Niks meer spasie op die tuisskerm nie."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Geen plek meer in die Gunstelinge-laai nie"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Programme"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index c8070f0..40b1694 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"መተግበሪያዎችን ይፈልጉ…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"ወደ <xliff:g id="QUERY">%1$s</xliff:g> ሂድ"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"በዚህ መነሻ ማያ ገጽ ላይ ምንም ቦታ የለም።"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"በተወዳጆች መሣቢያ ውስጥ ተጨማሪ ቦታ የለም"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"መተግበሪያዎች"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index e059d26..f555ee1 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"البحث في التطبيقات…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"الانتقال إلى <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"ليس هناك مساحة أخرى في هذه الشاشة الرئيسية."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"لا يوجد المزيد من الحقول في علبة المفضلة"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"التطبيقات"</string>
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml
index 239d718..cf109b4 100644
--- a/res/values-az-rAZ/strings.xml
+++ b/res/values-az-rAZ/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Tətbiqləri Axtarın..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Tətbiqlər endirilir..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" sorğusuna uyğun Tətbiqlər tapılmadı"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> daxil olun"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Bu Əsas ekranda boş yer yoxdur."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritlər-də yer yoxdur"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Tətbiqlər"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index a09d457..b2c072f 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Търсене в приложенията…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Отваряне на <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"На този начален екран няма повече място."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Няма повече място в областта с любимите"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Приложения"</string>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
index a584f17..271d739 100644
--- a/res/values-bn-rBD/strings.xml
+++ b/res/values-bn-rBD/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"অ্যাপ্লিকেশানগুলি অনুসন্ধান করুন..."</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> এ যান"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"এই হোম স্ক্রীনে আর কোনো জায়গা নেই৷"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"পছন্দসই ট্রে-তে আর কোনো জায়গা নেই"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"অ্যাপ্লিকেশানগুলি"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 8027031..1704459 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Cerca a les aplicacions…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"S\'estan carregant les aplicacions..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"No s\'ha trobat cap aplicació que coincideixi amb <xliff:g id="QUERY">%1$s</xliff:g>"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Vés a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Ja no queda espai en aquesta pantalla d\'inici."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"No hi ha més espai a la safata Preferits."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicacions"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index a047c39..9a7459d 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Hledat v aplikacích…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Načítání aplikací…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Dotazu „<xliff:g id="QUERY">%1$s</xliff:g>“ neodpovídají žádné aplikace"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Přejít na <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Na této ploše již není místo."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Na panelu Oblíbené položky již není místo."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikace"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index c24c159..811a802 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Søg i Apps…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Indlæser apps…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Der blev ikke fundet nogen apps, som matcher \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Gå til <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Der er ikke mere plads på denne startskærm."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Der er ikke mere plads i bakken Foretrukne"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 4633adb..a87ccd8 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Apps suchen…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Apps werden geladen..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Keine Apps für \"<xliff:g id="QUERY">%1$s</xliff:g>\" gefunden"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Gehe zu <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Auf diesem Startbildschirm ist kein Platz mehr vorhanden."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ablage \"Favoriten\" ist voll."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index fd8f1f8..f2081e7 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Αναζήτηση εφαρμογών…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Μετάβαση σε <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Δεν υπάρχει χώρος σε αυτήν την αρχική οθόνη."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Δεν υπάρχει επιπλέον χώρος στην περιοχή Αγαπημένα"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Εφαρμογές"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 23882b1..781ddee 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Search Apps…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Loading Apps…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"No Apps found matching \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Go to <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
@@ -59,7 +60,7 @@
<string name="migration_cling_description" msgid="2752413805582227644">"Import icons and folders from your old Home screens?"</string>
<string name="migration_cling_copy_apps" msgid="946331230090919440">"COPY ICONS"</string>
<string name="migration_cling_use_default" msgid="2626475813981258626">"START AFRESH"</string>
- <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Wallpapers, widgets, & settings"</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Wallpapers, widgets & settings"</string>
<string name="workspace_cling_longpress_description" msgid="6569028007301925917">"Tap & hold background to customise"</string>
<string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"GOT IT"</string>
<string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 23882b1..781ddee 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Search Apps…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Loading Apps…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"No Apps found matching \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Go to <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
@@ -59,7 +60,7 @@
<string name="migration_cling_description" msgid="2752413805582227644">"Import icons and folders from your old Home screens?"</string>
<string name="migration_cling_copy_apps" msgid="946331230090919440">"COPY ICONS"</string>
<string name="migration_cling_use_default" msgid="2626475813981258626">"START AFRESH"</string>
- <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Wallpapers, widgets, & settings"</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Wallpapers, widgets & settings"</string>
<string name="workspace_cling_longpress_description" msgid="6569028007301925917">"Tap & hold background to customise"</string>
<string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"GOT IT"</string>
<string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 23882b1..781ddee 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Search Apps…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Loading Apps…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"No Apps found matching \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Go to <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
@@ -59,7 +60,7 @@
<string name="migration_cling_description" msgid="2752413805582227644">"Import icons and folders from your old Home screens?"</string>
<string name="migration_cling_copy_apps" msgid="946331230090919440">"COPY ICONS"</string>
<string name="migration_cling_use_default" msgid="2626475813981258626">"START AFRESH"</string>
- <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Wallpapers, widgets, & settings"</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Wallpapers, widgets & settings"</string>
<string name="workspace_cling_longpress_description" msgid="6569028007301925917">"Tap & hold background to customise"</string>
<string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"GOT IT"</string>
<string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 0061f66..fd32a19 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Buscar apps…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Cargando aplicaciones…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"No hay aplicaciones que coincidan con <xliff:g id="QUERY">%1$s</xliff:g>."</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Ir a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"No hay más espacio en esta pantalla principal."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"La bandeja de favoritos está llena."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicaciones"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 1169f44..8e5d07f 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Buscar aplicaciones…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Cargando aplicaciones…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"No se han encontrado aplicaciones que contengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Ir a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"No queda espacio en la pantalla de inicio."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"La bandeja de favoritos está completa"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicaciones"</string>
@@ -76,10 +77,10 @@
<string name="allow_rotation_desc" msgid="7635719920854330492">"Al girar el dispositivo"</string>
<string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"La configuración de pantalla actual no permite girar la pantalla"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
- <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Quitar"</string>
<string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Esta aplicación no está instalada"</string>
- <string name="abandoned_promise_explanation" msgid="3990027586878167529">"La aplicación de este icono no está instalada. Puedes eliminar el icono o buscar la aplicación e instalarla manualmente."</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"La aplicación de este icono no está instalada. Puedes quitar el icono o buscar la aplicación e instalarla manualmente."</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Descargando <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="PROGRESS">%2$s</xliff:g> completado)"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"Esperando para instalar <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Añadir a la pantalla de inicio"</string>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index 3fb031a..00f960d 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Otsimine rakendustest …"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Rakenduste laadimine ..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Päringule „<xliff:g id="QUERY">%1$s</xliff:g>” ei vastanud ükski rakendus"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Mine: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Sellel avaekraanil pole enam ruumi."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Salves Lemmikud pole rohkem ruumi"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Rakendused"</string>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
index 6688ed4..e410597 100644
--- a/res/values-eu-rES/strings.xml
+++ b/res/values-eu-rES/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Bilatu aplikazioetan…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Aplikazioak kargatzen…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Ez da aurkitu \"<xliff:g id="QUERY">%1$s</xliff:g>\" bilaketarekin bat datorren aplikaziorik"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Joan hona: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Hasierako pantaila honetan ez dago toki gehiago."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ez dago toki gehiago Gogokoak erretiluan"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikazioak"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 720bb31..747da84 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"جستجوی برنامهها…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"رفتن به <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"فضای بیشتری در این صفحه اصلی موجود نیست."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"فضای بیشتری در سینی موارد دلخواه وجود ندارد"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"برنامهها"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index ac14d86..c97d7b1 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Hae sovelluksia…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Ladataan sovelluksia…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"”<xliff:g id="QUERY">%1$s</xliff:g>” ei palauttanut sovelluksia."</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Siirry: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Tässä aloitusruudussa ei ole enää tilaa."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Suosikit-valikossa ei ole enää tilaa"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Sovellukset"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index c87169f..265e636 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Rechercher des applications..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Chargement des applications en cours..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Aucune application trouvée correspondant à « <xliff:g id="QUERY">%1$s</xliff:g> »"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Aller à « <xliff:g id="QUERY">%1$s</xliff:g> »"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Pas d\'espace libre sur l\'écran d\'accueil."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Il n\'y a plus d\'espace dans la zone des favoris"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Applications"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 192a025..af19699 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Rechercher des applications…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Chargement des applications en cours…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Aucune application ne correspond à la requête \"<xliff:g id="QUERY">%1$s</xliff:g>\"."</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Accéder à <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Pas d\'espace libre sur cet écran d\'accueil."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Plus d\'espace disponible dans la zone de favoris."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Applications"</string>
diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml
index 6ddef28..115497a 100644
--- a/res/values-gl-rES/strings.xml
+++ b/res/values-gl-rES/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Buscar aplicacións..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Cargando aplicacións..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Non se atoparon aplicacións que coincidan con \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Ir a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Non hai máis espazo nesta pantalla de inicio."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Non hai máis espazo na bandexa de favoritos"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicacións"</string>
diff --git a/res/values-gu-rIN/strings.xml b/res/values-gu-rIN/strings.xml
index fbe3efe..ef37318 100644
--- a/res/values-gu-rIN/strings.xml
+++ b/res/values-gu-rIN/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ઍપ્લિકેશનોમાં શોધો…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> પર જાઓ"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"આ હોમ સ્ક્રીન પર વધુ જગ્યા નથી."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"મનપસંદ ટ્રે પર વધુ જગ્યા નથી"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"ઍપ્લિકેશનો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 695a1e6..742cd90 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ऐप्स खोजें…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> पर जाएं"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"इस होम स्क्रीन पर स्थान शेष नहीं है."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"पसंदीदा ट्रे में और स्थान नहीं है"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"ऐप्लिकेशन"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 61b365f..027c926 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Pretraživanje aplikacija…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Učitavanje aplikacija…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nema aplikacija podudarnih s upitom \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Idite na <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Na ovom početnom zaslonu više nema mjesta."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Nema više prostora na traci Favoriti"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacije"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index f558fb6..783a367 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Alkalmazások keresése…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Alkalmazások betöltése…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Egy alkalmazás sem található a(z) „<xliff:g id="QUERY">%1$s</xliff:g>” lekérdezésre."</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Keresse fel ezt: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Nincs több hely ezen a kezdőképernyőn."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Nincs több hely a Kedvencek tálcán"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Alkalmazások"</string>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index 1f2de4c..8d57e59 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Հավելվածների որոնում…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Գնալ <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Այլևս տեղ չկա այս հիմնական էկրանին:"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ընտրյալների ցուցակում այլևս ազատ տեղ չկա"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Ծրագրեր"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 50aa03d..3faefb7 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Telusuri Aplikasi..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Memuat Aplikasi..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Tidak ditemukan Aplikasi yang cocok dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Buka <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Tidak ada ruang lagi pada layar Utama ini."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Tidak ada ruang tersisa di baki Favorit"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikasi"</string>
diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml
index c2f3396..42cb440 100644
--- a/res/values-is-rIS/strings.xml
+++ b/res/values-is-rIS/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Leita í forritum…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Hleður forrit…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Ekki fundust forrit sem samsvara „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Fara í <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Ekki meira pláss á þessum heimaskjá."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ekki meira pláss í bakka fyrir uppáhald"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Forrit"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 467094f..b651bc8 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Ricerca app…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Caricamento di app…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nessuna app trovata corrispondente a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Vai a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Spazio nella schermata Home esaurito."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Spazio esaurito nella barra dei Preferiti"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"App"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 14d0aa6..44bcd87 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"חיפוש אפליקציות..."</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"עבור אל <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"אין עוד מקום במסך דף הבית הזה."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"אין עוד מקום במגש המועדפים"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"אפליקציות"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index e86e56f..e70770c 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"アプリを検索…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>にアクセス"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"このホーム画面に空きスペースがありません。"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"お気に入りトレイに空きスペースがありません"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"アプリ"</string>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index 682cc12..b531a56 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"აპების ძიება…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"გადადი <xliff:g id="QUERY">%1$s</xliff:g>-ში"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"ამ მთავარ ეკრანზე ადგილი აღარ არის."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"რჩეულების თაროზე ადგილი არ არის"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"აპები"</string>
@@ -59,7 +60,7 @@
<string name="migration_cling_description" msgid="2752413805582227644">"გსურთ, ძველი მთავარი ეკრანიდან ხატულების და საქაღ. იმპორტი?"</string>
<string name="migration_cling_copy_apps" msgid="946331230090919440">"ხატულების კოპირება"</string>
<string name="migration_cling_use_default" msgid="2626475813981258626">"სტანდარტული განლაგება"</string>
- <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"ფონები, ვიჯეტები, & პარამეტრები"</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"ფონები, ვიჯეტები & პარამეტრები"</string>
<string name="workspace_cling_longpress_description" msgid="6569028007301925917">"მოსარგებად, ხანგრძლივად შეეხეთ ფონს."</string>
<string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"გასაგებია"</string>
<string name="folder_opened" msgid="94695026776264709">"საქაღალდე გახსნილია, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
index 3fa3d80..7c9aaee 100644
--- a/res/values-kk-rKZ/strings.xml
+++ b/res/values-kk-rKZ/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Қолданбаларды іздеу…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> сұрауына өту"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Бұл Негізгі экранда орын қалмады."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Қалаулылар науасында орын қалмады"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Қолданбалар"</string>
@@ -69,7 +70,7 @@
<string name="folder_renamed" msgid="1794088362165669656">"Қалта атауы <xliff:g id="NAME">%1$s</xliff:g> болып өзгертілді"</string>
<string name="folder_name_format" msgid="6629239338071103179">"Қалта: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Артқы фондар"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Тұсқағаздар"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Параметрлер"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Әкімші өшірді"</string>
<string name="allow_rotation_title" msgid="3132336367556833843">"Негізгі экранды айналдыруды рұқсат ету"</string>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index 4cd6856..dc9b606 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ស្វែងរកកម្មវិធី…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"ចូលទៅកាន់ <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"គ្មានបន្ទប់នៅលើអេក្រង់ដើមនេះទៀតទេ។"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"គ្មានបន្ទប់ក្នុងថាសនិយមប្រើ"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"កម្មវិធី"</string>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
index dcd64ec..5d6ec41 100644
--- a/res/values-kn-rIN/strings.xml
+++ b/res/values-kn-rIN/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ಅಪ್ಲಿಕೇಷನ್ಗಳನ್ನು ಹುಡುಕಿ..."</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> ಗೆ ಹೋಗಿ"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"ಈ ಮುಖಪುಟದ ಪರದೆಯಲ್ಲಿ ಹೆಚ್ಚು ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ಮೆಚ್ಚಿನವುಗಳ ಟ್ರೇನಲ್ಲಿ ಹೆಚ್ಚಿನ ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"ಅಪ್ಲಿಕೇಶನ್ಗಳು"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 572c62d..5fd20e2 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"앱 검색..."</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>(으)로 이동"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"홈 화면에 더 이상 공간이 없습니다."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"즐겨찾기 트레이에 더 이상 공간이 없습니다."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"앱"</string>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index 2edd170..8fce8c8 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Колдонмолорду издөө…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> сурамына өтүңүз"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Бул Үй экранында бош орун жок."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Тандамалдар тайпасында орун калган жок"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Колдонмолор"</string>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index 228bd90..24e6096 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ຄົ້ນຫາແອັບ"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"ໄປທີ່ <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"ບໍ່ມີຫ້ອງເຫຼືອໃນໜ້າຈໍຫຼັກນີ້."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ບໍ່ມີບ່ອນຫວ່າງໃນຖາດສຳລັບເກັບສິ່ງທີ່ໃຊ້ເປັນປະຈຳ"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"ແອັບຯ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 3796c26..f48f569 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Ieškoti programų..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Įkeliamos programos..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nerasta jokių užklausą „<xliff:g id="QUERY">%1$s</xliff:g>“ atitinkančių programų"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Eiti į <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Šiame pagrindiniame ekrane vietos nebėra."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Mėgstamiausių dėkle nebėra vietos"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Programos"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index c592d3b..7aaab86 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Meklēt lietotnes…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Notiek lietotņu ielāde…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Vaicājumam “<xliff:g id="QUERY">%1$s</xliff:g>” neatbilda neviena lietotne."</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Doties uz: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Šajā sākuma ekrānā vairs nav vietas."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Izlases joslā vairs nav vietas."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Lietotnes"</string>
diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml
index a691bce..d7c67f2 100644
--- a/res/values-mk-rMK/strings.xml
+++ b/res/values-mk-rMK/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Пребарувај апликации…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Оди на <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Нема повеќе простор на овој екран на почетната страница."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Нема повеќе простор на лентата „Омилени“"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Апликации"</string>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index 5cfff55..94dd5b5 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ആപ്പ്സ് തിരയുക…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> എന്നതിലേക്ക് പോവുക"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"ഈ ഹോം സ്ക്രീനിൽ ഒഴിവൊന്നുമില്ല."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"പ്രിയപ്പെട്ടവയുടെ ട്രേയിൽ ഒഴിവൊന്നുമില്ല"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"അപ്ലിക്കേഷനുകൾ"</string>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index a47fcfc..35d522c 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Апп хайх..."</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> руу очих"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Энэ Нүүр дэлгэц зайгүй."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"\"Дуртай\" трей дээр өөр зай байхгүй байна"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Апп"</string>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
index c14a5d2..1598e52 100644
--- a/res/values-mr-rIN/strings.xml
+++ b/res/values-mr-rIN/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"अॅप्स शोधा..."</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> वर जा"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"या मुख्य स्क्रीनवर आणखी जागा नाही."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"आवडीच्या ट्रे मध्ये आणखी जागा नाही"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"अॅप्स"</string>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index 9bb4ea0..e9c4eac 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Cari Apl..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Memuatkan Apl…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Tiada Apl yang ditemui sepadan dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Pergi ke <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Tiada lagi ruang pada skrin Laman Utama ini."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Tiada ruang dalam dulang Kegemaran lagi"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apl"</string>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index b9ca0ea..53786a7 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -32,10 +32,11 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"အက်ပ်များကို ရှာဖွေပါ…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"App များ ရယူနေစဉ်..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" နှင့်ကိုက်ညီသည့် အပ်ဖ်များမတွေ့ပါ"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>ကို သွားပါ"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"ဤပင်မမျက်နှာစာတွင် နေရာလွတ် မကျန်တော့ပါ"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"အနှစ်သက်ဆုံးများ ထားရာတွင် နေရာလွတ် မကျန်တော့ပါ"</string>
- <string name="all_apps_button_label" msgid="9110807029020582876">"အပ်ပလီကေးရှင်းများ"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"အက်ပ်များ"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"ပင်မစာမျက်နှာ"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"ဖယ်ရှားမည်"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"ဖယ်ထုတ်မည်"</string>
@@ -59,18 +60,18 @@
<string name="migration_cling_description" msgid="2752413805582227644">"ပင်မစာမျက်နှာအဟောင်းမှ ပုံညွှန်းများ နှင့် အကန့်များကို ယူလာပါမလား"</string>
<string name="migration_cling_copy_apps" msgid="946331230090919440">"COPY ICONS"</string>
<string name="migration_cling_use_default" msgid="2626475813981258626">"START FRESH"</string>
- <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"နောက်ခံများ၊ ဝီဂျက်များ& ဆက်တင်များ"</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"နောက်ခံများ၊ ဝိဂျက်များ& ဆက်တင်များ"</string>
<string name="workspace_cling_longpress_description" msgid="6569028007301925917">"စိတ်တိုင်းကျပြုပြင်ရန် နောက်ခံတို့၍ & ဖိထားပါ"</string>
- <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ရပြီ"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ရပါပြီ"</string>
<string name="folder_opened" msgid="94695026776264709">"ဖွင့်ထားသောအကန့်, <xliff:g id="WIDTH">%1$d</xliff:g> နှင့် <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"ဖိုင်တွဲကို ပိတ်ရန် တို့ပါ"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"အမည်ပြောင်းခြင်းကို သိမ်းဆည်းရန် တို့ပါ"</string>
<string name="folder_closed" msgid="4100806530910930934">"ပိတ်ထားသောအကန့်"</string>
<string name="folder_renamed" msgid="1794088362165669656">"ပြောင်းလဲလိုက်သော အကန့်အမည် <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format" msgid="6629239338071103179">"အကန့်အမည်: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="widget_button_text" msgid="2880537293434387943">"ဝဒ်ဂျက်များ"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"ဝိဂျက်များ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"နောက်ခံများ"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"အပြင်အဆင်များ"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"ဆက်တင်များ"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"သင့်စီမံခန့်ခွဲသူက ပိတ်လိုက်ပါသည်"</string>
<string name="allow_rotation_title" msgid="3132336367556833843">"ပင်မစာမျက်နှာကို လှည့်ခွင့်ပြုပါ"</string>
<string name="allow_rotation_desc" msgid="7635719920854330492">"စက်ပစ္စည်းကို လှည့်ထားသည့်အခါ"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index e5bd1c1..5a0c2b5 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Søk i apper"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Laster inn apper …"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Fant ingen apper som samsvarer med «<xliff:g id="QUERY">%1$s</xliff:g>»"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Gå til <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Denne startsiden er full."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritter-skuffen er full"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apper"</string>
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml
index 169f3de..22d994e 100644
--- a/res/values-ne-rNP/strings.xml
+++ b/res/values-ne-rNP/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"अनुप्रयोगहरू खोज्नुहोस्..."</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> मा जानुहोस्"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"यो गृह स्क्रिनमा कुनै थप ठाउँ छैन।"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"मनपर्ने ट्रे अब कुनै ठाँउ छैन"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"अनुप्रयोगहरू"</string>
@@ -59,7 +60,7 @@
<string name="migration_cling_description" msgid="2752413805582227644">"आफ्नो पुरानो गृह स्क्रीनबाट अाईकन र फोल्डरहरू आयात गर्नुहोस्?"</string>
<string name="migration_cling_copy_apps" msgid="946331230090919440">"ICONS प्रतिलिप गर्नुहोस्"</string>
<string name="migration_cling_use_default" msgid="2626475813981258626">"START FRESH"</string>
- <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"वालपेपरहरू, विजेट; सेटिङहरू"</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"वालपेपर, विजेट तथा सेटिङहरू"</string>
<string name="workspace_cling_longpress_description" msgid="6569028007301925917">"अनुकूलन गर्नका लागि पृष्ठभूमिलाई ट्याप गरी थिचिरहनुहोस्"</string>
<string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"बुँझें"</string>
<string name="folder_opened" msgid="94695026776264709">"फोल्डर खुल्यो <xliff:g id="WIDTH">%1$d</xliff:g> बाट <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index a546a85..b2f48a0 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Apps zoeken…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Apps laden…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Er zijn geen apps gevonden die overeenkomen met \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Ga naar <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Er is geen ruimte meer op dit startscherm."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Geen ruimte meer in het vak \'Favorieten\'"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
diff --git a/res/values-pa-rIN/strings.xml b/res/values-pa-rIN/strings.xml
index 47bb556..4ab6b9b 100644
--- a/res/values-pa-rIN/strings.xml
+++ b/res/values-pa-rIN/strings.xml
@@ -30,9 +30,10 @@
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"ਡਬਲ-ਟੈਪ & ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਹੋਲਡ ਕਰੋ ਅਤੇ ਕਸਟਮ ਕਿਰਿਆਵਾਂ ਵਰਤੋ।"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ਐਪਾਂ ਖੋਜੋ…"</string>
- <string name="all_apps_loading_message" msgid="7557140873644765180">"ਐਪਸ ਲੋਡ ਕਰ ਰਿਹਾ ਹੈ..."</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> ਤੇ ਜਾਓ"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"ਇਸ ਹੋਮ ਸਕ੍ਰੀਨ ਲਈ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ ਹੈ।"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ਮਨਪਸੰਦ ਟ੍ਰੇ ਵਿੱਚ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ।"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"ਐਪਾਂ"</string>
@@ -40,12 +41,12 @@
<string name="remove_drop_target_label" msgid="7812859488053230776">"ਹਟਾਓ"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"ਸਥਾਪਨਾ ਰੱਦ ਕਰੋ"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ਐਪ ਜਾਣਕਾਰੀ"</string>
- <string name="permlab_install_shortcut" msgid="5632423390354674437">"ਸ਼ੌਰਟਕਟ ਇੰਸਟੌਲ ਕਰੋ"</string>
- <string name="permdesc_install_shortcut" msgid="923466509822011139">"ਇੱਕ ਐਪ ਨੂੰ ਉਪਭੋਗਤਾ ਦੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਸ਼ੌਰਟਕਟ ਜੋੜਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
- <string name="permlab_read_settings" msgid="1941457408239617576">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ੌਰਟਕਟ ਪੜ੍ਹੋ"</string>
- <string name="permdesc_read_settings" msgid="5833423719057558387">"ਐਪ ਨੂੰ ਹੋਮ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ੌਰਟਕਟ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
- <string name="permlab_write_settings" msgid="3574213698004620587">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ੌਰਟਕਟ ਲਿਖੋ"</string>
- <string name="permdesc_write_settings" msgid="5440712911516509985">"ਐਪ ਨੂੰ ਹੋਮ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ੌਰਟਕਟ ਬਦਲਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"ਸ਼ਾਰਟਕੱਟ ਇੰਸਟੌਲ ਕਰੋ"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"ਇੱਕ ਐਪ ਨੂੰ ਉਪਭੋਗਤਾ ਦੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਸ਼ਾਰਟਕੱਟ ਜੋੜਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਪੜ੍ਹੋ"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"ਐਪ ਨੂੰ ਹੋਮ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਲਿਖੋ"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"ਐਪ ਨੂੰ ਹੋਮ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਬਦਲਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਫੋਨ ਕਾਲਾਂ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਹੈ"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"ਵਿਜੇਟ ਲੋਡ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"ਸਥਾਪਤ ਕਰੋ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 54d7780..92063e1 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Szukaj w aplikacjach…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Wczytuję aplikacje…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nie znaleziono aplikacji pasujących do zapytania „<xliff:g id="QUERY">%1$s</xliff:g>”"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Otwórz <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Brak miejsca na tym ekranie głównym."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Brak miejsca w Ulubionych"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacje"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index dddf2dc..29d6c94 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Pesquisar aplicações..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"A carregar aplicações..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Não foram encontradas aplic. que correspondam a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Aceder a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Sem espaço suficiente neste Ecrã principal."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Não existe mais espaço no tabuleiro de Favoritos"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicações"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index f821b21..69a60e1 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Pesquisar apps..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Carregando apps…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nenhum app encontrado que corresponda a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Ir para <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Não há mais espaço na tela inicial."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Sem espaço na bandeja de favoritos"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 6be7727..cfe631f 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Căutați aplicații…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Se încarcă aplicațiile..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nu s-a găsit nicio aplicație pentru „<xliff:g id="QUERY">%1$s</xliff:g>”"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Accesați <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Nu mai este loc pe acest Ecran de pornire."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Spațiu epuizat în bara Preferate"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicații"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 9349c0b..a9fe949 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Поиск приложений"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"На этом экране все занято"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"В разделе \"Избранное\" больше нет места"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Приложения"</string>
diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml
index d13ae32..41e4d9a 100644
--- a/res/values-si-rLK/strings.xml
+++ b/res/values-si-rLK/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"යෙදුම් සොයන්න..."</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> වෙත යන්න"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"මෙම මුල් පිටු තිරය මත තවත් අවසර නැත."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ප්රියතම දෑ ඇති තැටියේ තවත් ඉඩ නොමැත"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"යෙදුම්"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 4f92d1e..d0dcf27 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -29,10 +29,11 @@
<string name="long_press_widget_to_add" msgid="5154837155685183344">"Miniaplikáciu pridáte klepnutím a podržaním."</string>
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"Miniaplikáciu pridáte dvojitým klepnutím a pridržaním alebo pomocou vlastných akcií."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Vyhľadávanie v aplikáciách…"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Hľadať aplikácie…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Načítavajú sa aplikácie..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nenašli sa žiadne aplikácie zodpovedajúce dopytu <xliff:g id="QUERY">%1$s</xliff:g>"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Prejsť na dopyt <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Na tejto ploche už nie je miesto"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Na paneli Obľúbené položky už nie je miesto"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikácie"</string>
@@ -60,7 +61,7 @@
<string name="migration_cling_copy_apps" msgid="946331230090919440">"SKOPÍROVAŤ IKONY"</string>
<string name="migration_cling_use_default" msgid="2626475813981258626">"ZAČAŤ ODZNOVA"</string>
<string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Tapety, miniaplikácie a nastavenia"</string>
- <string name="workspace_cling_longpress_description" msgid="6569028007301925917">"Pozadie prispôsobíte klepnutím a podržaním"</string>
+ <string name="workspace_cling_longpress_description" msgid="6569028007301925917">"Pozadie môžete prispôsobiť jeho pridržaním"</string>
<string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"DOBRE"</string>
<string name="folder_opened" msgid="94695026776264709">"Otvorený priečinok, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Priečinok zavriete klepnutím"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 27412db..5d396e4 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Iskanje po aplikacijah …"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Nalaganje aplikacij …"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Ni aplikacij, ki bi ustrezale poizvedbi »<xliff:g id="QUERY">%1$s</xliff:g>«"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Odpri storitev <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Na tem začetnem zaslonu ni več prostora."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"V vrstici za priljubljene ni več prostora"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacije"</string>
diff --git a/res/values-sq-rAL/strings.xml b/res/values-sq-rAL/strings.xml
index 4ee58f0..2627691 100644
--- a/res/values-sq-rAL/strings.xml
+++ b/res/values-sq-rAL/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Kërko për aplikacione..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Po ngarkon aplikacionet..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nuk u gjet asnjë aplikacion që përputhet me \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Shko te <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Nuk ka më hapësirë në këtë ekran bazë."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Nuk ka më hapësirë në tabakanë \"Të preferuarat\""</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacionet"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index c174ace..c4154f5 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Претражите апликације..."</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Иди на апликацију <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Нема више простора на овом почетном екрану."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Нема више простора на траци Омиљено"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Апликације"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 3019603..7f5af4b 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Sök efter appar …"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Läser in appar …"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Det gick inte att hitta några appar som matchar <xliff:g id="QUERY">%1$s</xliff:g>"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Gå till <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Det finns inte plats för mer på den här startskärmen."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritfältet är fullt"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Appar"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 5b2eb27..291e52d 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Tafuta Programu..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Inapakia Programu..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Haikupata programu zinazolingana na \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Nenda kwenye <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Hakuna nafasi katika skrini hii ya Mwanzo."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Hakuna nafasi zaidi katika treya ya Vipendeleo"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Programu"</string>
@@ -62,7 +63,7 @@
<string name="migration_cling_copy_apps" msgid="946331230090919440">"NAKILI IKONI"</string>
<string name="migration_cling_use_default" msgid="2626475813981258626">"ANZA UPYA"</string>
<string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Mandhari, wijeti na mipangilio"</string>
- <string name="workspace_cling_longpress_description" msgid="6569028007301925917">"Gusa na ushikilie mandhari ili uweke mapendeleo"</string>
+ <string name="workspace_cling_longpress_description" msgid="6569028007301925917">"Gusa na ushikilie mandhari ili ubadilishe upendavyo"</string>
<string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"NIMEELEWA"</string>
<string name="folder_opened" msgid="94695026776264709">"Folda imefunguliwa, <xliff:g id="WIDTH">%1$d</xliff:g> kwa <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Gonga ili ufunge folda"</string>
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml
index 6bb0a10..495f9d1 100644
--- a/res/values-ta-rIN/strings.xml
+++ b/res/values-ta-rIN/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"பயன்பாடுகளில் தேடுக…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>க்குச் செல்லவும்"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"முகப்புத் திரையில் இடமில்லை."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"பிடித்தவை ட்ரேயில் இடமில்லை"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"பயன்பாடுகள்"</string>
diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml
index 82a772e..2757d4a 100644
--- a/res/values-te-rIN/strings.xml
+++ b/res/values-te-rIN/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"అనువర్తనాల్లో శోధించండి…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>కి వెళ్లు"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"ఈ హోమ్ స్క్రీన్లో ఖాళీ లేదు."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ఇష్టమైనవి ట్రేలో ఖాళీ లేదు"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"అనువర్తనాలు"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index e22ebf1..3fa6211 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ค้นหาแอป…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"ไปที่ <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"ไม่มีที่ว่างในหน้าจอหลักนี้"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ไม่มีพื้นที่เหลือในถาดรายการโปรด"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"แอป"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index ed0206e..dd7adea 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Maghanap ng Mga App…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Nilo-load ang Mga App…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Walang nakitang Mga App na tumutugma sa \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Pumunta sa <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Wala nang lugar sa Home screen na ito."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Wala nang lugar sa tray ng Mga Paborito"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 94c38d9..40ac473 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Uygulamalarda Ara…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Uygulamalar Yükleniyor…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ile eşleşen uygulama bulunamadı"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> uygulamasına git"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Bu Ana ekranda yer kalmadı."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoriler tepsisinde başka yer kalmadı"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Uygulamalar"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 1fc74b6..497d71e 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Пошук додатків…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Перейти в додаток <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"На цьому головному екрані більше немає місця."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"В області \"Вибране\" немає місця"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Додатки"</string>
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml
index c6e6cfc..6bc601d 100644
--- a/res/values-ur-rPK/strings.xml
+++ b/res/values-ur-rPK/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ایپس تلاش کریں…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> پر جائیں"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"اس ہوم اسکرین پر مزید کوئی گنجائش نہیں ہے۔"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"پسندیدہ ٹرے میں مزید کوئی گنجائش نہیں ہے"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"ایپس"</string>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index 859ff12..80e3460 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Ilovalar ichidan qidirish…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Ilovalar yuklanmoqda…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"“<xliff:g id="QUERY">%1$s</xliff:g>” so‘rovi bo‘yicha hech narsa topilmadi"</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Uy ekranida bitta ham xona yo‘q."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ajratilganlarda birorta ham xona yo‘q"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Ilovalar"</string>
@@ -73,7 +74,7 @@
<string name="settings_button_text" msgid="8119458837558863227">"Sozlamalar"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator tomonidan o‘chirilgan"</string>
<string name="allow_rotation_title" msgid="3132336367556833843">"Asosiy ekranni aylantirishga ruxsat berish"</string>
- <string name="allow_rotation_desc" msgid="7635719920854330492">"Qurilma aylanganda"</string>
+ <string name="allow_rotation_desc" msgid="7635719920854330492">"Qurilma burilganda"</string>
<string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Ekran sozlamalariga ko‘ra uni aylantirib bo‘lmaydi"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Noma’lum"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"O‘chirish"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 5da05a1..baf2cad 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Tìm kiếm ứng dụng..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Đang tải ứng dụng..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Không tìm thấy ứng dụng nào phù hợp với \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Chuyển tới <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Không còn chỗ trên Màn hình chính này."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Không còn chỗ trong khay Mục yêu thích"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Ứng dụng"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 8f269ed..d784b47 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"搜索应用…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"转到 <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"此主屏幕上已没有空间。"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"收藏栏已满"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"应用"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index a8bd7bb..0440648 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"搜尋應用程式…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"前往 <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"主畫面已無空間。"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"我的收藏寄存區沒有足夠空間"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"應用程式"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 6d66f18..c9bb47f 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"搜尋應用程式…"</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>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"前往 <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"這個主螢幕已無空間。"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"「我的最愛」匣已無可用空間"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"應用程式"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 2184831..bacf2b8 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -32,7 +32,8 @@
<string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Sesha izinhlelo zokusebenza..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Ilayisha izinhlelo zokusebenza..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Azikho izinhlelo zokusebenza ezitholakele ezifana ne-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
- <string name="all_apps_search_market_message" msgid="5470761048755751471">"Hamba ku-<xliff:g id="QUERY">%1$s</xliff:g>"</string>
+ <!-- no translation found for all_apps_search_market_message (1366263386197059176) -->
+ <skip />
<string name="out_of_space" msgid="4691004494942118364">"Asisekho isikhala kulesi sikrini Sasekhaya."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Asisekho isikhala kwitreyi lezintandokazi"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Izinhlelo zokusebenza"</string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 6f41a4b..64868f2 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -17,19 +17,7 @@
<resources>
<!-- Dynamic Grid -->
<dimen name="dynamic_grid_edge_margin">6dp</dimen>
- <dimen name="dynamic_grid_search_bar_height">48dp</dimen>
- <!-- We want 46dp extra for the tall search bar. -->
- <dimen name="dynamic_grid_search_bar_height_tall">94dp</dimen>
- <dimen name="qsb_internal_padding_top">8dp</dimen>
- <dimen name="qsb_internal_padding_bottom">8dp</dimen>
- <dimen name="dynamic_grid_search_bar_extra_top_padding">0dp</dimen>
- <!-- Reduce the space between the status bar and the search bar when the search bar is tall -->
- <dimen name="dynamic_grid_search_bar_negative_top_padding_short">-4dp</dimen>
- <dimen name="dynamic_grid_search_bar_bottom_padding">4dp</dimen>
- <!-- Reduce the padding between the search bar and workspace when the search bar is tall -->
- <dimen name="dynamic_grid_search_bar_bottom_negative_padding_short">-6dp</dimen>
- <dimen name="dynamic_grid_search_bar_bottom_padding_tablet">16dp</dimen>
- <dimen name="dynamic_grid_page_indicator_height">20dp</dimen>
+ <dimen name="dynamic_grid_page_indicator_height">1dp</dimen>
<dimen name="dynamic_grid_icon_drawable_padding">4dp</dimen>
<dimen name="dynamic_grid_workspace_page_spacing">8dp</dimen>
<dimen name="dynamic_grid_overview_min_icon_zone_height">80dp</dimen>
@@ -37,6 +25,15 @@
<dimen name="dynamic_grid_overview_bar_item_width">80dp</dimen>
<dimen name="dynamic_grid_overview_bar_spacer_width">20dp</dimen>
+ <dimen name="dynamic_grid_workspace_top_padding">12dp</dimen>
+ <!-- Minimum space between workspace and hotseat in spring loaded mode -->
+ <dimen name="dynamic_grid_min_spring_loaded_space">8dp</dimen>
+
+<!-- Drop target bar -->
+ <dimen name="dynamic_grid_drop_target_size">48dp</dimen>
+ <dimen name="vert_drop_target_vertical_gap">20dp</dimen>
+ <dimen name="vert_drop_target_horizontal_gap">14dp</dimen>
+
<!-- App Widget resize frame -->
<dimen name="default_widget_padding">8dp</dimen>
<dimen name="widget_handle_margin">13dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e675a92..9f011e5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -52,9 +52,8 @@
<string name="all_apps_loading_message">Loading Apps…</string>
<!-- No-search-results text. [CHAR_LIMIT=50] -->
<string name="all_apps_no_search_results">No Apps found matching \"<xliff:g id="query" example="Android">%1$s</xliff:g>\"</string>
- <!-- Search market text. This is a format string where the first argument is the name of the activity
- handling the search. The format string does not need to handle both of these arguments. [CHAR_LIMIT=50] -->
- <string name="all_apps_search_market_message">Go to <xliff:g id="query" example="Play Store">%1$s</xliff:g></string>
+ <!-- Label for the button which allows the user to get app search results. [CHAR_LIMIT=50] -->
+ <string name="all_apps_search_market_message">Search for more apps</string>
<!-- Drag and drop -->
<skip />
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index 28fd268..e0694f3 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -66,6 +66,7 @@
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
}
+ @Override
public Intent getIntent() {
return intent;
}
diff --git a/src/com/android/launcher3/AppInfoDropTargetBar.java b/src/com/android/launcher3/AppInfoDropTargetBar.java
deleted file mode 100644
index e06f941..0000000
--- a/src/com/android/launcher3/AppInfoDropTargetBar.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.Context;
-import android.util.AttributeSet;
-
-import com.android.launcher3.dragndrop.DragController;
-
-public class AppInfoDropTargetBar extends BaseDropTargetBar {
- private ButtonDropTarget mAppInfoDropTarget;
-
- public AppInfoDropTargetBar(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public AppInfoDropTargetBar(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- // Get the individual components
- mAppInfoDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.info_target_text);
-
- mAppInfoDropTarget.setDropTargetBar(this);
- }
-
- @Override
- public void setup(Launcher launcher, DragController dragController) {
- dragController.addDragListener(this);
-
- dragController.addDragListener(mAppInfoDropTarget);
- dragController.addDropTarget(mAppInfoDropTarget);
-
- mAppInfoDropTarget.setLauncher(launcher);
- }
-
- @Override
- public void showDropTargets() {
- animateDropTargetBarToAlpha(1f, DEFAULT_DRAG_FADE_DURATION);
- }
-
- @Override
- public void hideDropTargets() {
- animateDropTargetBarToAlpha(0f, DEFAULT_DRAG_FADE_DURATION);
- }
-
- private void animateDropTargetBarToAlpha(float alpha, int duration) {
- resetAnimation(duration);
- if (duration > 0) {
- animateAlpha(mDropTargetBar, alpha, DEFAULT_INTERPOLATOR);
- mCurrentAnimation.start();
- } else {
- mDropTargetBar.setAlpha(alpha);
- AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
- }
- }
-
- @Override
- public void enableAccessibleDrag(boolean enable) {
- mAppInfoDropTarget.enableAccessibleDrag(enable);
- }
-}
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
index d0cacd3..a9ef43d 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -24,6 +24,9 @@
import android.view.View;
import android.widget.FrameLayout;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.config.FeatureFlags;
+
/**
* A base container view, which supports resizing.
*/
@@ -48,7 +51,11 @@
super(context, attrs, defStyleAttr);
int width = ((Launcher) context).getDeviceProfile().availableWidthPx;
- mHorizontalPadding = DeviceProfile.getContainerPadding(context, width);
+ if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && (this instanceof AllAppsContainerView)) {
+ mHorizontalPadding = 0;
+ } else {
+ mHorizontalPadding = DeviceProfile.getContainerPadding(context, width);
+ }
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.BaseContainerView, defStyleAttr, 0);
diff --git a/src/com/android/launcher3/BaseDropTargetBar.java b/src/com/android/launcher3/BaseDropTargetBar.java
deleted file mode 100644
index 9b38623..0000000
--- a/src/com/android/launcher3/BaseDropTargetBar.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AccelerateInterpolator;
-import android.widget.FrameLayout;
-
-import com.android.launcher3.dragndrop.DragController;
-
-/**
- * Base class for drop target bars (where you can drop apps to do actions such as uninstall).
- */
-public abstract class BaseDropTargetBar extends FrameLayout implements DragController.DragListener {
- protected static final int DEFAULT_DRAG_FADE_DURATION = 175;
- protected static final TimeInterpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator();
-
- protected View mDropTargetBar;
- protected boolean mAccessibilityEnabled = false;
-
- protected AnimatorSet mCurrentAnimation;
- protected boolean mDeferOnDragEnd;
-
- public BaseDropTargetBar(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public BaseDropTargetBar(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mDropTargetBar = findViewById(R.id.drag_target_bar);
-
- // Create the various fade animations
- mDropTargetBar.setAlpha(0f);
- }
-
- /**
- * Convenience method to animate the alpha of a view.
- */
- protected void animateAlpha(View v, float alpha, TimeInterpolator interpolator) {
- if (Float.compare(v.getAlpha(), alpha) != 0) {
- ObjectAnimator anim = ObjectAnimator.ofFloat(v, View.ALPHA, alpha);
- anim.setInterpolator(interpolator);
- anim.addListener(new ViewVisiblilyUpdateHandler(v));
- mCurrentAnimation.play(anim);
- }
- }
-
- protected void resetAnimation(int newAnimationDuration) {
- // Update the accessibility state
- AccessibilityManager am = (AccessibilityManager)
- getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- mAccessibilityEnabled = am.isEnabled();
-
- // Cancel any existing animation
- if (mCurrentAnimation != null) {
- mCurrentAnimation.cancel();
- mCurrentAnimation = null;
- }
-
- if (newAnimationDuration > 0) {
- mCurrentAnimation = new AnimatorSet();
- mCurrentAnimation.setDuration(newAnimationDuration);
- }
- }
-
- /*
- * DragController.DragListener implementation
- */
- @Override
- public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
- showDropTargets();
- }
-
- /**
- * This is called to defer hiding the delete drop target until the drop animation has completed,
- * instead of hiding immediately when the drag has ended.
- */
- protected void deferOnDragEnd() {
- mDeferOnDragEnd = true;
- }
-
- @Override
- public void onDragEnd() {
- if (!mDeferOnDragEnd) {
- hideDropTargets();
- } else {
- mDeferOnDragEnd = false;
- }
- }
-
- public abstract void showDropTargets();
-
- public abstract void hideDropTargets();
-
- public abstract void enableAccessibleDrag(boolean enable);
-
- public abstract void setup(Launcher launcher, DragController dragController);
-
- private class ViewVisiblilyUpdateHandler extends AnimatorListenerAdapter {
- private final View mView;
-
- ViewVisiblilyUpdateHandler(View v) {
- mView = v;
- }
-
- @Override
- public void onAnimationStart(Animator animation) {
- // Ensure that the view is visible for the animation
- mView.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void onAnimationEnd(Animator animation){
- AlphaUpdateListener.updateVisibility(mView, mAccessibilityEnabled);
- }
-
- }
-}
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
index a680169..fd0045e 100644
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
+++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
@@ -27,7 +27,6 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.view.MotionEvent;
-import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import com.android.launcher3.util.Thunk;
@@ -293,7 +292,7 @@
/**
* Returns whether the specified points are near the scroll bar bounds.
*/
- private boolean isNearThumb(int x, int y) {
+ public boolean isNearThumb(int x, int y) {
mTmpRect.set(mThumbOffset.x, mThumbOffset.y, mThumbOffset.x + mThumbWidth,
mThumbOffset.y + mThumbHeight);
mTmpRect.inset(mTouchInset, mTouchInset);
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 43afbe5..61ac713 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -55,10 +55,10 @@
private static final int DRAG_VIEW_DROP_DURATION = 285;
private final boolean mHideParentOnDisable;
+ protected final Launcher mLauncher;
- protected Launcher mLauncher;
private int mBottomDragPadding;
- protected BaseDropTargetBar mDropTargetBar;
+ protected DropTargetBar mDropTargetBar;
/** Whether this drop target is active for the current drag */
protected boolean mActive;
@@ -80,6 +80,8 @@
public ButtonDropTarget(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ mLauncher = (Launcher) context;
+
Resources resources = getResources();
mBottomDragPadding = resources.getDimensionPixelSize(R.dimen.drop_target_drag_padding);
@@ -109,11 +111,7 @@
}
}
- public void setLauncher(Launcher launcher) {
- mLauncher = launcher;
- }
-
- public void setDropTargetBar(BaseDropTargetBar dropTargetBar) {
+ public void setDropTargetBar(DropTargetBar dropTargetBar) {
mDropTargetBar = dropTargetBar;
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index e3bb5fa..6755ff7 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -896,14 +896,30 @@
if (!isFullscreen) {
left += (int) Math.ceil(getUnusedHorizontalSpace() / 2f);
}
+ int right = r - l - getPaddingRight();
+ if (!isFullscreen) {
+ right -= (int) Math.ceil(getUnusedHorizontalSpace() / 2f);
+ }
+
int top = getPaddingTop();
+ int bottom = b - t - getPaddingBottom();
mTouchFeedbackView.layout(left, top,
left + mTouchFeedbackView.getMeasuredWidth(),
top + mTouchFeedbackView.getMeasuredHeight());
- mShortcutsAndWidgets.layout(left, top,
- left + r - l,
- top + b - t);
+ mShortcutsAndWidgets.layout(left, top, right, bottom);
+
+ // Expand the background drawing bounds by the padding baked into the background drawable
+ mBackground.getPadding(mTempRect);
+ mBackground.setBounds(
+ left - mTempRect.left,
+ top - mTempRect.top,
+ right + mTempRect.right,
+ bottom + mTempRect.bottom);
+ }
+
+ public Rect getBackgroundBounds() {
+ return mBackground.getBounds();
}
/**
@@ -916,16 +932,6 @@
}
@Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
-
- // Expand the background drawing bounds by the padding baked into the background drawable
- mBackground.getPadding(mTempRect);
- mBackground.setBounds(-mTempRect.left, -mTempRect.top,
- w + mTempRect.right, h + mTempRect.bottom);
- }
-
- @Override
protected void setChildrenDrawingCacheEnabled(boolean enabled) {
mShortcutsAndWidgets.setChildrenDrawingCacheEnabled(enabled);
}
@@ -2708,7 +2714,7 @@
/**
* Indicates whether this item can be reordered. Always true except in the case of the
- * the AllApps button.
+ * the AllApps button and QSB place holder.
*/
public boolean canReorder = true;
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 8d11aaa..4a550ed 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -70,7 +70,10 @@
public final Rect defaultWidgetPadding;
private final int pageIndicatorHeightPx;
private final int defaultPageSpacingPx;
+ private final int topWorkspacePadding;
private float dragViewScale;
+ public float workspaceSpringLoadShrinkFactor;
+ public final int workspaceSpringLoadedBottomSpace;
// Workspace icons
public int iconSizePx;
@@ -92,8 +95,7 @@
public int hotseatCellWidthPx;
public int hotseatCellHeightPx;
public int hotseatIconSizePx;
- private int normalHotseatBarHeightPx, shortHotseatBarHeightPx;
- private int hotseatBarHeightPx; // One of the above.
+ private int hotseatBarHeightPx;
// All apps
public int allAppsNumCols;
@@ -102,15 +104,8 @@
public final int allAppsIconSizePx;
public final float allAppsIconTextSizeSp;
- // QSB
- private int searchBarWidgetInternalPaddingTop, searchBarWidgetInternalPaddingBottom;
- private int searchBarTopPaddingPx;
- private int tallSearchBarNegativeTopPaddingPx, normalSearchBarTopExtraPaddingPx;
- private int searchBarTopExtraPaddingPx; // One of the above.
- private int normalSearchBarBottomPaddingPx, tallSearchBarBottomPaddingPx;
- private int searchBarBottomPaddingPx; // One of the above.
- private int normalSearchBarSpaceHeightPx, tallSearchBarSpaceHeightPx;
- private int searchBarSpaceHeightPx; // One of the above.
+ // Drop Target
+ public int dropTargetBarSizePx;
public DeviceProfile(Context context, InvariantDeviceProfile inv,
Point minSize, Point maxSize,
@@ -140,6 +135,8 @@
res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height);
defaultPageSpacingPx =
res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
+ topWorkspacePadding =
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_top_padding);
overviewModeMinIconZoneHeightPx =
res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_min_icon_zone_height);
overviewModeMaxIconZoneHeightPx =
@@ -152,6 +149,9 @@
res.getInteger(R.integer.config_dynamic_grid_overview_icon_zone_percentage) / 100f;
iconDrawablePaddingOriginalPx =
res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
+ dropTargetBarSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_drop_target_size);
+ workspaceSpringLoadedBottomSpace =
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space);
// AllApps uses the original non-scaled icon text size
allAppsIconTextSizeSp = inv.iconTextSize;
@@ -195,7 +195,7 @@
float usedHeight = (cellHeightPx * inv.numRows);
// We only care about the top and bottom workspace padding, which is not affected by RTL.
- Rect workspacePadding = getWorkspacePadding(false /* isLayoutRtl */);
+ Rect workspacePadding = getWorkspacePadding();
int maxHeight = (availableHeightPx - workspacePadding.top - workspacePadding.bottom);
if (usedHeight > maxHeight) {
scale = maxHeight / usedHeight;
@@ -211,33 +211,6 @@
iconDrawablePaddingPx = drawablePadding;
hotseatIconSizePx = (int) (Utilities.pxFromDp(inv.hotseatIconSize, dm) * scale);
- // Search Bar
- normalSearchBarSpaceHeightPx = res.getDimensionPixelSize(
- R.dimen.dynamic_grid_search_bar_height);
- tallSearchBarSpaceHeightPx = res.getDimensionPixelSize(
- R.dimen.dynamic_grid_search_bar_height_tall);
- searchBarWidgetInternalPaddingTop = res.getDimensionPixelSize(
- R.dimen.qsb_internal_padding_top);
- searchBarWidgetInternalPaddingBottom = res.getDimensionPixelSize(
- R.dimen.qsb_internal_padding_bottom);
- normalSearchBarTopExtraPaddingPx = res.getDimensionPixelSize(
- R.dimen.dynamic_grid_search_bar_extra_top_padding);
- tallSearchBarNegativeTopPaddingPx = res.getDimensionPixelSize(
- R.dimen.dynamic_grid_search_bar_negative_top_padding_short);
- if (isTablet && !isVerticalBarLayout()) {
- searchBarTopPaddingPx = searchBarWidgetInternalPaddingTop;
- normalSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom +
- res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_bottom_padding_tablet);
- tallSearchBarBottomPaddingPx = normalSearchBarBottomPaddingPx;
- } else {
- searchBarTopPaddingPx = searchBarWidgetInternalPaddingTop;
- normalSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom +
- res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_bottom_padding);
- tallSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom
- + res.getDimensionPixelSize(
- R.dimen.dynamic_grid_search_bar_bottom_negative_padding_short);
- }
-
// Calculate the actual text height
Paint textPaint = new Paint();
textPaint.setTextSize(iconTextSizePx);
@@ -249,11 +222,22 @@
dragViewScale = (iconSizePx + scaleDps) / iconSizePx;
// Hotseat
- normalHotseatBarHeightPx = iconSizePx + 4 * edgeMarginPx;
- shortHotseatBarHeightPx = iconSizePx + 2 * edgeMarginPx;
+ hotseatBarHeightPx = iconSizePx + 4 * edgeMarginPx;
hotseatCellWidthPx = iconSizePx;
hotseatCellHeightPx = iconSizePx;
+ if (!isVerticalBarLayout()) {
+ int expectedWorkspaceHeight = availableHeightPx - hotseatBarHeightPx
+ - pageIndicatorHeightPx - topWorkspacePadding;
+ float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace;
+ workspaceSpringLoadShrinkFactor = Math.min(
+ res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f,
+ 1 - (minRequiredHeight / expectedWorkspaceHeight));
+ } else {
+ workspaceSpringLoadShrinkFactor =
+ res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
+ }
+
// Folder
int folderCellPadding = isTablet || isLandscape ? 6 * edgeMarginPx : 3 * edgeMarginPx;
// Don't let the folder get too close to the edges of the screen.
@@ -281,60 +265,33 @@
allAppsNumPredictiveCols = numPredictiveAppCols;
}
- /** Returns the amount of extra space to allocate to the search bar for vertical padding. */
- private int getSearchBarTotalVerticalPadding() {
- return searchBarTopPaddingPx + searchBarTopExtraPaddingPx + searchBarBottomPaddingPx;
- }
-
/** Returns the width and height of the search bar, ignoring any padding. */
- public Point getSearchBarDimensForWidgetOpts(Resources res) {
- Rect searchBarBounds = getSearchBarBounds(Utilities.isRtl(res));
+ public Point getSearchBarDimensForWidgetOpts() {
if (isVerticalBarLayout()) {
- return new Point(searchBarBounds.width(), searchBarBounds.height());
- }
- int widgetInternalPadding = searchBarWidgetInternalPaddingTop +
- searchBarWidgetInternalPaddingBottom;
- return new Point(searchBarBounds.width(), searchBarSpaceHeightPx + widgetInternalPadding);
- }
-
- /** Returns the search bar bounds in the current orientation */
- public Rect getSearchBarBounds(boolean isLayoutRtl) {
- Rect bounds = new Rect();
- if (isVerticalBarLayout()) {
- if (isLayoutRtl) {
- bounds.set(availableWidthPx - normalSearchBarSpaceHeightPx, edgeMarginPx,
- availableWidthPx, availableHeightPx - edgeMarginPx);
- } else {
- bounds.set(0, edgeMarginPx, normalSearchBarSpaceHeightPx,
- availableHeightPx - edgeMarginPx);
- }
+ return new Point(dropTargetBarSizePx, availableHeightPx - 2 * edgeMarginPx);
} else {
- int boundsBottom = searchBarSpaceHeightPx + getSearchBarTotalVerticalPadding();
+ int gap;
if (isTablet) {
// Pad the left and right of the workspace to ensure consistent spacing
// between all icons
int width = getCurrentWidth();
// XXX: If the icon size changes across orientations, we will have to take
// that into account here too.
- int gap = (int) ((width - 2 * edgeMarginPx -
- (inv.numColumns * cellWidthPx)) / (2 * (inv.numColumns + 1)));
- bounds.set(edgeMarginPx + gap, 0,
- availableWidthPx - (edgeMarginPx + gap), boundsBottom);
+ gap = ((width - 2 * edgeMarginPx
+ - (inv.numColumns * cellWidthPx)) / (2 * (inv.numColumns + 1)))
+ + edgeMarginPx;
} else {
- bounds.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left,
- 0,
- availableWidthPx - (desiredWorkspaceLeftRightMarginPx -
- defaultWidgetPadding.right), boundsBottom);
+ gap = desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right;
}
+ return new Point(availableWidthPx - 2 * gap, dropTargetBarSizePx);
}
- return bounds;
}
public Point getCellSize() {
Point result = new Point();
// Since we are only concerned with the overall padding, layout direction does
// not matter.
- Rect padding = getWorkspacePadding(false /* isLayoutRtl */ );
+ Rect padding = getWorkspacePadding();
result.x = calculateCellWidth(availableWidthPx - padding.left - padding.right,
inv.numColumns);
result.y = calculateCellHeight(availableHeightPx - padding.top - padding.bottom,
@@ -343,20 +300,13 @@
}
/** Returns the workspace padding in the specified orientation */
- public Rect getWorkspacePadding(boolean isLayoutRtl) {
- Rect searchBarBounds = getSearchBarBounds(isLayoutRtl);
+ public Rect getWorkspacePadding() {
Rect padding = new Rect();
if (isVerticalBarLayout()) {
- // Pad the left and right of the workspace with search/hotseat bar sizes
- if (isLayoutRtl) {
- padding.set(normalHotseatBarHeightPx, edgeMarginPx,
- searchBarBounds.width(), edgeMarginPx);
- } else {
- padding.set(searchBarBounds.width(), edgeMarginPx,
- normalHotseatBarHeightPx, edgeMarginPx);
- }
+ // in case of isVerticalBarLayout, the hotseat is always on the right and the drop
+ // target bar is on the left, independent of the layout direction.
+ padding.set(dropTargetBarSizePx, edgeMarginPx, hotseatBarHeightPx, edgeMarginPx);
} else {
- int paddingTop = searchBarBounds.bottom;
int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx;
if (isTablet) {
// Pad the left and right of the workspace to ensure consistent spacing
@@ -369,14 +319,14 @@
((inv.numColumns - 1) * gapScale * cellWidthPx)));
availablePaddingX = (int) Math.min(availablePaddingX,
width * MAX_HORIZONTAL_PADDING_PERCENT);
- int availablePaddingY = Math.max(0, height - paddingTop - paddingBottom
+ int availablePaddingY = Math.max(0, height - topWorkspacePadding - paddingBottom
- (int) (2 * inv.numRows * cellHeightPx));
- padding.set(availablePaddingX / 2, paddingTop + availablePaddingY / 2,
+ padding.set(availablePaddingX / 2, topWorkspacePadding + availablePaddingY / 2,
availablePaddingX / 2, paddingBottom + availablePaddingY / 2);
} else {
// Pad the top and bottom of the workspace with search/hotseat bar sizes
padding.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left,
- paddingTop,
+ topWorkspacePadding,
desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right,
paddingBottom);
}
@@ -384,7 +334,7 @@
return padding;
}
- private int getWorkspacePageSpacing(boolean isLayoutRtl) {
+ private int getWorkspacePageSpacing() {
if (isVerticalBarLayout() || isLargeTablet) {
// In landscape mode the page spacing is set to the default.
return defaultPageSpacingPx;
@@ -392,7 +342,7 @@
// In portrait, we want the pages spaced such that there is no
// overhang of the previous / next page into the current page viewport.
// We assume symmetrical padding in portrait mode.
- return Math.max(defaultPageSpacingPx, 2 * getWorkspacePadding(isLayoutRtl).left);
+ return Math.max(defaultPageSpacingPx, 2 * getWorkspacePadding().left);
}
}
@@ -444,50 +394,28 @@
return visibleChildren;
}
- // TODO(twickham): b/25154513
- public void setSearchBarHeight(int searchBarHeight) {
- if (searchBarHeight == LauncherCallbacks.SEARCH_BAR_HEIGHT_TALL) {
- hotseatBarHeightPx = shortHotseatBarHeightPx;
- searchBarSpaceHeightPx = tallSearchBarSpaceHeightPx;
- searchBarBottomPaddingPx = tallSearchBarBottomPaddingPx;
- searchBarTopExtraPaddingPx = isPhone ? tallSearchBarNegativeTopPaddingPx
- : normalSearchBarTopExtraPaddingPx;
- } else {
- hotseatBarHeightPx = normalHotseatBarHeightPx;
- searchBarSpaceHeightPx = normalSearchBarSpaceHeightPx;
- searchBarBottomPaddingPx = normalSearchBarBottomPaddingPx;
- searchBarTopExtraPaddingPx = normalSearchBarTopExtraPaddingPx;
- }
- }
-
public void layout(Launcher launcher) {
FrameLayout.LayoutParams lp;
boolean hasVerticalBarLayout = isVerticalBarLayout();
final boolean isLayoutRtl = Utilities.isRtl(launcher.getResources());
// Layout the search bar space
- Rect searchBarBounds = getSearchBarBounds(isLayoutRtl);
- View searchBar = launcher.getSearchDropTargetBar();
+ Point searchBarBounds = getSearchBarDimensForWidgetOpts();
+ View searchBar = launcher.getDropTargetBar();
lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
- lp.width = searchBarBounds.width();
- lp.height = searchBarBounds.height();
- lp.topMargin = searchBarTopExtraPaddingPx;
+ lp.width = searchBarBounds.x;
+ lp.height = searchBarBounds.y;
+ lp.topMargin = edgeMarginPx;
searchBar.setLayoutParams(lp);
- // Layout the app info bar space
- View appInfoBar = launcher.getAppInfoDropTargetBar();
- lp = (FrameLayout.LayoutParams) appInfoBar.getLayoutParams();
- lp.bottomMargin = hotseatBarHeightPx;
- appInfoBar.setLayoutParams(lp);
-
// Layout the workspace
PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace);
lp = (FrameLayout.LayoutParams) workspace.getLayoutParams();
lp.gravity = Gravity.CENTER;
- Rect padding = getWorkspacePadding(isLayoutRtl);
+ Rect padding = getWorkspacePadding();
workspace.setLayoutParams(lp);
workspace.setPadding(padding.left, padding.top, padding.right, padding.bottom);
- workspace.setPageSpacing(getWorkspacePageSpacing(isLayoutRtl));
+ workspace.setPageSpacing(getWorkspacePageSpacing());
// Layout the hotseat
View hotseat = launcher.findViewById(R.id.hotseat);
@@ -503,7 +431,7 @@
// Vertical hotseat -- The hotseat is fixed in the layout to be on the right of the
// screen regardless of RTL
lp.gravity = Gravity.RIGHT;
- lp.width = normalHotseatBarHeightPx;
+ lp.width = hotseatBarHeightPx;
lp.height = LayoutParams.MATCH_PARENT;
hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx);
} else if (isTablet) {
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
new file mode 100644
index 0000000..5966af5
--- /dev/null
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewPropertyAnimator;
+import android.view.accessibility.AccessibilityManager;
+import android.view.animation.AccelerateInterpolator;
+import android.widget.LinearLayout;
+
+import com.android.launcher3.dragndrop.DragController;
+
+/*
+ * The top bar containing various drop targets: Delete/App Info/Uninstall.
+ */
+public class DropTargetBar extends LinearLayout implements DragController.DragListener {
+
+ protected static final int DEFAULT_DRAG_FADE_DURATION = 175;
+ protected static final TimeInterpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator();
+
+ private final Runnable mFadeAnimationEndRunnable = new Runnable() {
+
+ @Override
+ public void run() {
+ AccessibilityManager am = (AccessibilityManager)
+ getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+ boolean accessibilityEnabled = am.isEnabled();
+ AlphaUpdateListener.updateVisibility(DropTargetBar.this, accessibilityEnabled);
+ }
+ };
+
+ @ViewDebug.ExportedProperty(category = "launcher")
+ protected boolean mDeferOnDragEnd;
+
+ @ViewDebug.ExportedProperty(category = "launcher")
+ protected boolean mVisible = false;
+
+ private ViewPropertyAnimator mCurrentAnimation;
+
+ // Drop targets
+ private ButtonDropTarget mDeleteDropTarget;
+ private ButtonDropTarget mAppInfoDropTarget;
+ private ButtonDropTarget mUninstallDropTarget;
+
+ public DropTargetBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public DropTargetBar(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ // Get the individual components
+ mDeleteDropTarget = (ButtonDropTarget) findViewById(R.id.delete_target_text);
+ mAppInfoDropTarget = (ButtonDropTarget) findViewById(R.id.info_target_text);
+ mUninstallDropTarget = (ButtonDropTarget) findViewById(R.id.uninstall_target_text);
+
+ mDeleteDropTarget.setDropTargetBar(this);
+ mAppInfoDropTarget.setDropTargetBar(this);
+ mUninstallDropTarget.setDropTargetBar(this);
+
+ // Initialize with hidden state
+ setAlpha(0f);
+ }
+
+ public void setup(DragController dragController) {
+ dragController.addDragListener(this);
+ dragController.setFlingToDeleteDropTarget(mDeleteDropTarget);
+
+ dragController.addDragListener(mDeleteDropTarget);
+ dragController.addDragListener(mAppInfoDropTarget);
+ dragController.addDragListener(mUninstallDropTarget);
+
+ dragController.addDropTarget(mDeleteDropTarget);
+ dragController.addDropTarget(mAppInfoDropTarget);
+ dragController.addDropTarget(mUninstallDropTarget);
+ }
+
+ private void animateToVisibility(boolean isVisible) {
+ if (mVisible != isVisible) {
+ mVisible = isVisible;
+
+ // Cancel any existing animation
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.cancel();
+ mCurrentAnimation = null;
+ }
+
+ float finalAlpha = mVisible ? 1 : 0;
+ if (Float.compare(getAlpha(), finalAlpha) != 0) {
+ setVisibility(View.VISIBLE);
+ mCurrentAnimation = animate().alpha(finalAlpha)
+ .setInterpolator(DEFAULT_INTERPOLATOR)
+ .setDuration(DEFAULT_DRAG_FADE_DURATION)
+ .withEndAction(mFadeAnimationEndRunnable);
+ }
+
+ }
+ }
+
+ public void enableAccessibleDrag(boolean enable) {
+ mDeleteDropTarget.enableAccessibleDrag(enable);
+ mAppInfoDropTarget.enableAccessibleDrag(enable);
+ mUninstallDropTarget.enableAccessibleDrag(enable);
+ }
+
+ /*
+ * DragController.DragListener implementation
+ */
+ @Override
+ public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
+ animateToVisibility(true);
+ }
+
+ /**
+ * This is called to defer hiding the delete drop target until the drop animation has completed,
+ * instead of hiding immediately when the drag has ended.
+ */
+ protected void deferOnDragEnd() {
+ mDeferOnDragEnd = true;
+ }
+
+ @Override
+ public void onDragEnd() {
+ if (!mDeferOnDragEnd) {
+ animateToVisibility(false);
+ } else {
+ mDeferOnDragEnd = false;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index f99c08a..0b9e4ac 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -356,7 +356,7 @@
CellLayout iconLayout = (CellLayout) parent.getParent();
final Workspace workspace = (Workspace) iconLayout.getParent();
final ViewGroup dragLayer = (ViewGroup) workspace.getParent();
- final ViewGroup tabs = (ViewGroup) dragLayer.findViewById(R.id.search_drop_target_bar);
+ final ViewGroup tabs = (ViewGroup) dragLayer.findViewById(R.id.drop_target_bar);
final Hotseat hotseat = (Hotseat) dragLayer.findViewById(R.id.hotseat);
final ItemInfo itemInfo = (ItemInfo) v.getTag();
diff --git a/src/com/android/launcher3/FocusIndicatorView.java b/src/com/android/launcher3/FocusIndicatorView.java
deleted file mode 100644
index a835d99..0000000
--- a/src/com/android/launcher3/FocusIndicatorView.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.animation.Animator;
-import android.animation.PropertyValuesHolder;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.util.AttributeSet;
-import android.util.Pair;
-import android.view.View;
-
-import com.android.launcher3.util.Thunk;
-
-public class FocusIndicatorView extends View implements View.OnFocusChangeListener {
-
- // It can be any number >0. The view is resized using scaleX and scaleY.
- static final int DEFAULT_LAYOUT_SIZE = 100;
-
- private static final float MIN_VISIBLE_ALPHA = 0.2f;
- private static final long ANIM_DURATION = 150;
-
- private final int[] mIndicatorPos = new int[2];
- private final int[] mTargetViewPos = new int[2];
-
- private Animator mCurrentAnimation;
- private ViewAnimState mTargetState;
-
- private View mLastFocusedView;
- private boolean mInitiated;
- private final OnFocusChangeListener mHideIndicatorOnFocusListener;
-
- private Pair<View, Boolean> mPendingCall;
-
- public FocusIndicatorView(Context context) {
- this(context, null);
- }
-
- public FocusIndicatorView(Context context, AttributeSet attrs) {
- super(context, attrs);
- setAlpha(0);
- setBackgroundColor(getResources().getColor(R.color.focused_background));
-
- mHideIndicatorOnFocusListener = new OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (hasFocus) {
- endCurrentAnimation();
- setAlpha(0);
- }
- }
- };
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
-
- // Redraw if it is already showing. This avoids a bug where the height changes by a small
- // amount on connecting/disconnecting a bluetooth keyboard.
- if (mLastFocusedView != null) {
- mPendingCall = Pair.create(mLastFocusedView, Boolean.TRUE);
- invalidate();
- }
- }
-
- /**
- * Sets the alpha of this FocusIndicatorView to 0 when a view with this listener receives focus.
- */
- public View.OnFocusChangeListener getHideIndicatorOnFocusListener() {
- return mHideIndicatorOnFocusListener;
- }
-
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- mPendingCall = null;
- if (!mInitiated && (getWidth() == 0)) {
- // View not yet laid out. Wait until the view is ready to be drawn, so that be can
- // get the location on screen.
- mPendingCall = Pair.create(v, hasFocus);
- invalidate();
- return;
- }
-
- if (!mInitiated) {
- // The parent view should always the a parent of the target view.
- computeLocationRelativeToParent(this, (View) getParent(), mIndicatorPos);
- mInitiated = true;
- }
-
- if (hasFocus) {
- int indicatorWidth = getWidth();
- int indicatorHeight = getHeight();
-
- endCurrentAnimation();
- ViewAnimState nextState = new ViewAnimState();
- nextState.scaleX = v.getScaleX() * v.getWidth() / indicatorWidth;
- nextState.scaleY = v.getScaleY() * v.getHeight() / indicatorHeight;
-
- computeLocationRelativeToParent(v, (View) getParent(), mTargetViewPos);
- nextState.x = mTargetViewPos[0] - mIndicatorPos[0] - (1 - nextState.scaleX) * indicatorWidth / 2;
- nextState.y = mTargetViewPos[1] - mIndicatorPos[1] - (1 - nextState.scaleY) * indicatorHeight / 2;
-
- if (getAlpha() > MIN_VISIBLE_ALPHA) {
- mTargetState = nextState;
- mCurrentAnimation = new LauncherViewPropertyAnimator(this)
- .alpha(1)
- .translationX(mTargetState.x)
- .translationY(mTargetState.y)
- .scaleX(mTargetState.scaleX)
- .scaleY(mTargetState.scaleY);
- } else {
- applyState(nextState);
- mCurrentAnimation = LauncherAnimUtils.ofPropertyValuesHolder(this,
- PropertyValuesHolder.ofFloat(View.ALPHA, 1));
- }
- mLastFocusedView = v;
- } else {
- if (mLastFocusedView == v) {
- mLastFocusedView = null;
- endCurrentAnimation();
- mCurrentAnimation = LauncherAnimUtils.ofPropertyValuesHolder(this,
- PropertyValuesHolder.ofFloat(View.ALPHA, 0));
- }
- }
- if (mCurrentAnimation != null) {
- mCurrentAnimation.setDuration(ANIM_DURATION).start();
- }
- }
-
- private void endCurrentAnimation() {
- if (mCurrentAnimation != null) {
- mCurrentAnimation.cancel();
- mCurrentAnimation = null;
- }
- if (mTargetState != null) {
- applyState(mTargetState);
- mTargetState = null;
- }
- }
-
- private void applyState(ViewAnimState state) {
- setTranslationX(state.x);
- setTranslationY(state.y);
- setScaleX(state.scaleX);
- setScaleY(state.scaleY);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- if (mPendingCall != null) {
- onFocusChange(mPendingCall.first, mPendingCall.second);
- }
- }
-
- /**
- * Computes the location of a view relative to {@param parent}, off-setting
- * any shift due to page view scroll.
- * @param pos an array of two integers in which to hold the coordinates
- */
- private static void computeLocationRelativeToParent(View v, View parent, int[] pos) {
- pos[0] = pos[1] = 0;
- computeLocationRelativeToParentHelper(v, parent, pos);
-
- // If a view is scaled, its position will also shift accordingly. For optimization, only
- // consider this for the last node.
- pos[0] += (1 - v.getScaleX()) * v.getWidth() / 2;
- pos[1] += (1 - v.getScaleY()) * v.getHeight() / 2;
- }
-
- private static void computeLocationRelativeToParentHelper(View child,
- View commonParent, int[] shift) {
- View parent = (View) child.getParent();
- shift[0] += child.getLeft();
- shift[1] += child.getTop();
- if (parent instanceof PagedView) {
- PagedView page = (PagedView) parent;
- shift[0] -= page.getScrollForPage(page.indexOfChild(child));
- }
-
- if (parent != commonParent) {
- computeLocationRelativeToParentHelper(parent, commonParent, shift);
- }
- }
-
- @Thunk static final class ViewAnimState {
- float x, y, scaleX, scaleY;
- }
-}
diff --git a/src/com/android/launcher3/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java
index f4bfa45..61edc0f 100644
--- a/src/com/android/launcher3/InsettableFrameLayout.java
+++ b/src/com/android/launcher3/InsettableFrameLayout.java
@@ -9,6 +9,9 @@
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.config.FeatureFlags;
+
public class InsettableFrameLayout extends FrameLayout implements
ViewGroup.OnHierarchyChangeListener, Insettable {
@@ -31,6 +34,9 @@
lp.rightMargin += (newInsets.right - oldInsets.right);
lp.bottomMargin += (newInsets.bottom - oldInsets.bottom);
}
+ if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && child instanceof AllAppsContainerView) {
+ lp.setMargins(0, 0, 0, lp.bottomMargin);
+ }
child.setLayoutParams(lp);
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 13e451a..a504250 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -45,20 +45,16 @@
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
@@ -95,7 +91,9 @@
import android.widget.Toast;
import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.allapps.DefaultAppSearchController;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherActivityInfoCompat;
@@ -110,14 +108,14 @@
import com.android.launcher3.dynamicui.ExtractedColors;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.keyboard.ViewGroupFocusHelper;
+import com.android.launcher3.logging.FileLog;
import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.WidgetsModel;
-import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.pageindicators.PageIndicatorLine;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.logging.FileLog;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.TestingUtils;
import com.android.launcher3.util.Thunk;
@@ -183,6 +181,9 @@
static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
"com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
+ public static final String ACTION_APPWIDGET_HOST_RESET =
+ "com.android.launcher3.intent.ACTION_APPWIDGET_HOST_RESET";
+
// Type: int
private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
// Type: int
@@ -197,9 +198,6 @@
static final String INTRO_SCREEN_DISMISSED = "launcher.intro_screen_dismissed";
static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed";
- private static final String QSB_WIDGET_ID = "qsb_widget_id";
- private static final String QSB_WIDGET_PROVIDER = "qsb_widget_provider";
-
/** The different states that Launcher can be in. */
enum State { NONE, WORKSPACE, WORKSPACE_SPRING_LOADED, APPS, APPS_SPRING_LOADED,
WIDGETS, WIDGETS_SPRING_LOADED }
@@ -219,8 +217,19 @@
private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
@Thunk static int NEW_APPS_ANIMATION_DELAY = 500;
- private final BroadcastReceiver mCloseSystemDialogsReceiver
- = new CloseSystemDialogsIntentReceiver();
+ private final BroadcastReceiver mUiBroadcastReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
+ closeSystemDialogs();
+ } else if (ACTION_APPWIDGET_HOST_RESET.equals(intent.getAction())) {
+ if (mAppWidgetHost != null) {
+ mAppWidgetHost.startListening();
+ }
+ }
+ }
+ };
@Thunk Workspace mWorkspace;
private View mLauncherView;
@@ -245,18 +254,16 @@
private View mAllAppsButton;
private View mWidgetsButton;
- private SearchDropTargetBar mSearchDropTargetBar;
- private AppInfoDropTargetBar mAppInfoDropTargetBar;
+ private DropTargetBar mDropTargetBar;
// Main container view for the all apps screen.
@Thunk AllAppsContainerView mAppsView;
+ AllAppsTransitionController mAllAppsController;
// Main container view and the model for the widget tray screen.
@Thunk WidgetsContainerView mWidgetsView;
@Thunk WidgetsModel mWidgetsModel;
- private AppWidgetHostView mQsb;
-
private Bundle mSavedState;
// We set the state in both onCreate and then onNewIntent in some cases, which causes both
// scroll issues (because the workspace may not have been measured yet) and extra work.
@@ -358,7 +365,7 @@
private UserEventDispatcher mUserEventDispatcher;
- public FocusIndicatorView mFocusHandler;
+ public ViewGroupFocusHelper mFocusHandler;
private boolean mRotationEnabled = false;
@Thunk void setOrientation() {
@@ -409,7 +416,8 @@
mIconCache = app.getIconCache();
mDragController = new DragController(this);
- mStateTransitionAnimation = new LauncherStateTransitionAnimation(this);
+ mAllAppsController = new AllAppsTransitionController(this);
+ mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, mAllAppsController);
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
@@ -428,8 +436,6 @@
setContentView(R.layout.launcher);
- app.getInvariantDeviceProfile().landscapeProfile.setSearchBarHeight(getSearchBarHeight());
- app.getInvariantDeviceProfile().portraitProfile.setSearchBarHeight(getSearchBarHeight());
setupViews();
mDeviceProfile.layout(this);
mExtractedColors = new ExtractedColors();
@@ -464,7 +470,8 @@
Selection.setSelection(mDefaultKeySsb, 0);
IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- registerReceiver(mCloseSystemDialogsReceiver, filter);
+ filter.addAction(ACTION_APPWIDGET_HOST_RESET);
+ registerReceiver(mUiBroadcastReceiver, filter);
mRotationEnabled = getResources().getBoolean(R.bool.allow_rotation);
// In case we are on a device with locked rotation, we should look at preferences to check
@@ -739,10 +746,6 @@
} else if (resultCode == RESULT_OK) {
addAppWidgetImpl(appWidgetId, mPendingAddInfo, null,
mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
-
- // When the user has granted permission to bind widgets, we should check to see if
- // we can inflate the default search bar widget.
- getOrCreateQsbBar();
}
return;
} else if (requestCode == REQUEST_PICK_WALLPAPER) {
@@ -879,7 +882,7 @@
sPendingAddItem = null;
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- startActivity(v, intent, null);
+ startActivitySafely(v, intent, null);
} else {
// TODO: Show a snack bar with link to settings
Toast.makeText(this, getString(R.string.msg_no_phone_permission,
@@ -1056,7 +1059,6 @@
if (!isWorkspaceLoading()) {
getWorkspace().reinflateWidgetsIfNecessary();
}
- reinflateQSBIfNecessary();
if (DEBUG_RESUME_TIME) {
Log.d(TAG, "Time spent in onResume: " + (System.currentTimeMillis() - startTime));
@@ -1339,11 +1341,10 @@
* Finds all the views we need and configure them properly.
*/
private void setupViews() {
- final DragController dragController = mDragController;
-
mLauncherView = findViewById(R.id.launcher);
- mFocusHandler = (FocusIndicatorView) findViewById(R.id.focus_indicator);
mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
+ mFocusHandler = mDragLayer.getFocusIndicatorHelper();
+
mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
mPageIndicator = (PageIndicatorLine) mDragLayer.findViewById(R.id.page_indicator);
@@ -1353,7 +1354,8 @@
mWorkspaceBackgroundDrawable = getResources().getDrawable(R.drawable.workspace_bg);
// Setup the drag layer
- mDragLayer.setup(this, dragController);
+
+ mDragLayer.setup(this, mDragController, mAllAppsController);
// Setup the hotseat
mHotseat = (Hotseat) findViewById(R.id.hotseat);
@@ -1367,15 +1369,12 @@
// Setup the workspace
mWorkspace.setHapticFeedbackEnabled(false);
mWorkspace.setOnLongClickListener(this);
- mWorkspace.setup(dragController);
- dragController.addDragListener(mWorkspace);
+ mWorkspace.setup(mDragController);
+ mWorkspace.bindAndInitFirstWorkspaceScreen(null /* recycled qsb */);
+ mDragController.addDragListener(mWorkspace);
// Get the search/delete/uninstall bar
- mSearchDropTargetBar = (SearchDropTargetBar)
- mDragLayer.findViewById(R.id.search_drop_target_bar);
- // Get the app info bar
- mAppInfoDropTargetBar = (AppInfoDropTargetBar)
- mDragLayer.findViewById(R.id.app_info_drop_target_bar);
+ mDropTargetBar = (DropTargetBar) mDragLayer.findViewById(R.id.drop_target_bar);
// Setup Apps and Widgets
mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
@@ -1387,17 +1386,11 @@
}
// Setup the drag controller (drop targets have to be added in reverse order in priority)
- dragController.setDragScoller(mWorkspace);
- dragController.setScrollView(mDragLayer);
- dragController.setMoveTarget(mWorkspace);
- dragController.addDropTarget(mWorkspace);
- if (mSearchDropTargetBar != null) {
- mSearchDropTargetBar.setup(this, dragController);
- mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar());
- }
- if (mAppInfoDropTargetBar != null) {
- mAppInfoDropTargetBar.setup(this, dragController);
- }
+ mDragController.setDragScoller(mWorkspace);
+ mDragController.setScrollView(mDragLayer);
+ mDragController.setMoveTarget(mWorkspace);
+ mDragController.addDropTarget(mWorkspace);
+ mDropTargetBar.setup(mDragController);
if (TestingUtils.MEMORY_DUMP_ENABLED) {
TestingUtils.addWeightWatcher(this);
@@ -1806,12 +1799,8 @@
return mOverviewPanel;
}
- public SearchDropTargetBar getSearchDropTargetBar() {
- return mSearchDropTargetBar;
- }
-
- public AppInfoDropTargetBar getAppInfoDropTargetBar() {
- return mAppInfoDropTargetBar;
+ public DropTargetBar getDropTargetBar() {
+ return mDropTargetBar;
}
public LauncherAppWidgetHost getAppWidgetHost() {
@@ -1940,6 +1929,7 @@
if (mWorkspace.getChildCount() > 0) {
outState.putInt(RUNTIME_STATE_CURRENT_SCREEN,
mWorkspace.getCurrentPageOffsetFromCustomContent());
+
}
super.onSaveInstanceState(outState);
@@ -1999,7 +1989,7 @@
((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
.removeAccessibilityStateChangeListener(this);
- unregisterReceiver(mCloseSystemDialogsReceiver);
+ unregisterReceiver(mUiBroadcastReceiver);
LauncherAnimUtils.onDestroyActivity();
@@ -2052,10 +2042,9 @@
appSearchData = new Bundle();
appSearchData.putString("source", "launcher-search");
}
- Rect sourceBounds = new Rect();
- if (mSearchDropTargetBar != null) {
- sourceBounds = mSearchDropTargetBar.getSearchBarBounds();
- }
+
+ // TODO send proper bounds.
+ Rect sourceBounds = null;
boolean clearTextImmediately = startSearch(initialQuery, selectInitialQuery,
appSearchData, sourceBounds);
@@ -2465,19 +2454,6 @@
}
/**
- * Re-listen when widget host is reset.
- */
- @Override
- public void onAppWidgetHostReset() {
- if (mAppWidgetHost != null) {
- mAppWidgetHost.startListening();
- }
-
- // Recreate the QSB, as the widget has been reset.
- bindSearchProviderChanged();
- }
-
- /**
* Launches the intent referred by the clicked shortcut.
*
* @param v The view representing the clicked shortcut.
@@ -2679,26 +2655,13 @@
startAppShortcutOrInfoActivity(v);
}
- @Thunk void startAppShortcutOrInfoActivity(View v) {
- Object tag = v.getTag();
- final ShortcutInfo shortcut;
- final Intent intent;
- if (tag instanceof ShortcutInfo) {
- shortcut = (ShortcutInfo) tag;
- intent = shortcut.intent;
- int[] pos = new int[2];
- v.getLocationOnScreen(pos);
- intent.setSourceBounds(new Rect(pos[0], pos[1],
- pos[0] + v.getWidth(), pos[1] + v.getHeight()));
-
- } else if (tag instanceof AppInfo) {
- shortcut = null;
- intent = ((AppInfo) tag).intent;
- } else {
- throw new IllegalArgumentException("Input must be a Shortcut or AppInfo");
+ private void startAppShortcutOrInfoActivity(View v) {
+ ItemInfo item = (ItemInfo) v.getTag();
+ Intent intent = item.getIntent();
+ if (intent == null) {
+ throw new IllegalArgumentException("Input must have a valid intent");
}
-
- boolean success = startActivitySafely(v, intent, tag);
+ boolean success = startActivitySafely(v, intent, item);
getUserEventDispatcher().logAppLaunch(v, intent);
if (success && v instanceof BubbleTextView) {
@@ -2838,102 +2801,109 @@
}
}
- private boolean startActivity(View v, Intent intent, Object tag) {
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
try {
- // Only launch using the new animation if the shortcut has not opted out (this is a
- // private contract between launcher and may be ignored in the future).
- boolean useLaunchAnimation = (v != null) &&
- !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
- LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
- UserManagerCompat userManager = UserManagerCompat.getInstance(this);
-
- UserHandleCompat user = null;
- if (intent.hasExtra(AppInfo.EXTRA_PROFILE)) {
- long serialNumber = intent.getLongExtra(AppInfo.EXTRA_PROFILE, -1);
- user = userManager.getUserForSerialNumber(serialNumber);
- }
-
- Bundle optsBundle = null;
- if (useLaunchAnimation) {
- ActivityOptions opts = null;
- if (Utilities.ATLEAST_MARSHMALLOW) {
- int left = 0, top = 0;
- int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
- if (v instanceof TextView) {
- // Launch from center of icon, not entire view
- Drawable icon = Workspace.getTextViewIcon((TextView) v);
- if (icon != null) {
- Rect bounds = icon.getBounds();
- left = (width - bounds.width()) / 2;
- top = v.getPaddingTop();
- width = bounds.width();
- height = bounds.height();
- }
- }
- opts = ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
- } else if (!Utilities.ATLEAST_LOLLIPOP) {
- // Below L, we use a scale up animation
- opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
- v.getMeasuredWidth(), v.getMeasuredHeight());
- } else if (Utilities.ATLEAST_LOLLIPOP_MR1) {
- // On L devices, we use the device default slide-up transition.
- // On L MR1 devices, we a custom version of the slide-up transition which
- // doesn't have the delay present in the device default.
- opts = ActivityOptions.makeCustomAnimation(this,
- R.anim.task_open_enter, R.anim.no_anim);
- }
- optsBundle = opts != null ? opts.toBundle() : null;
- }
-
- if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
+ StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
+ try {
+ // Temporarily disable deathPenalty on all default checks. For eg, shortcuts
+ // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
+ // is enabled by default on NYC.
+ StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
+ .penaltyLog().build());
// Could be launching some bookkeeping activity
startActivity(intent, optsBundle);
- } else {
- // TODO Component can be null when shortcuts are supported for secondary user
- launcherApps.startActivityForProfile(intent.getComponent(), user,
- intent.getSourceBounds(), optsBundle);
+ } finally {
+ StrictMode.setVmPolicy(oldPolicy);
}
- return true;
} catch (SecurityException e) {
- if (Utilities.ATLEAST_MARSHMALLOW && tag instanceof ItemInfo) {
- // Due to legacy reasons, direct call shortcuts require Launchers to have the
- // corresponding permission. Show the appropriate permission prompt if that
- // is the case.
- if (intent.getComponent() == null
- && Intent.ACTION_CALL.equals(intent.getAction())
- && checkSelfPermission(Manifest.permission.CALL_PHONE) !=
- PackageManager.PERMISSION_GRANTED) {
- // TODO: Rename sPendingAddItem to a generic name.
- sPendingAddItem = preparePendingAddArgs(REQUEST_PERMISSION_CALL_PHONE, intent,
- 0, (ItemInfo) tag);
- requestPermissions(new String[]{Manifest.permission.CALL_PHONE},
- REQUEST_PERMISSION_CALL_PHONE);
- return false;
- }
+ // Due to legacy reasons, direct call shortcuts require Launchers to have the
+ // corresponding permission. Show the appropriate permission prompt if that
+ // is the case.
+ if (intent.getComponent() == null
+ && Intent.ACTION_CALL.equals(intent.getAction())
+ && checkSelfPermission(Manifest.permission.CALL_PHONE) !=
+ PackageManager.PERMISSION_GRANTED) {
+ // TODO: Rename sPendingAddItem to a generic name.
+ sPendingAddItem = preparePendingAddArgs(REQUEST_PERMISSION_CALL_PHONE, intent,
+ 0, info);
+ requestPermissions(new String[]{Manifest.permission.CALL_PHONE},
+ REQUEST_PERMISSION_CALL_PHONE);
+ } else {
+ // No idea why this was thrown.
+ throw e;
}
- Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- Log.e(TAG, "Launcher does not have the permission to launch " + intent +
- ". Make sure to create a MAIN intent-filter for the corresponding activity " +
- "or use the exported attribute for this activity. "
- + "tag="+ tag + " intent=" + intent, e);
}
- return false;
}
- public boolean startActivitySafely(View v, Intent intent, Object tag) {
- boolean success = false;
+ private Bundle getActivityLaunchOptions(View v) {
+ if (Utilities.ATLEAST_MARSHMALLOW) {
+ int left = 0, top = 0;
+ int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
+ if (v instanceof TextView) {
+ // Launch from center of icon, not entire view
+ Drawable icon = Workspace.getTextViewIcon((TextView) v);
+ if (icon != null) {
+ Rect bounds = icon.getBounds();
+ left = (width - bounds.width()) / 2;
+ top = v.getPaddingTop();
+ width = bounds.width();
+ height = bounds.height();
+ }
+ }
+ return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height).toBundle();
+ } else if (Utilities.ATLEAST_LOLLIPOP_MR1) {
+ // On L devices, we use the device default slide-up transition.
+ // On L MR1 devices, we use a custom version of the slide-up transition which
+ // doesn't have the delay present in the device default.
+ return ActivityOptions.makeCustomAnimation(
+ this, R.anim.task_open_enter, R.anim.no_anim).toBundle();
+ }
+ return null;
+ }
+
+ public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
return false;
}
- try {
- success = startActivity(v, intent, tag);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
+ // Only launch using the new animation if the shortcut has not opted out (this is a
+ // private contract between launcher and may be ignored in the future).
+ boolean useLaunchAnimation = (v != null) &&
+ !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
+ Bundle optsBundle = useLaunchAnimation ? getActivityLaunchOptions(v) : null;
+
+ UserHandleCompat user = null;
+ if (intent.hasExtra(AppInfo.EXTRA_PROFILE)) {
+ long serialNumber = intent.getLongExtra(AppInfo.EXTRA_PROFILE, -1);
+ user = UserManagerCompat.getInstance(this).getUserForSerialNumber(serialNumber);
}
- return success;
+
+ // Prepare intent
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ if (v != null) {
+ int[] pos = new int[2];
+ v.getLocationOnScreen(pos);
+ intent.setSourceBounds(
+ new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight()));
+ }
+ try {
+ if (Utilities.ATLEAST_MARSHMALLOW &&
+ item != null && item.itemType == Favorites.ITEM_TYPE_SHORTCUT) {
+ // Shortcuts need some special checks due to legacy reasons.
+ startShortcutIntentSafely(intent, optsBundle, item);
+ } else if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
+ // Could be launching some bookkeeping activity
+ startActivity(intent, optsBundle);
+ } else {
+ LauncherAppsCompat.getInstance(this).startActivityForProfile(
+ intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
+ }
+ return true;
+ } catch (ActivityNotFoundException|SecurityException e) {
+ Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
+ }
+ return false;
}
/**
@@ -3167,6 +3137,8 @@
mWorkspace.startReordering(v);
} else {
showOverviewMode(true);
+ mHotseat.requestDisallowInterceptTouchEvent(true);
+
}
} else {
final boolean isAllAppsButton = inHotseat && isAllAppsButtonRank(
@@ -3322,7 +3294,7 @@
/**
* Shows the apps view.
*/
- void showAppsView(boolean animated, boolean resetListToTop, boolean updatePredictedApps,
+ public void showAppsView(boolean animated, boolean resetListToTop, boolean updatePredictedApps,
boolean focusSearchBar) {
if (resetListToTop) {
mAppsView.scrollToTop();
@@ -3497,105 +3469,6 @@
return (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch());
}
- public View getOrCreateQsbBar() {
- if (launcherCallbacksProvidesSearch()) {
- return mLauncherCallbacks.getQsbBar();
- }
-
- if (mQsb == null) {
- AppWidgetProviderInfo searchProvider = Utilities.getSearchWidgetProvider(this);
- if (searchProvider == null) {
- return null;
- }
-
- Bundle opts = new Bundle();
- opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
- AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
-
- // Determine the min and max dimensions of the widget.
- LauncherAppState app = LauncherAppState.getInstance();
- DeviceProfile portraitProfile = app.getInvariantDeviceProfile().portraitProfile;
- DeviceProfile landscapeProfile = app.getInvariantDeviceProfile().landscapeProfile;
- float density = getResources().getDisplayMetrics().density;
- Point searchDimens = portraitProfile.getSearchBarDimensForWidgetOpts(getResources());
- int maxHeight = (int) (searchDimens.y / density);
- int minHeight = maxHeight;
- int maxWidth = (int) (searchDimens.x / density);
- int minWidth = maxWidth;
- if (!landscapeProfile.isVerticalBarLayout()) {
- searchDimens = landscapeProfile.getSearchBarDimensForWidgetOpts(getResources());
- maxHeight = (int) Math.max(maxHeight, searchDimens.y / density);
- minHeight = (int) Math.min(minHeight, searchDimens.y / density);
- maxWidth = (int) Math.max(maxWidth, searchDimens.x / density);
- minWidth = (int) Math.min(minWidth, searchDimens.x / density);
- }
- opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, maxHeight);
- opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, minHeight);
- opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, maxWidth);
- opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, minWidth);
- if (LOGD) {
- Log.d(TAG, "QSB widget options: maxHeight=" + maxHeight + " minHeight=" + minHeight
- + " maxWidth=" + maxWidth + " minWidth=" + minWidth);
- }
-
- if (mLauncherCallbacks != null) {
- opts.putAll(mLauncherCallbacks.getAdditionalSearchWidgetOptions());
- }
-
- int widgetId = mSharedPrefs.getInt(QSB_WIDGET_ID, -1);
- AppWidgetProviderInfo widgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId);
- if (!searchProvider.provider.flattenToString().equals(
- mSharedPrefs.getString(QSB_WIDGET_PROVIDER, null))
- || (widgetInfo == null)
- || !widgetInfo.provider.equals(searchProvider.provider)) {
- // A valid widget is not already bound.
- if (widgetId > -1) {
- mAppWidgetHost.deleteAppWidgetId(widgetId);
- widgetId = -1;
- }
-
- // Try to bind a new widget
- widgetId = mAppWidgetHost.allocateAppWidgetId();
-
- if (!AppWidgetManagerCompat.getInstance(this)
- .bindAppWidgetIdIfAllowed(widgetId, searchProvider, opts)) {
- mAppWidgetHost.deleteAppWidgetId(widgetId);
- widgetId = -1;
- }
-
- mSharedPrefs.edit()
- .putInt(QSB_WIDGET_ID, widgetId)
- .putString(QSB_WIDGET_PROVIDER, searchProvider.provider.flattenToString())
- .apply();
- }
-
- mAppWidgetHost.setQsbWidgetId(widgetId);
- if (widgetId != -1) {
- mQsb = mAppWidgetHost.createView(this, widgetId, searchProvider);
- mQsb.setId(R.id.qsb_widget);
- if (!Utilities.containsAll(
- AppWidgetManager.getInstance(this).getAppWidgetOptions(widgetId), opts)) {
- // Launcher should not be updating the options often.
- FileLog.d(TAG, "Options for QSB were not same");
- mQsb.updateAppWidgetOptions(opts);
- }
- mQsb.setPadding(0, 0, 0, 0);
- mSearchDropTargetBar.addView(mQsb);
- mSearchDropTargetBar.setQsbSearchBar(mQsb);
- }
- }
- return mQsb;
- }
-
- private void reinflateQSBIfNecessary() {
- if (mQsb instanceof LauncherAppWidgetHostView &&
- ((LauncherAppWidgetHostView) mQsb).isReinflateRequired()) {
- mSearchDropTargetBar.removeView(mQsb);
- mQsb = null;
- mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar());
- }
- }
-
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
final boolean result = super.dispatchPopulateAccessibilityEvent(event);
@@ -3615,16 +3488,6 @@
}
/**
- * Receives notifications when system dialogs are to be closed.
- */
- @Thunk class CloseSystemDialogsIntentReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- closeSystemDialogs();
- }
- }
-
- /**
* If the activity is currently paused, signal that we need to run the passed Runnable
* in onResume.
*
@@ -3729,12 +3592,13 @@
@Override
public void bindScreens(ArrayList<Long> orderedScreenIds) {
- bindAddScreens(orderedScreenIds);
-
- // If there are no screens, we need to have an empty screen
- if (orderedScreenIds.size() == 0) {
- mWorkspace.addExtraEmptyScreen();
+ // Make sure the first screen is always at the start.
+ if (orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
+ orderedScreenIds.remove(Workspace.FIRST_SCREEN_ID);
+ orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID);
+ mModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
}
+ bindAddScreens(orderedScreenIds);
// Create the custom content page (this call updates mDefaultScreen which calls
// setCurrentPage() so ensure that all pages are added before calling this).
@@ -3744,11 +3608,14 @@
}
}
- @Override
- public void bindAddScreens(ArrayList<Long> orderedScreenIds) {
+ private void bindAddScreens(ArrayList<Long> orderedScreenIds) {
int count = orderedScreenIds.size();
for (int i = 0; i < count; i++) {
- mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(orderedScreenIds.get(i));
+ long screenId = orderedScreenIds.get(i);
+ if (screenId != Workspace.FIRST_SCREEN_ID) {
+ // No need to bind the first screen, as its always bound.
+ mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId);
+ }
}
}
@@ -3827,24 +3694,6 @@
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
ShortcutInfo info = (ShortcutInfo) item;
view = createShortcut(info);
-
- /*
- * TODO: FIX collision case
- */
- if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
- if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {
- View v = cl.getChildAt(item.cellX, item.cellY);
- Object tag = v.getTag();
- String desc = "Collision while binding workspace item: " + item
- + ". Collides with " + tag;
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
- throw (new RuntimeException(desc));
- } else {
- Log.d(TAG, desc);
- }
- }
- }
break;
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
view = FolderIcon.fromXml(R.layout.folder_icon, this,
@@ -3855,6 +3704,25 @@
throw new RuntimeException("Invalid Item Type");
}
+ /*
+ * Remove colliding items.
+ */
+ if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
+ if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {
+ View v = cl.getChildAt(item.cellX, item.cellY);
+ Object tag = v.getTag();
+ String desc = "Collision while binding workspace item: " + item
+ + ". Collides with " + tag;
+ if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ throw (new RuntimeException(desc));
+ } else {
+ Log.d(TAG, desc);
+ LauncherModel.deleteItemFromDatabase(this, item);
+ continue;
+ }
+ }
+ }
workspace.addInScreenFromBind(view, item.container, item.screenId, item.cellX,
item.cellY, 1, 1);
if (animateIcons) {
@@ -4142,11 +4010,6 @@
return mDeviceProfile.isVerticalBarLayout();
}
- /** Returns the search bar bounds in pixels. */
- protected Rect getSearchBarBounds() {
- return mDeviceProfile.getSearchBarBounds(Utilities.isRtl(getResources()));
- }
-
public int getSearchBarHeight() {
if (mLauncherCallbacks != null) {
return mLauncherCallbacks.getSearchBarHeight();
@@ -4154,17 +4017,6 @@
return LauncherCallbacks.SEARCH_BAR_HEIGHT_NORMAL;
}
- public void bindSearchProviderChanged() {
- if (mSearchDropTargetBar == null) {
- return;
- }
- if (mQsb != null) {
- mSearchDropTargetBar.removeView(mQsb);
- mQsb = null;
- }
- mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar());
- }
-
/**
* A runnable that we can dequeue and re-enqueue when all applications are bound (to prevent
* multiple calls to bind the same list.)
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 9d889e0..ae3abbf 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -105,7 +105,6 @@
// Register intent receivers
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
- filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
// For handling managed profiles
filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED);
filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED);
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 8c23ff3..3bb7381 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -37,7 +37,6 @@
private final ArrayList<Runnable> mProviderChangeListeners = new ArrayList<Runnable>();
- private int mQsbWidgetId = -1;
private Launcher mLauncher;
public LauncherAppWidgetHost(Launcher launcher, int hostId) {
@@ -45,23 +44,9 @@
mLauncher = launcher;
}
- public void setQsbWidgetId(int widgetId) {
- mQsbWidgetId = widgetId;
- }
-
@Override
protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
AppWidgetProviderInfo appWidget) {
- if (appWidgetId == mQsbWidgetId) {
- return new LauncherAppWidgetHostView(context) {
-
- @Override
- protected View getErrorView() {
- // For the QSB, show an empty view instead of an error view.
- return new View(getContext());
- }
- };
- }
return new LauncherAppWidgetHostView(context);
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 28557d0..53d9c04 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -54,6 +54,8 @@
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mChildrenFocused;
+ protected int mErrorViewId = R.layout.appwidget_error;
+
public LauncherAppWidgetHostView(Context context) {
super(context);
mContext = context;
@@ -68,7 +70,7 @@
@Override
protected View getErrorView() {
- return mInflater.inflate(R.layout.appwidget_error, this, false);
+ return mInflater.inflate(mErrorViewId, this, false);
}
public void updateLastInflationOrientation() {
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 4c8fedf..99210fd 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -72,7 +72,7 @@
*/
int appWidgetId = NO_ID;
- ComponentName providerName;
+ public ComponentName providerName;
/**
* Indicates the restore status of the widget.
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index 28d8052..f245cd3 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -63,9 +63,8 @@
LauncherAppState app = LauncherAppState.getInstance();
InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
- // We only care out the cell size, which is independent of the the layout direction.
- Rect paddingLand = idp.landscapeProfile.getWorkspacePadding(false /* isLayoutRtl */);
- Rect paddingPort = idp.portraitProfile.getWorkspacePadding(false /* isLayoutRtl */);
+ Rect paddingLand = idp.landscapeProfile.getWorkspacePadding();
+ Rect paddingPort = idp.portraitProfile.getWorkspacePadding();
// Always assume we're working with the smallest span to make sure we
// reserve enough space in both orientations.
diff --git a/src/com/android/launcher3/LauncherBackupAgent.java b/src/com/android/launcher3/LauncherBackupAgent.java
index b2f5c57..b3e73f7 100644
--- a/src/com/android/launcher3/LauncherBackupAgent.java
+++ b/src/com/android/launcher3/LauncherBackupAgent.java
@@ -3,24 +3,12 @@
import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
import android.os.ParcelFileDescriptor;
-import com.android.launcher3.LauncherProvider.DatabaseHelper;
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.logging.FileLog;
-
-import java.io.InvalidObjectException;
+import com.android.launcher3.provider.RestoreDbTask;
public class LauncherBackupAgent extends BackupAgent {
- private static final String TAG = "LauncherBackupAgent";
-
- private static final String INFO_COLUMN_NAME = "name";
- private static final String INFO_COLUMN_DEFAULT_VALUE = "dflt_value";
-
@Override
public void onRestore(
BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) {
@@ -35,99 +23,6 @@
@Override
public void onRestoreFinished() {
- DatabaseHelper helper = new DatabaseHelper(this, null, LauncherFiles.LAUNCHER_DB);
-
- if (!sanitizeDBSafely(helper)) {
- helper.createEmptyDB(helper.getWritableDatabase());
- }
-
- try {
- // Flush all logs before the process is killed.
- FileLog.flushAll(null);
- } catch (Exception e) { }
- }
-
- private boolean sanitizeDBSafely(DatabaseHelper helper) {
- SQLiteDatabase db = helper.getWritableDatabase();
- db.beginTransaction();
- try {
- sanitizeDB(helper, db);
- db.setTransactionSuccessful();
- return true;
- } catch (Exception e) {
- FileLog.e(TAG, "Failed to verify db", e);
- return false;
- } finally {
- db.endTransaction();
- }
- }
-
- /**
- * Makes the following changes in the provider DB.
- * 1. Removes all entries belonging to a managed profile as managed profiles
- * cannot be restored.
- * 2. Marks all entries as restored. The flags are updated during first load or as
- * the restored apps get installed.
- * 3. If the user serial for primary profile is different than that of the previous device,
- * update the entries to the new profile id.
- */
- private void sanitizeDB(DatabaseHelper helper, SQLiteDatabase db) throws Exception {
- long oldProfileId = getDefaultProfileId(db);
- // Delete all entries which do not belong to the main user
- int itemsDeleted = db.delete(
- Favorites.TABLE_NAME, "profileId != ?", new String[]{Long.toString(oldProfileId)});
- if (itemsDeleted > 0) {
- FileLog.d(TAG, itemsDeleted + " items belonging to a managed profile, were deleted");
- }
-
- // Mark all items as restored.
- ContentValues values = new ContentValues();
- values.put(Favorites.RESTORED, 1);
- db.update(Favorites.TABLE_NAME, values, null, null);
-
- // Mark widgets with appropriate restore flag
- values.put(Favorites.RESTORED,
- LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
- LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
- LauncherAppWidgetInfo.FLAG_UI_NOT_READY);
- db.update(Favorites.TABLE_NAME, values, "itemType = ?",
- new String[]{Integer.toString(Favorites.ITEM_TYPE_APPWIDGET)});
-
- long myProfileId = helper.getDefaultUserSerial();
- if (Utilities.longCompare(oldProfileId, myProfileId) != 0) {
- FileLog.d(TAG, "Changing primary user id from " + oldProfileId + " to " + myProfileId);
- migrateProfileId(db, myProfileId);
- }
- }
-
- /**
- * Updates profile id of all entries and changes the default value for the column.
- */
- protected void migrateProfileId(SQLiteDatabase db, long newProfileId) {
- // Update existing entries.
- ContentValues values = new ContentValues();
- values.put(Favorites.PROFILE_ID, newProfileId);
- db.update(Favorites.TABLE_NAME, values, null, null);
-
- // Change default value of the column.
- db.execSQL("ALTER TABLE favorites RENAME TO favorites_old;");
- Favorites.addTableToDb(db, newProfileId, false);
- db.execSQL("INSERT INTO favorites SELECT * FROM favorites_old;");
- db.execSQL("DROP TABLE favorites_old;");
- }
-
- /**
- * Returns the profile id for used in the favorites table of the provided db.
- */
- protected long getDefaultProfileId(SQLiteDatabase db) throws Exception {
- try (Cursor c = db.rawQuery("PRAGMA table_info (favorites)", null)){
- int nameIndex = c.getColumnIndex(INFO_COLUMN_NAME);
- while (c.moveToNext()) {
- if (Favorites.PROFILE_ID.equals(c.getString(nameIndex))) {
- return c.getLong(c.getColumnIndex(INFO_COLUMN_DEFAULT_VALUE));
- }
- }
- throw new InvalidObjectException("Table does not have a profile id column");
- }
+ RestoreDbTask.setPending(this, true);
}
}
diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java
index c44969f..f0e9593 100644
--- a/src/com/android/launcher3/LauncherClings.java
+++ b/src/com/android/launcher3/LauncherClings.java
@@ -38,7 +38,7 @@
import com.android.launcher3.util.Thunk;
-class LauncherClings implements OnClickListener, OnKeyListener {
+public class LauncherClings implements OnClickListener, OnKeyListener {
private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
private static final String TAG_CROP_TOP_AND_SIDES = "crop_bg_top_and_sides";
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 649f42d..557a91a 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -16,7 +16,6 @@
package com.android.launcher3;
-import android.app.SearchManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -177,7 +176,6 @@
public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,
boolean forceAnimateIcons);
public void bindScreens(ArrayList<Long> orderedScreenIds);
- public void bindAddScreens(ArrayList<Long> orderedScreenIds);
public void finishBindingItems();
public void bindAppWidget(LauncherAppWidgetInfo info);
public void bindAllApplications(ArrayList<AppInfo> apps);
@@ -196,7 +194,6 @@
public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);
public void notifyWidgetProvidersChanged();
public void bindWidgetsModel(WidgetsModel model);
- public void bindSearchProviderChanged();
public boolean isAllAppsButtonRank(int rank);
public void onPageBoundSynchronously(int page);
public void executeOnNextDraw(ViewOnDrawExecutor executor);
@@ -1139,11 +1136,6 @@
if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
// If we have changed locale we need to clear out the labels in all apps/workspace.
forceReload();
- } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action)) {
- Callbacks callbacks = getCallback();
- if (callbacks != null) {
- callbacks.bindSearchProviderChanged();
- }
} else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action)
|| LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
UserManagerCompat.getInstance(context).enableAndResetCache();
@@ -1528,7 +1520,12 @@
}
if (!occupied.containsKey(item.screenId)) {
- occupied.put(item.screenId, new GridOccupancy(countX + 1, countY + 1));
+ GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1);
+ if (item.screenId == Workspace.FIRST_SCREEN_ID) {
+ // Mark the first row as occupied in order to account for the QSB.
+ screen.markCells(0, 0, countX + 1, 1, true);
+ }
+ occupied.put(item.screenId, screen);
}
final GridOccupancy occupancy = occupied.get(item.screenId);
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 11d61d0..dfb8ba2 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -46,7 +46,6 @@
import android.os.Message;
import android.os.Process;
import android.os.UserManager;
-import android.provider.BaseColumns;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
@@ -58,6 +57,7 @@
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dynamicui.ExtractionUtils;
+import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.util.ManagedProfileHeuristic;
import com.android.launcher3.util.NoLocaleSqliteContext;
import com.android.launcher3.util.Preconditions;
@@ -117,6 +117,15 @@
protected synchronized void createDbIfNotExists() {
if (mOpenHelper == null) {
mOpenHelper = new DatabaseHelper(getContext(), mListenerHandler);
+
+ if (RestoreDbTask.isPending(getContext())) {
+ if (!RestoreDbTask.performRestore(mOpenHelper)) {
+ mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
+ }
+ // Set is pending to false irrespective of the result, so that it doesn't get
+ // executed again.
+ RestoreDbTask.setPending(getContext(), false);
+ }
}
}
@@ -611,8 +620,11 @@
// Database was just created, so wipe any previous widgets
if (mWidgetHostResetHandler != null) {
new AppWidgetHost(mContext, Launcher.APPWIDGET_HOST_ID).deleteHost();
- mWidgetHostResetHandler.sendEmptyMessage(
- ChangeListenerWrapper.MSG_APP_WIDGET_HOST_RESET);
+ mWidgetHostResetHandler.sendMessage(Message.obtain(
+ mWidgetHostResetHandler,
+ ChangeListenerWrapper.MSG_APP_WIDGET_HOST_RESET,
+ mContext
+ ));
}
// Set the flag for empty DB
@@ -623,7 +635,7 @@
mContext);
}
- protected long getDefaultUserSerial() {
+ public long getDefaultUserSerial() {
return UserManagerCompat.getInstance(mContext).getSerialNumberForUser(
UserHandleCompat.myUserHandle());
}
@@ -1100,7 +1112,11 @@
mListener.onExtractedColorsChanged();
break;
case MSG_APP_WIDGET_HOST_RESET:
- mListener.onAppWidgetHostReset();
+ Context context = (Context) msg.obj;
+ if (context != null) {
+ context.sendBroadcast(new Intent(Launcher.ACTION_APPWIDGET_HOST_RESET)
+ .setPackage(context.getPackageName()));
+ }
break;
}
}
diff --git a/src/com/android/launcher3/LauncherProviderChangeListener.java b/src/com/android/launcher3/LauncherProviderChangeListener.java
index 524befc..5998dad 100644
--- a/src/com/android/launcher3/LauncherProviderChangeListener.java
+++ b/src/com/android/launcher3/LauncherProviderChangeListener.java
@@ -10,6 +10,4 @@
public void onLauncherProviderChange();
public void onExtractedColorsChanged();
-
- public void onAppWidgetHostReset();
}
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 17a5424..d62c629 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -33,6 +33,8 @@
import android.view.animation.DecelerateInterpolator;
import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.UiThreadCircularReveal;
import com.android.launcher3.widget.WidgetsContainerView;
@@ -82,6 +84,17 @@
*/
public class LauncherStateTransitionAnimation {
+ /**
+ * animation used for all apps and widget tray when
+ *{@link FeatureFlags#LAUNCHER3_ALL_APPS_PULL_UP} is {@code false}
+ */
+ public static final int CIRCULAR_REVEAL = 0;
+ /**
+ * animation used for all apps and not widget tray when
+ *{@link FeatureFlags#LAUNCHER3_ALL_APPS_PULL_UP} is {@code true}
+ */
+ public static final int PULLUP = 1;
+
private static final float FINAL_REVEAL_ALPHA_FOR_WIDGETS = 0.3f;
/**
@@ -113,9 +126,11 @@
@Thunk Launcher mLauncher;
@Thunk AnimatorSet mCurrentAnimation;
+ AllAppsTransitionController mAllAppsController;
- public LauncherStateTransitionAnimation(Launcher l) {
+ public LauncherStateTransitionAnimation(Launcher l, AllAppsTransitionController allAppsController) {
mLauncher = l;
+ mAllAppsController = allAppsController;
}
/**
@@ -154,9 +169,13 @@
}
}
};
+ int animType = CIRCULAR_REVEAL;
+ if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+ animType = PULLUP;
+ }
// Only animate the search bar if animating from spring loaded mode back to all apps
mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
- Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, cb);
+ Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, animType, cb);
}
/**
@@ -167,7 +186,7 @@
final WidgetsContainerView toView = mLauncher.getWidgetsView();
final View buttonView = mLauncher.getWidgetsButton();
mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
- Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, animated,
+ Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, animated, CIRCULAR_REVEAL,
new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS){
@Override
void onTransitionComplete() {
@@ -189,8 +208,12 @@
}
if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
+ int animType = CIRCULAR_REVEAL;
+ if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+ animType = PULLUP;
+ }
startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState,
- animated, onCompleteRunnable);
+ animated, animType, onCompleteRunnable);
} else if (fromState == Launcher.State.WIDGETS ||
fromState == Launcher.State.WIDGETS_SPRING_LOADED) {
startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState,
@@ -208,7 +231,7 @@
private AnimatorSet startAnimationToOverlay(
final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
final View buttonView, final BaseContainerView toView,
- final boolean animated, final PrivateTransitionCallbacks pCb) {
+ final boolean animated, int animType, final PrivateTransitionCallbacks pCb) {
final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
final Resources res = mLauncher.getResources();
final boolean material = Utilities.ATLEAST_LOLLIPOP;
@@ -225,12 +248,37 @@
// Cancel the current animation
cancelAnimation();
- playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
- animated, initialized, animation, revealDuration, layerViews);
-
final View contentView = toView.getContentView();
- if (animated && initialized) {
+ if (!animated || !initialized) {
+ playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
+ animated, initialized, animation, revealDuration, layerViews);
+
+ toView.setTranslationX(0.0f);
+ toView.setTranslationY(0.0f);
+ toView.setScaleX(1.0f);
+ toView.setScaleY(1.0f);
+ toView.setAlpha(1.0f);
+ toView.setVisibility(View.VISIBLE);
+ toView.bringToFront();
+
+ // Show the content view
+ contentView.setVisibility(View.VISIBLE);
+
+ dispatchOnLauncherTransitionPrepare(fromView, animated, false);
+ dispatchOnLauncherTransitionStart(fromView, animated, false);
+ dispatchOnLauncherTransitionEnd(fromView, animated, false);
+ dispatchOnLauncherTransitionPrepare(toView, animated, false);
+ dispatchOnLauncherTransitionStart(toView, animated, false);
+ dispatchOnLauncherTransitionEnd(toView, animated, false);
+ pCb.onTransitionComplete();
+
+ return null;
+ }
+ if (animType == CIRCULAR_REVEAL) {
+ playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
+ animated, initialized, animation, revealDuration, layerViews);
+
// Setup the reveal view animation
final View revealView = toView.getRevealView();
@@ -366,27 +414,41 @@
toView.post(startAnimRunnable);
return animation;
- } else {
- toView.setTranslationX(0.0f);
- toView.setTranslationY(0.0f);
- toView.setScaleX(1.0f);
- toView.setScaleY(1.0f);
- toView.setVisibility(View.VISIBLE);
- toView.bringToFront();
-
- // Show the content view
- contentView.setVisibility(View.VISIBLE);
+ } else if (animType == PULLUP) {
+ animation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ dispatchOnLauncherTransitionEnd(fromView, animated, false);
+ dispatchOnLauncherTransitionEnd(toView, animated, false);
+ cleanupAnimation();
+ pCb.onTransitionComplete();
+ }
+ });
+ mAllAppsController.animateToAllApps(animation, revealDuration);
+ playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
+ animated, initialized, animation, revealDuration, layerViews);
dispatchOnLauncherTransitionPrepare(fromView, animated, false);
- dispatchOnLauncherTransitionStart(fromView, animated, false);
- dispatchOnLauncherTransitionEnd(fromView, animated, false);
dispatchOnLauncherTransitionPrepare(toView, animated, false);
- dispatchOnLauncherTransitionStart(toView, animated, false);
- dispatchOnLauncherTransitionEnd(toView, animated, false);
- pCb.onTransitionComplete();
- return null;
+ final AnimatorSet stateAnimation = animation;
+ final Runnable startAnimRunnable = new Runnable() {
+ public void run() {
+ // Check that mCurrentAnimation hasn't changed while
+ // we waited for a layout/draw pass
+ if (mCurrentAnimation != stateAnimation)
+ return;
+ dispatchOnLauncherTransitionStart(fromView, animated, false);
+ dispatchOnLauncherTransitionStart(toView, animated, false);
+
+ toView.requestFocus();
+ stateAnimation.start();
+ }
+ };
+ toView.post(startAnimRunnable);
+ return animation;
}
+ return null;
}
/**
@@ -401,12 +463,6 @@
Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
animated, layerViews);
- // Animate the search bar
- final SearchDropTargetBar.State toSearchBarState =
- toWorkspaceState.searchDropTargetBarState;
- mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState,
- animated ? revealDuration : 0, animation);
-
if (animated && initialized) {
// Play the workspace animation
if (workspaceAnim != null) {
@@ -439,7 +495,7 @@
* Starts an animation to the workspace from the apps view.
*/
private void startAnimationToWorkspaceFromAllApps(final Workspace.State fromWorkspaceState,
- final Workspace.State toWorkspaceState, final boolean animated,
+ final Workspace.State toWorkspaceState, final boolean animated, int type,
final Runnable onCompleteRunnable) {
AllAppsContainerView appsView = mLauncher.getAppsView();
// No alpha anim from all apps
@@ -476,7 +532,7 @@
// Only animate the search bar if animating to spring loaded mode from all apps
mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
mLauncher.getAllAppsButton(), appsView,
- animated, onCompleteRunnable, cb);
+ animated, type, onCompleteRunnable, cb);
}
/**
@@ -506,7 +562,7 @@
mCurrentAnimation = startAnimationToWorkspaceFromOverlay(
fromWorkspaceState, toWorkspaceState,
mLauncher.getWidgetsButton(), widgetsView,
- animated, onCompleteRunnable, cb);
+ animated, CIRCULAR_REVEAL, onCompleteRunnable, cb);
}
/**
@@ -598,7 +654,7 @@
private AnimatorSet startAnimationToWorkspaceFromOverlay(
final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
final View buttonView, final BaseContainerView fromView,
- final boolean animated, final Runnable onCompleteRunnable,
+ final boolean animated, int animType, final Runnable onCompleteRunnable,
final PrivateTransitionCallbacks pCb) {
final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
final Resources res = mLauncher.getResources();
@@ -619,10 +675,31 @@
boolean multiplePagesVisible = toWorkspaceState.hasMultipleVisiblePages;
- playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
- animated, initialized, animation, revealDuration, layerViews);
+ if (!animated || !initialized) {
+ playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
+ animated, initialized, animation, revealDuration, layerViews);
+ if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+ mAllAppsController.finishPullDown();
+ }
+ fromView.setVisibility(View.GONE);
+ dispatchOnLauncherTransitionPrepare(fromView, animated, multiplePagesVisible);
+ dispatchOnLauncherTransitionStart(fromView, animated, true);
+ dispatchOnLauncherTransitionEnd(fromView, animated, true);
+ dispatchOnLauncherTransitionPrepare(toView, animated, multiplePagesVisible);
+ dispatchOnLauncherTransitionStart(toView, animated, true);
+ dispatchOnLauncherTransitionEnd(toView, animated, true);
+ pCb.onTransitionComplete();
- if (animated && initialized) {
+ // Run any queued runnables
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+
+ return null;
+ }
+ if (animType == CIRCULAR_REVEAL) {
+ playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
+ animated, initialized, animation, revealDuration, layerViews);
final View revealView = fromView.getRevealView();
final View contentView = fromView.getContentView();
@@ -791,23 +868,61 @@
fromView.post(startAnimRunnable);
return animation;
- } else /* if (!(animated && initialized)) */ {
- fromView.setVisibility(View.GONE);
- dispatchOnLauncherTransitionPrepare(fromView, animated, multiplePagesVisible);
- dispatchOnLauncherTransitionStart(fromView, animated, true);
- dispatchOnLauncherTransitionEnd(fromView, animated, true);
- dispatchOnLauncherTransitionPrepare(toView, animated, multiplePagesVisible);
- dispatchOnLauncherTransitionStart(toView, animated, true);
- dispatchOnLauncherTransitionEnd(toView, animated, true);
- pCb.onTransitionComplete();
+ } else if (animType == PULLUP) {
+ animation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ dispatchOnLauncherTransitionEnd(fromView, animated, false);
+ dispatchOnLauncherTransitionEnd(toView, animated, false);
+ cleanupAnimation();
+ pCb.onTransitionComplete();
+ }
- // Run any queued runnables
- if (onCompleteRunnable != null) {
- onCompleteRunnable.run();
- }
+ });
+ mAllAppsController.animateToWorkspace(animation, revealDuration);
+ playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
+ animated, initialized, animation, revealDuration, layerViews);
- return null;
+ // Dispatch the prepare transition signal
+ dispatchOnLauncherTransitionPrepare(fromView, animated, false);
+ dispatchOnLauncherTransitionPrepare(toView, animated, false);
+
+ animation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ dispatchOnLauncherTransitionEnd(fromView, animated, true);
+ dispatchOnLauncherTransitionEnd(toView, animated, true);
+
+ // Run any queued runnables
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+
+ // This can hold unnecessary references to views.
+ cleanupAnimation();
+ pCb.onTransitionComplete();
+ }
+ });
+
+ final AnimatorSet stateAnimation = animation;
+ final Runnable startAnimRunnable = new Runnable() {
+ public void run() {
+ // Check that mCurrentAnimation hasn't changed while
+ // we waited for a layout/draw pass
+ if (mCurrentAnimation != stateAnimation)
+ return;
+ dispatchOnLauncherTransitionStart(fromView, animated, false);
+ dispatchOnLauncherTransitionStart(toView, animated, false);
+
+ // Focus the new view
+ toView.requestFocus();
+ stateAnimation.start();
+ }
+ };
+ fromView.post(startAnimRunnable);
+ return animation;
}
+ return null;
}
/**
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index fdca9f2..9266793 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -647,6 +647,9 @@
public static class LayoutParams extends ViewGroup.LayoutParams {
public boolean isFullScreenPage = false;
+ // If true, the start edge of the page snaps to the start edge of the viewport.
+ public boolean matchStartEdge = false;
+
/**
* {@inheritDoc}
*/
@@ -778,6 +781,10 @@
childWidth = getViewportWidth() - horizontalPadding
- mInsets.left - mInsets.right;
+
+ if (lp.matchStartEdge) {
+ childWidth += getPaddingStart();
+ }
childHeight = getViewportHeight() - verticalPadding
- mInsets.top - mInsets.bottom;
mNormalChildHeight = childHeight;
@@ -827,7 +834,8 @@
LayoutParams lp = (LayoutParams) getChildAt(startIndex).getLayoutParams();
LayoutParams nextLp;
- int childLeft = offsetX + (lp.isFullScreenPage ? 0 : getPaddingLeft());
+ int childLeft = offsetX +
+ ((lp.isFullScreenPage || (!mIsRtl && lp.matchStartEdge)) ? 0 : getPaddingLeft());
if (mPageScrolls == null || childCount != mChildCountOnLastLayout) {
mPageScrolls = new int[childCount];
}
@@ -851,7 +859,8 @@
child.layout(childLeft, childTop,
childLeft + child.getMeasuredWidth(), childTop + childHeight);
- int scrollOffsetLeft = lp.isFullScreenPage ? 0 : getPaddingLeft();
+ int scrollOffsetLeft = (lp.isFullScreenPage || (!mIsRtl & lp.matchStartEdge)) ?
+ 0 : getPaddingLeft();
mPageScrolls[i] = childLeft - scrollOffsetLeft - offsetX;
int pageGap = mPageSpacing;
@@ -1653,7 +1662,8 @@
if (DEBUG) Log.d(TAG, "mParentDownMotionY: " + mParentDownMotionY);
final int pageUnderPointIndex = getNearestHoverOverPageIndex();
- if (pageUnderPointIndex > -1 && pageUnderPointIndex != indexOfChild(mDragView)) {
+ // Do not allow any page to be moved to 0th position.
+ if (pageUnderPointIndex > 0 && pageUnderPointIndex != indexOfChild(mDragView)) {
mTempVisiblePagesRange[0] = 0;
mTempVisiblePagesRange[1] = getPageCount() - 1;
getFreeScrollPageRange(mTempVisiblePagesRange);
@@ -1961,7 +1971,7 @@
snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
}
- private static class ScrollInterpolator implements Interpolator {
+ public static class ScrollInterpolator implements Interpolator {
public ScrollInterpolator() {
}
@@ -2171,7 +2181,8 @@
public boolean startReordering(View v) {
int dragViewIndex = indexOfChild(v);
- if (mTouchState != TOUCH_STATE_REST || dragViewIndex == -1) return false;
+ // Do not allow the first page to be moved around
+ if (mTouchState != TOUCH_STATE_REST || dragViewIndex <= 0) return false;
mTempVisiblePagesRange[0] = 0;
mTempVisiblePagesRange[1] = getPageCount() - 1;
diff --git a/src/com/android/launcher3/PinchAnimationManager.java b/src/com/android/launcher3/PinchAnimationManager.java
index c8c8fa4..c1c2519 100644
--- a/src/com/android/launcher3/PinchAnimationManager.java
+++ b/src/com/android/launcher3/PinchAnimationManager.java
@@ -150,12 +150,10 @@
animateOverviewPanelButtons(goingTowards == OVERVIEW);
} else if (startState == NORMAL) {
animateHotseatAndPageIndicator(goingTowards == NORMAL);
- animateQsb(goingTowards == NORMAL);
}
} else if (threshold == PinchThresholdManager.THRESHOLD_TWO) {
if (startState == OVERVIEW) {
animateHotseatAndPageIndicator(goingTowards == NORMAL);
- animateQsb(goingTowards == NORMAL);
animateScrim(goingTowards == OVERVIEW);
} else if (startState == NORMAL) {
animateOverviewPanelButtons(goingTowards == OVERVIEW);
@@ -198,12 +196,6 @@
}
}
- private void animateQsb(boolean show) {
- SearchDropTargetBar.State searchBarState = show ? SearchDropTargetBar.State.SEARCH_BAR
- : SearchDropTargetBar.State.INVISIBLE;
- mLauncher.getSearchDropTargetBar().animateToState(searchBarState, THRESHOLD_ANIM_DURATION);
- }
-
private void animateOverviewPanelButtons(boolean show) {
animateShowHideView(INDEX_OVERVIEW_PANEL_BUTTONS, mLauncher.getOverviewPanel(), show);
}
diff --git a/src/com/android/launcher3/PinchToOverviewListener.java b/src/com/android/launcher3/PinchToOverviewListener.java
index 0c8568e..6ee96fc 100644
--- a/src/com/android/launcher3/PinchToOverviewListener.java
+++ b/src/com/android/launcher3/PinchToOverviewListener.java
@@ -21,6 +21,8 @@
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
+import com.android.launcher3.util.TouchController;
+
/**
* Detects pinches and animates the Workspace to/from overview mode.
*
@@ -30,7 +32,8 @@
* @see PinchThresholdManager
* @see PinchAnimationManager
*/
-public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
+public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleGestureListener
+ implements TouchController {
private static final float OVERVIEW_PROGRESS = 0f;
private static final float WORKSPACE_PROGRESS = 1f;
/**
@@ -63,15 +66,16 @@
return mPinchStarted;
}
- public void onTouchEvent(MotionEvent ev) {
+ public boolean onTouchEvent(MotionEvent ev) {
if (mPinchStarted) {
if (ev.getPointerCount() > 2) {
// Using more than two fingers causes weird behavior, so just cancel the pinch.
cancelPinch(mPreviousProgress, -1);
} else {
- mPinchDetector.onTouchEvent(ev);
+ return mPinchDetector.onTouchEvent(ev);
}
}
+ return false;
}
@Override
diff --git a/src/com/android/launcher3/QsbContainerView.java b/src/com/android/launcher3/QsbContainerView.java
new file mode 100644
index 0000000..7d939a0
--- /dev/null
+++ b/src/com/android/launcher3/QsbContainerView.java
@@ -0,0 +1,263 @@
+/*
+ * 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;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.SearchManager;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+
+/**
+ * A frame layout which contains a QSB. This internally uses fragment to bind the view, which
+ * allows it to contain the logic for {@link Fragment#startActivityForResult(Intent, int)}.
+ */
+public class QsbContainerView extends FrameLayout {
+
+ public QsbContainerView(Context context) {
+ super(context);
+ }
+
+ public QsbContainerView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public QsbContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ public void setPadding(int left, int top, int right, int bottom) {
+ super.setPadding(0, 0, 0, 0);
+ }
+
+ /**
+ * A fragment to display the QSB.
+ */
+ public static class QsbFragment extends Fragment implements View.OnClickListener {
+
+ private static final int REQUEST_BIND_QSB = 1;
+ private static final String QSB_WIDGET_ID = "qsb_widget_id";
+
+ private static int sSavedWidgetId = -1;
+
+ private AppWidgetProviderInfo mWidgetInfo;
+ private LauncherAppWidgetHostView mQsb;
+
+ private BroadcastReceiver mRebindReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ rebindFragment();
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ IntentFilter filter = new IntentFilter(Launcher.ACTION_APPWIDGET_HOST_RESET);
+ filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
+ getActivity().registerReceiver(mRebindReceiver, filter);
+ }
+
+ private FrameLayout mWrapper;
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ if (savedInstanceState != null) {
+ sSavedWidgetId = savedInstanceState.getInt(QSB_WIDGET_ID, -1);
+ }
+ mWrapper = new FrameLayout(getActivity());
+ mWrapper.addView(createQsb(inflater, mWrapper));
+ return mWrapper;
+ }
+
+ private View createQsb(LayoutInflater inflater, ViewGroup container) {
+ Launcher launcher = (Launcher) getActivity();
+ mWidgetInfo = getSearchWidgetProvider(launcher);
+ if (mWidgetInfo == null) {
+ // There is no search provider, just show the default widget.
+ return getDefaultView(inflater, container, false);
+ }
+
+ SharedPreferences prefs = Utilities.getPrefs(launcher);
+ AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(launcher);
+ LauncherAppWidgetHost widgetHost = launcher.getAppWidgetHost();
+ InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
+
+ Bundle opts = new Bundle();
+ Rect size = AppWidgetResizeFrame.getWidgetSizeRanges(launcher, idp.numColumns, 1, null);
+ opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, size.left);
+ opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, size.top);
+ opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, size.right);
+ opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, size.bottom);
+
+ int widgetId = prefs.getInt(QSB_WIDGET_ID, -1);
+ AppWidgetProviderInfo widgetInfo = widgetManager.getAppWidgetInfo(widgetId);
+ boolean isWidgetBound = (widgetInfo != null) &&
+ widgetInfo.provider.equals(mWidgetInfo.provider);
+
+ if (!isWidgetBound) {
+ // widgetId is already bound and its not the correct provider.
+ // Delete the widget id.
+ if (widgetId > -1) {
+ widgetHost.deleteAppWidgetId(widgetId);
+ widgetId = -1;
+ }
+
+ widgetId = widgetHost.allocateAppWidgetId();
+ isWidgetBound = widgetManager.bindAppWidgetIdIfAllowed(widgetId, mWidgetInfo, opts);
+ if (!isWidgetBound) {
+ widgetHost.deleteAppWidgetId(widgetId);
+ widgetId = -1;
+ }
+ }
+
+ if (isWidgetBound) {
+ mQsb = (LauncherAppWidgetHostView)
+ widgetHost.createView(launcher, widgetId, mWidgetInfo);
+ mQsb.setId(R.id.qsb_widget);
+ mQsb.mErrorViewId = R.layout.qsb_default_view;
+
+ if (!Utilities.containsAll(AppWidgetManager.getInstance(launcher)
+ .getAppWidgetOptions(widgetId), opts)) {
+ mQsb.updateAppWidgetOptions(opts);
+ }
+ mQsb.setPadding(0, 0, 0, 0);
+ return mQsb;
+ }
+
+ // Return a default widget with setup icon.
+ return getDefaultView(inflater, container, true);
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view.getId() == R.id.btn_qsb_search) {
+ getActivity().startSearch("", false, null, true);
+ } else if (view.getId() == R.id.btn_qsb_setup) {
+ // Allocate a new widget id for QSB
+ sSavedWidgetId = ((Launcher) getActivity())
+ .getAppWidgetHost().allocateAppWidgetId();
+ // Start intent for bind the widget
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, sSavedWidgetId);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, mWidgetInfo.provider);
+ startActivityForResult(intent, REQUEST_BIND_QSB);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(QSB_WIDGET_ID, sSavedWidgetId);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_BIND_QSB) {
+ if (resultCode == Activity.RESULT_OK) {
+ int widgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+ sSavedWidgetId);
+ Utilities.getPrefs(getActivity()).edit().putInt(QSB_WIDGET_ID, widgetId).apply();
+ sSavedWidgetId = -1;
+ rebindFragment();
+ } else if (sSavedWidgetId != -1) {
+ ((Launcher) getActivity()).getAppWidgetHost().deleteAppWidgetId(sSavedWidgetId);
+ sSavedWidgetId = -1;
+ }
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (mQsb != null && mQsb.isReinflateRequired()) {
+ rebindFragment();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ getActivity().unregisterReceiver(mRebindReceiver);
+ super.onDestroy();
+ }
+
+ private void rebindFragment() {
+ if (mWrapper != null && getActivity() != null) {
+ mWrapper.removeAllViews();
+ mWrapper.addView(createQsb(getActivity().getLayoutInflater(), mWrapper));
+ }
+ }
+
+ private View getDefaultView(LayoutInflater inflater, ViewGroup parent, boolean showSetup) {
+ View v = inflater.inflate(R.layout.qsb_default_view, parent, false);
+ if (showSetup) {
+ View setupButton = v.findViewById(R.id.btn_qsb_setup);
+ setupButton.setVisibility(View.VISIBLE);
+ setupButton.setOnClickListener(this);
+ }
+ v.findViewById(R.id.btn_qsb_search).setOnClickListener(this);
+ return v;
+ }
+ }
+
+ /**
+ * Returns a widget with category {@link AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX}
+ * provided by the same package which is set to be global search activity.
+ * If widgetCategory is not supported, or no such widget is found, returns the first widget
+ * provided by the package.
+ */
+ public static AppWidgetProviderInfo getSearchWidgetProvider(Context context) {
+ SearchManager searchManager =
+ (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
+ ComponentName searchComponent = searchManager.getGlobalSearchActivity();
+ if (searchComponent == null) return null;
+ String providerPkg = searchComponent.getPackageName();
+
+ AppWidgetProviderInfo defaultWidgetForSearchPackage = null;
+
+ AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
+ for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) {
+ if (info.provider.getPackageName().equals(providerPkg) && info.configure == null) {
+ if ((info.widgetCategory & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) {
+ return info;
+ } else if (defaultWidgetForSearchPackage == null) {
+ defaultWidgetForSearchPackage = info;
+ }
+ }
+ }
+ return defaultWidgetForSearchPackage;
+ }
+}
diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java
deleted file mode 100644
index 171dd87..0000000
--- a/src/com/android/launcher3/SearchDropTargetBar.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-
-import com.android.launcher3.dragndrop.DragController;
-import com.android.launcher3.util.Thunk;
-
-/*
- * This bar will manage the transition between the QSB search bar and the delete/uninstall drop
- * targets so that each of the individual ButtonDropTargets don't have to.
- */
-public class SearchDropTargetBar extends BaseDropTargetBar {
-
- private static final TimeInterpolator MOVE_DOWN_INTERPOLATOR = new DecelerateInterpolator(0.6f);
- private static final TimeInterpolator MOVE_UP_INTERPOLATOR = new DecelerateInterpolator(1.5f);
-
- /** The different states that the search bar space can be in. */
- public enum State {
- INVISIBLE (0f, 0f, 0f),
- INVISIBLE_TRANSLATED (0f, 0f, -1f),
- SEARCH_BAR (1f, 0f, 0f),
- DROP_TARGET (0f, 1f, 0f);
-
- private final float mSearchBarAlpha;
- private final float mDropTargetBarAlpha;
- private final float mTranslation;
-
- State(float sbAlpha, float dtbAlpha, float translation) {
- mSearchBarAlpha = sbAlpha;
- mDropTargetBarAlpha = dtbAlpha;
- mTranslation = translation;
- }
-
- }
-
-
- @ViewDebug.ExportedProperty(category = "launcher")
- private State mState = State.SEARCH_BAR;
- @Thunk View mQSB;
-
- // Drop targets
- private ButtonDropTarget mDeleteDropTarget;
- private ButtonDropTarget mUninstallDropTarget;
-
- public SearchDropTargetBar(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public SearchDropTargetBar(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- // Get the individual components
- mDeleteDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.delete_target_text);
- mUninstallDropTarget = (ButtonDropTarget) mDropTargetBar
- .findViewById(R.id.uninstall_target_text);
-
- mDeleteDropTarget.setDropTargetBar(this);
- mUninstallDropTarget.setDropTargetBar(this);
- }
-
- @Override
- public void setup(Launcher launcher, DragController dragController) {
- dragController.addDragListener(this);
- dragController.setFlingToDeleteDropTarget(mDeleteDropTarget);
-
- dragController.addDragListener(mDeleteDropTarget);
- dragController.addDragListener(mUninstallDropTarget);
-
- dragController.addDropTarget(mDeleteDropTarget);
- dragController.addDropTarget(mUninstallDropTarget);
-
- mDeleteDropTarget.setLauncher(launcher);
- mUninstallDropTarget.setLauncher(launcher);
- }
-
- @Override
- public void showDropTargets() {
- animateToState(State.DROP_TARGET, DEFAULT_DRAG_FADE_DURATION);
- }
-
- @Override
- public void hideDropTargets() {
- animateToState(State.SEARCH_BAR, DEFAULT_DRAG_FADE_DURATION);
- }
-
- public void setQsbSearchBar(View qsb) {
- mQSB = qsb;
- }
-
- /**
- * Animates the current search bar state to a new state. If the {@param duration} is 0, then
- * the state is applied immediately.
- */
- public void animateToState(State newState, int duration) {
- animateToState(newState, duration, null);
- }
-
- public void animateToState(State newState, int duration, AnimatorSet animation) {
- if (mState != newState) {
- mState = newState;
-
- resetAnimation(duration);
- if (duration > 0) {
- animateAlpha(mDropTargetBar, mState.mDropTargetBarAlpha, DEFAULT_INTERPOLATOR);
- } else {
- mDropTargetBar.setAlpha(mState.mDropTargetBarAlpha);
- AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
- }
-
- if (mQSB != null) {
- boolean isVertical = ((Launcher) getContext()).getDeviceProfile()
- .isVerticalBarLayout();
- float translation = isVertical ? 0 : mState.mTranslation * getMeasuredHeight();
-
- if (duration > 0) {
- int translationChange = Float.compare(mQSB.getTranslationY(), translation);
-
- animateAlpha(mQSB, mState.mSearchBarAlpha,
- translationChange == 0 ? DEFAULT_INTERPOLATOR
- : (translationChange < 0 ? MOVE_DOWN_INTERPOLATOR
- : MOVE_UP_INTERPOLATOR));
-
- if (translationChange != 0) {
- mCurrentAnimation.play(
- ObjectAnimator.ofFloat(mQSB, View.TRANSLATION_Y, translation));
- }
- } else {
- mQSB.setTranslationY(translation);
- mQSB.setAlpha(mState.mSearchBarAlpha);
- AlphaUpdateListener.updateVisibility(mQSB, mAccessibilityEnabled);
- }
- }
-
- // Start the final animation
- if (duration > 0) {
- if (animation != null) {
- animation.play(mCurrentAnimation);
- } else {
- mCurrentAnimation.start();
- }
- }
- }
- }
-
- /**
- * @return the bounds of the QSB search bar.
- */
- public Rect getSearchBarBounds() {
- if (mQSB != null) {
- final int[] pos = new int[2];
- mQSB.getLocationOnScreen(pos);
-
- final Rect rect = new Rect();
- rect.left = pos[0];
- rect.top = pos[1];
- rect.right = pos[0] + mQSB.getWidth();
- rect.bottom = pos[1] + mQSB.getHeight();
- return rect;
- } else {
- return null;
- }
- }
-
- @Override
- public void enableAccessibleDrag(boolean enable) {
- if (mQSB != null) {
- mQSB.setVisibility(enable ? View.GONE : View.VISIBLE);
- }
- mDeleteDropTarget.enableAccessibleDrag(enable);
- mUninstallDropTarget.enableAccessibleDrag(enable);
- }
-}
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
index 5ef6dd5..cedeb39 100644
--- a/src/com/android/launcher3/SettingsActivity.java
+++ b/src/com/android/launcher3/SettingsActivity.java
@@ -59,7 +59,7 @@
// Launcher supports rotation by default. No need to show this setting.
getPreferenceScreen().removePreference(rotationPref);
} else {
- ContentResolver resolver = getContext().getContentResolver();
+ ContentResolver resolver = getActivity().getContentResolver();
mRotationLockObserver = new SystemDisplayRotationLockObserver(rotationPref, resolver);
// Register a content observer to listen for system setting changes while
@@ -70,14 +70,14 @@
// Initialize the UI once
mRotationLockObserver.onChange(true);
- rotationPref.setDefaultValue(Utilities.getAllowRotationDefaultValue(getContext()));
+ rotationPref.setDefaultValue(Utilities.getAllowRotationDefaultValue(getActivity()));
}
}
@Override
public void onDestroy() {
if (mRotationLockObserver != null) {
- getContext().getContentResolver().unregisterContentObserver(mRotationLockObserver);
+ getActivity().getContentResolver().unregisterContentObserver(mRotationLockObserver);
mRotationLockObserver = null;
}
super.onDestroy();
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 008dd84..c016aa9 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -18,6 +18,7 @@
import android.app.WallpaperManager;
import android.content.Context;
+import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
@@ -217,4 +218,11 @@
protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
super.setChildrenDrawnWithCacheEnabled(enabled);
}
+
+ @Override
+ public void setLayerType(int layerType, Paint paint) {
+ // When clip children is disabled do not use hardware layer,
+ // as hardware layer forces clip children.
+ super.setLayerType(getClipChildren() ? layerType : LAYER_TYPE_NONE, paint);
+ }
}
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 2cc9bb0..d051665 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -144,6 +144,7 @@
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
}
+ @Override
public Intent getIntent() {
return intent;
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index c9d8cce..4aaa02f 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -18,10 +18,7 @@
import android.annotation.TargetApi;
import android.app.Activity;
-import android.app.SearchManager;
import android.app.WallpaperManager;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
@@ -603,39 +600,6 @@
}
/**
- * Returns a widget with category {@link AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX}
- * provided by the same package which is set to be global search activity.
- * If widgetCategory is not supported, or no such widget is found, returns the first widget
- * provided by the package.
- */
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
- public static AppWidgetProviderInfo getSearchWidgetProvider(Context context) {
- SearchManager searchManager =
- (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
- ComponentName searchComponent = searchManager.getGlobalSearchActivity();
- if (searchComponent == null) return null;
- String providerPkg = searchComponent.getPackageName();
-
- AppWidgetProviderInfo defaultWidgetForSearchPackage = null;
-
- AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
- for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) {
- if (info.provider.getPackageName().equals(providerPkg)) {
- if (ATLEAST_JB_MR1) {
- if ((info.widgetCategory & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) {
- return info;
- } else if (defaultWidgetForSearchPackage == null) {
- defaultWidgetForSearchPackage = info;
- }
- } else {
- return info;
- }
- }
- }
- return defaultWidgetForSearchPackage;
- }
-
- /**
* Compresses the bitmap to a byte array for serialization.
*/
public static byte[] flattenBitmap(Bitmap bitmap) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 62c1bc8..85ba57c 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -18,6 +18,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
@@ -44,6 +45,7 @@
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Property;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.View;
@@ -73,7 +75,7 @@
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.pageindicators.PageIndicator;
+import com.android.launcher3.pageindicators.PageIndicatorLine;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.LongArrayMap;
@@ -109,7 +111,10 @@
private static final boolean MAP_RECURSE = true;
// The screen id used for the empty screen always present to the right.
- public final static long EXTRA_EMPTY_SCREEN_ID = -201;
+ public static final long EXTRA_EMPTY_SCREEN_ID = -201;
+ // The is the first screen. It is always present, even if its empty.
+ public static final long FIRST_SCREEN_ID = 0;
+
private final static long CUSTOM_CONTENT_SCREEN_ID = -301;
private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200;
@@ -173,26 +178,22 @@
private Matrix mTempMatrix = new Matrix();
private SpringLoadedDragController mSpringLoadedDragController;
- private float mSpringLoadedShrinkFactor;
private float mOverviewModeShrinkFactor;
// State variable that indicates whether the pages are small (ie when you're
// in all apps or customize mode)
- enum State {
- NORMAL (SearchDropTargetBar.State.SEARCH_BAR, false, false),
- NORMAL_HIDDEN (SearchDropTargetBar.State.INVISIBLE_TRANSLATED, false, false),
- SPRING_LOADED (SearchDropTargetBar.State.DROP_TARGET, false, true),
- OVERVIEW (SearchDropTargetBar.State.INVISIBLE, true, true),
- OVERVIEW_HIDDEN (SearchDropTargetBar.State.INVISIBLE, true, false);
+ public enum State {
+ NORMAL (false, false),
+ NORMAL_HIDDEN (false, false),
+ SPRING_LOADED (false, true),
+ OVERVIEW (true, true),
+ OVERVIEW_HIDDEN (true, false);
- public final SearchDropTargetBar.State searchDropTargetBarState;
public final boolean shouldUpdateWidget;
public final boolean hasMultipleVisiblePages;
- State(SearchDropTargetBar.State searchBarState, boolean shouldUpdateWidget,
- boolean hasMultipleVisiblePages) {
- searchDropTargetBarState = searchBarState;
+ State(boolean shouldUpdateWidget, boolean hasMultipleVisiblePages) {
this.shouldUpdateWidget = shouldUpdateWidget;
this.hasMultipleVisiblePages = hasMultipleVisiblePages;
}
@@ -281,6 +282,7 @@
private WorkspaceStateTransitionAnimation mStateTransitionAnimation;
private AccessibilityDelegate mPagesAccessibilityDelegate;
+ private OnStateChangeListener mOnStateChangeListener;
/**
* Used to inflate the Workspace from XML.
@@ -313,9 +315,6 @@
mWallpaperManager = WallpaperManager.getInstance(context);
mWallpaperOffset = new WallpaperOffsetInterpolator(this);
-
- mSpringLoadedShrinkFactor =
- res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
mOverviewModeShrinkFactor =
res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100f;
@@ -341,9 +340,14 @@
}
}
+ public void setOnStateChangeListener(OnStateChangeListener listener) {
+ mOnStateChangeListener = listener;
+ }
+
// estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each
// dimension if unsuccessful
public int[] estimateItemSize(ItemInfo itemInfo, boolean springLoaded) {
+ float shrinkFactor = mLauncher.getDeviceProfile().workspaceSpringLoadShrinkFactor;
int[] size = new int[2];
if (getChildCount() > 0) {
// Use the first non-custom page to estimate the child position
@@ -352,8 +356,8 @@
size[0] = r.width();
size[1] = r.height();
if (springLoaded) {
- size[0] *= mSpringLoadedShrinkFactor;
- size[1] *= mSpringLoadedShrinkFactor;
+ size[0] *= shrinkFactor;
+ size[1] *= shrinkFactor;
}
return size;
} else {
@@ -501,6 +505,38 @@
return mTouchState != TOUCH_STATE_REST;
}
+ /**
+ * Initializes and binds the first page
+ * @param qsb an exisitng qsb to recycle or null.
+ */
+ public void bindAndInitFirstWorkspaceScreen(View qsb) {
+ // Add the first page
+ CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, 0);
+
+ if (!mIsRtl || !mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ // Let the cell layout extend the start padding.
+ ((LayoutParams) firstPage.getLayoutParams()).matchStartEdge = true;
+ firstPage.setPaddingRelative(getPaddingStart(), 0, 0, 0);
+ }
+
+ if (qsb == null) {
+ // Always add a QSB on the first screen.
+ qsb = mLauncher.getLayoutInflater().inflate(R.layout.qsb_container,
+ firstPage, false);
+ }
+
+ CellLayout.LayoutParams lp = (CellLayout.LayoutParams) qsb.getLayoutParams();
+ lp.cellX = 0;
+ lp.cellY = 0;
+ lp.cellHSpan = firstPage.getCountX();
+ lp.cellVSpan = 1;
+ lp.canReorder = false;
+
+ if (!firstPage.addViewToCellLayout(qsb, 0, R.id.qsb_container, lp, true)) {
+ Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
+ }
+ }
+
public void removeAllWorkspaceScreens() {
// Disable all layout transitions before removing all pages to ensure that we don't get the
// transition animations competing with us changing the scroll when we add pages or the
@@ -513,30 +549,39 @@
removeCustomContentPage();
}
+ // Recycle the QSB widget
+ View qsb = findViewById(R.id.qsb_container);
+ if (qsb != null) {
+ ((ViewGroup) qsb.getParent()).removeView(qsb);
+ }
+
// Remove the pages and clear the screen models
removeAllViews();
mScreenOrder.clear();
mWorkspaceScreens.clear();
+ // Ensure that the first page is always present
+ bindAndInitFirstWorkspaceScreen(qsb);
+
// Re-enable the layout transitions
enableLayoutTransitions();
}
- public long insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) {
+ public void insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) {
// Find the index to insert this view into. If the empty screen exists, then
// insert it before that.
int insertIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
if (insertIndex < 0) {
insertIndex = mScreenOrder.size();
}
- return insertNewWorkspaceScreen(screenId, insertIndex);
+ insertNewWorkspaceScreen(screenId, insertIndex);
}
- public long insertNewWorkspaceScreen(long screenId) {
- return insertNewWorkspaceScreen(screenId, getChildCount());
+ public void insertNewWorkspaceScreen(long screenId) {
+ insertNewWorkspaceScreen(screenId, getChildCount());
}
- public long insertNewWorkspaceScreen(long screenId, int insertIndex) {
+ public CellLayout insertNewWorkspaceScreen(long screenId, int insertIndex) {
if (mWorkspaceScreens.containsKey(screenId)) {
throw new RuntimeException("Screen id " + screenId + " already exists!");
}
@@ -558,7 +603,8 @@
if (delegate != null && delegate.isInAccessibleDrag()) {
newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
}
- return screenId;
+
+ return newScreen;
}
public void createCustomContentContainer() {
@@ -765,6 +811,8 @@
if (stripEmptyScreens) {
stripEmptyScreens();
}
+ // Update the page indicator to reflect the removed page.
+ showPageIndicatorAtCurrentScroll();
}
}
};
@@ -860,7 +908,8 @@
for (int i = 0; i < total; i++) {
long id = mWorkspaceScreens.keyAt(i);
CellLayout cl = mWorkspaceScreens.valueAt(i);
- if (id >= 0 && cl.getShortcutsAndWidgets().getChildCount() == 0) {
+ // FIRST_SCREEN_ID can never be removed.
+ if (id > FIRST_SCREEN_ID && cl.getShortcutsAndWidgets().getChildCount() == 0) {
removeScreens.add(id);
}
}
@@ -1338,19 +1387,52 @@
// TODO(adamcohen): figure out a final effect here. We may need to recommend
// different effects based on device performance. On at least one relatively high-end
// device I've tried, translating the launcher causes things to get quite laggy.
- setTranslationAndAlpha(mLauncher.getSearchDropTargetBar(), transX, alpha);
- setTranslationAndAlpha(getPageIndicator(), transX, alpha);
- setTranslationAndAlpha(getChildAt(getCurrentPage()), transX, alpha);
- setTranslationAndAlpha(mLauncher.getHotseat(), transX, alpha);
+ setWorkspaceTranslation(TRANSLATION_X, transX, alpha);
+ setHotseatTranslation(TRANSLATION_X, transX, alpha);
+ }
+
+ /**
+ * Moves the workspace UI in the provided direction.
+ * @param direction either {@link #TRANSLATION_X} or {@link #TRANSLATION_Y}
+ * @param translation the amound of shift.
+ * @param alpha the alpha for the workspace page
+ */
+ public void setWorkspaceTranslation(
+ Property<View, Float> direction, float translation, float alpha) {
+ View currentChild = getChildAt(getCurrentPage());
+ if (currentChild != null) {
+ direction.set(currentChild, translation);
+ currentChild.setAlpha(alpha);
+ }
// When the animation finishes, reset all pages, just in case we missed a page.
- if (transX == 0) {
+ if (Float.compare(translation, 0) == 0) {
for (int i = getChildCount() - 1; i >= 0; i--) {
- setTranslationAndAlpha(getChildAt(i), 0, alpha);
+ View child = getChildAt(i);
+ direction.set(child, translation);
+ child.setAlpha(alpha);
}
}
}
+ /**
+ * Moves the Hotseat UI in the provided direction.
+ * @param direction either {@link #TRANSLATION_X} or {@link #TRANSLATION_Y}
+ * @param translation the amound of shift.
+ * @param alpha the alpha for the hotseat page
+ */
+ public void setHotseatTranslation(
+ Property<View, Float> direction, float translation, float alpha) {
+ View pageIndicator = getPageIndicator();
+ if (pageIndicator != null) {
+ direction.set(pageIndicator, translation);
+ pageIndicator.setAlpha(alpha);
+ }
+
+ direction.set(mLauncher.getHotseat(), translation);
+ mLauncher.getHotseat().setAlpha(alpha);
+ }
+
@Override
protected Matrix getPageShiftMatrix() {
if (Float.compare(mOverlayTranslation, 0) != 0) {
@@ -1363,13 +1445,6 @@
return super.getPageShiftMatrix();
}
- private void setTranslationAndAlpha(View v, float transX, float alpha) {
- if (v != null) {
- v.setTranslationX(transX);
- v.setAlpha(alpha);
- }
- }
-
@Override
protected void getEdgeVerticalPostion(int[] pos) {
View child = getChildAt(getPageCount() - 1);
@@ -1494,8 +1569,7 @@
// Reset our click listener
setOnClickListener(mLauncher);
}
- mLauncher.getSearchDropTargetBar().enableAccessibleDrag(enable);
- mLauncher.getAppInfoDropTargetBar().enableAccessibleDrag(enable);
+ mLauncher.getDropTargetBar().enableAccessibleDrag(enable);
mLauncher.getHotseat().getLayout()
.enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
}
@@ -1603,8 +1677,6 @@
if (listener != null) {
getPageIndicator().setOnClickListener(listener);
}
-
- showPageIndicatorAtCurrentScroll();
}
// Update wallpaper dimensions if they were changed since last onResume
@@ -1898,7 +1970,7 @@
int getOverviewModeTranslationY() {
DeviceProfile grid = mLauncher.getDeviceProfile();
- Rect workspacePadding = grid.getWorkspacePadding(Utilities.isRtl(getResources()));
+ Rect workspacePadding = grid.getWorkspacePadding();
int overviewButtonBarHeight = grid.getOverviewModeButtonBarHeight();
int scaledHeight = (int) (mOverviewModeShrinkFactor * getNormalChildHeight());
@@ -1911,15 +1983,26 @@
return -workspaceOffsetTopEdge + overviewOffsetTopEdge;
}
- int getSpringLoadedTranslationY() {
+ float getSpringLoadedTranslationY() {
DeviceProfile grid = mLauncher.getDeviceProfile();
- Rect workspacePadding = grid.getWorkspacePadding(Utilities.isRtl(getResources()));
- int scaledHeight = (int) (mSpringLoadedShrinkFactor * getNormalChildHeight());
- int workspaceTop = mInsets.top + workspacePadding.top;
- int workspaceBottom = getViewportHeight() - mInsets.bottom - workspacePadding.bottom;
- int workspaceHeight = workspaceBottom - workspaceTop;
- // Center the spring-loaded pages by translating it up by half of the reduced height.
- return -(workspaceHeight - scaledHeight) / 2;
+ if (grid.isVerticalBarLayout() || getChildCount() == 0) {
+ return 0;
+ }
+ Rect workspacePadding = grid.getWorkspacePadding();
+
+ float scaledHeight = grid.workspaceSpringLoadShrinkFactor * getNormalChildHeight();
+ float shrunkTop = mInsets.top + grid.dropTargetBarSizePx;
+ float shrunkBottom = getViewportHeight() - mInsets.bottom
+ - workspacePadding.bottom - grid.workspaceSpringLoadedBottomSpace;
+ float totalShrunkSpace = shrunkBottom - shrunkTop;
+
+ float desiredCellTop = shrunkTop + (totalShrunkSpace - scaledHeight) / 2;
+
+ float halfHeight = getHeight() / 2;
+ float myCenter = getTop() + halfHeight;
+ float cellTopFromCenter = halfHeight - getChildAt(0).getTop();
+ float actualCellTop = myCenter - cellTopFromCenter * grid.workspaceSpringLoadShrinkFactor;
+ return (desiredCellTop - actualCellTop) / grid.workspaceSpringLoadShrinkFactor;
}
float getOverviewModeShrinkFactor() {
@@ -1933,7 +2016,7 @@
public Animator setStateWithAnimation(State toState, boolean animated,
HashMap<View, Integer> layerViews) {
// Create the animation to the new state
- Animator workspaceAnim = mStateTransitionAnimation.getAnimationToState(mState,
+ AnimatorSet workspaceAnim = mStateTransitionAnimation.getAnimationToState(mState,
toState, animated, layerViews);
boolean shouldNotifyWidgetChange = !mState.shouldUpdateWidget
@@ -1946,6 +2029,10 @@
mLauncher.notifyWidgetProvidersChanged();
}
+ if (mOnStateChangeListener != null) {
+ mOnStateChangeListener.prepareStateChange(toState, animated ? workspaceAnim : null);
+ }
+
return workspaceAnim;
}
@@ -2009,6 +2096,14 @@
@Override
public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
+ if (mPageIndicator instanceof PageIndicatorLine) {
+ boolean isNewStateSpringLoaded = mState == State.SPRING_LOADED;
+ ((PageIndicatorLine) mPageIndicator).setShouldAutoHide(!isNewStateSpringLoaded);
+ if (isNewStateSpringLoaded) {
+ // Show the page indicator at the same time as the rest of the transition.
+ showPageIndicatorAtCurrentScroll();
+ }
+ }
}
@Override
@@ -2022,7 +2117,7 @@
updateChildrenLayersEnabled(false);
showCustomContentIfNecessary();
mForceDrawAdjacentPages = false;
- if (mState == State.NORMAL || mState == State.SPRING_LOADED) {
+ if (mState == State.SPRING_LOADED) {
showPageIndicatorAtCurrentScroll();
}
}
@@ -3938,7 +4033,7 @@
});
}
- private View getFirstMatch(final ItemOperator operator) {
+ public View getFirstMatch(final ItemOperator operator) {
final View[] value = new View[1];
mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
@Override
@@ -4351,4 +4446,14 @@
});
}
}
+
+ public interface OnStateChangeListener {
+
+ /**
+ * Called when the workspace state is changing.
+ * @param toState final state
+ * @param targetAnim animation which will be played during the transition or null.
+ */
+ void prepareStateChange(State toState, AnimatorSet targetAnim);
+ }
}
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index e268640..0f437c1 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -211,8 +211,7 @@
mOverviewTransitionTime = res.getInteger(R.integer.config_overviewTransitionTime);
mOverlayTransitionTime = res.getInteger(R.integer.config_overlayTransitionTime);
mSpringLoadedTransitionTime = mOverlayTransitionTime / 2;
- mSpringLoadedShrinkFactor =
- res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100f;
+ mSpringLoadedShrinkFactor = mLauncher.getDeviceProfile().workspaceSpringLoadShrinkFactor;
mOverviewModeShrinkFactor =
res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100f;
mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha) / 100f;
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index aa6e08e..6bf8abf 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -118,7 +118,9 @@
info.addAction(mActions.get(RESIZE));
}
}
- } if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) {
+ }
+
+ if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) {
info.addAction(mActions.get(ADD_TO_WORKSPACE));
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index c9bd02c..a74c4c5 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -18,18 +18,23 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
+import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.method.TextKeyListener;
import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
+import android.widget.LinearLayout;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BaseContainerView;
@@ -40,6 +45,7 @@
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
import com.android.launcher3.ExtendedEditText;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
@@ -47,6 +53,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
+import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.util.ComponentKey;
import java.nio.charset.Charset;
@@ -178,9 +185,13 @@
mApps.setAdapter(mAdapter);
mLayoutManager = mAdapter.getLayoutManager();
mItemDecoration = mAdapter.getItemDecoration();
- mRecyclerViewTopBottomPadding =
- res.getDimensionPixelSize(R.dimen.all_apps_list_top_bottom_padding);
-
+ if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+ mRecyclerViewTopBottomPadding = 0;
+ setPadding(0, 0, 0, 0);
+ } else {
+ mRecyclerViewTopBottomPadding =
+ res.getDimensionPixelSize(R.dimen.all_apps_list_top_bottom_padding);
+ }
mSearchQueryBuilder = new SpannableStringBuilder();
Selection.setSelection(mSearchQueryBuilder, 0);
}
@@ -220,6 +231,13 @@
mApps.removeApps(apps);
}
+ public void setSearchBarVisible(boolean visible) {
+ if (visible) {
+ mSearchBarController.setVisibility(View.VISIBLE);
+ } else {
+ mSearchBarController.setVisibility(View.INVISIBLE);
+ }
+ }
/**
* Sets the search bar that shows above the a-z list.
*/
@@ -240,6 +258,25 @@
}
/**
+ * Returns whether the view itself will handle the touch event or not.
+ */
+ public boolean shouldContainerScroll(float x, float y) {
+ int[] point = new int[2];
+ point[0] = (int) x;
+ point[1] = (int) y;
+ Utilities.mapCoordInSelfToDescendent(mAppsRecyclerView, this, point);
+
+ // if the MotionEvent is inside the thumb, container should not be pulled down.
+ if (mAppsRecyclerView.getScrollBar().isNearThumb(point[0], point[1])){
+ return false;
+ }
+ // If scroller is at the very top, then it's okay for the container to be pulled down.
+ if (Float.compare(0f, mAppsRecyclerView.getScrollBar().getThumbOffset().y) == 0) {
+ return true;
+ }
+ return false;
+ }
+ /**
* Focuses the search field and begins an app search.
*/
public void startAppsSearch() {
@@ -291,6 +328,10 @@
mAppsRecyclerView.addItemDecoration(mItemDecoration);
}
+ FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mAppsRecyclerView);
+ mAppsRecyclerView.addItemDecoration(focusedItemDecorator);
+ mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
+
// Precalculate the prediction icon and normal icon sizes
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
@@ -321,13 +362,33 @@
MeasureSpec.getSize(widthMeasureSpec) - mHorizontalPadding,
MeasureSpec.getSize(heightMeasureSpec));
+ DeviceProfile grid = mLauncher.getDeviceProfile();
+ int availableWidth = (!mContentBounds.isEmpty() ? mContentBounds.width() :
+ MeasureSpec.getSize(widthMeasureSpec))
+ - 2 * mAppsRecyclerView.getMaxScrollbarWidth();
+ if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+ if (mNumAppsPerRow != grid.inv.numColumns ||
+ mNumPredictedAppsPerRow != grid.inv.numColumns) {
+ mNumAppsPerRow = grid.inv.numColumns;
+ mNumPredictedAppsPerRow = grid.inv.numColumns;
+
+ mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
+ mAdapter.setNumAppsPerRow(mNumAppsPerRow);
+ mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, new FullMergeAlgorithm());
+ if (mNumAppsPerRow > 0) {
+ int iconSize = availableWidth / mNumAppsPerRow;
+ int iconSpacing = (iconSize - grid.allAppsIconSizePx) / 2;
+ }
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
+ }
+
+ // --- remove START when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
+
// Update the number of items in the grid before we measure the view
// TODO: mSectionNamesMargin is currently 0, but also account for it,
// if it's enabled in the future.
- int availableWidth = (!mContentBounds.isEmpty() ? mContentBounds.width() :
- MeasureSpec.getSize(widthMeasureSpec))
- - 2 * mAppsRecyclerView.getMaxScrollbarWidth();
- DeviceProfile grid = mLauncher.getDeviceProfile();
grid.updateAppsViewNumCols(getResources(), availableWidth);
if (mNumAppsPerRow != grid.allAppsNumCols ||
mNumPredictedAppsPerRow != grid.allAppsNumPredictiveCols) {
@@ -346,6 +407,8 @@
mAdapter.setNumAppsPerRow(mNumAppsPerRow);
mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeAlgorithm);
+ // TODO: should we not do all this complicated computation but just match the
+ // numAppsPerRow with the workspace?
if (mNumAppsPerRow > 0) {
int iconSize = availableWidth / mNumAppsPerRow;
int iconSpacing = (iconSize - grid.allAppsIconSizePx) / 2;
@@ -353,6 +416,7 @@
}
}
+ // --- remove END when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@@ -385,6 +449,25 @@
MarginLayoutParams lp = (MarginLayoutParams) mSearchContainer.getLayoutParams();
lp.leftMargin = bgPadding.left;
lp.rightMargin = bgPadding.right;
+
+ if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+ MarginLayoutParams mlp = (MarginLayoutParams) mAppsRecyclerView.getLayoutParams();
+
+ int navBarHeight = 84; /* replace with mInset.height() in dragLayer */
+ DeviceProfile grid = mLauncher.getDeviceProfile();
+ int height = navBarHeight + grid.hotseatCellHeightPx;
+
+ mlp.topMargin = height;
+ mAppsRecyclerView.setLayoutParams(mlp);
+
+ LinearLayout.LayoutParams llp =
+ (LinearLayout.LayoutParams) mSearchInput.getLayoutParams();
+ llp.topMargin = navBarHeight;
+ mSearchInput.setLayoutParams(llp);
+
+ lp.height = height;
+ mSearchContainer.setBackground(null);
+ }
mSearchContainer.setLayoutParams(lp);
}
@@ -437,6 +520,7 @@
// Return early if this is not initiated from a touch
if (!v.isInTouchMode()) return false;
// When we have exited all apps or are in transition, disregard long clicks
+
if (!mLauncher.isAppsViewVisible() ||
mLauncher.getWorkspace().isSwitchingState()) return false;
// Return if global dragging is not enabled
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 115db9d..c2a319c 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -33,6 +33,7 @@
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.View.OnFocusChangeListener;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
@@ -348,15 +349,10 @@
private BindViewCallback mBindViewCallback;
private AllAppsSearchBarController mSearchController;
+ private OnFocusChangeListener mIconFocusListener;
// The text to show when there are no search results and no market search handler.
private String mEmptySearchMessage;
- // The name of the market app which handles searches, to be used in the format str
- // below when updating the search-market view. Only needs to be loaded once.
- private String mMarketAppName;
- // The text to show when there is a market app which can handle a specific query, updated
- // each time the search query changes.
- private String mMarketSearchMessage;
// The intent to send off to the market app, updated each time the search query changes.
private Intent mMarketSearchIntent;
@@ -402,14 +398,10 @@
public void setSearchController(AllAppsSearchBarController searchController) {
mSearchController = searchController;
+ }
- // Resolve the market app handling additional searches
- PackageManager pm = mLauncher.getPackageManager();
- ResolveInfo marketInfo = pm.resolveActivity(mSearchController.createMarketSearchIntent(""),
- PackageManager.MATCH_DEFAULT_ONLY);
- if (marketInfo != null) {
- mMarketAppName = marketInfo.loadLabel(pm).toString();
- }
+ public void setIconFocusListener(OnFocusChangeListener focusListener) {
+ mIconFocusListener = focusListener;
}
/**
@@ -419,11 +411,7 @@
public void setLastSearchQuery(String query) {
Resources res = mLauncher.getResources();
mEmptySearchMessage = res.getString(R.string.all_apps_no_search_results, query);
- if (mMarketAppName != null) {
- mMarketSearchMessage = res.getString(R.string.all_apps_search_market_message,
- mMarketAppName);
- mMarketSearchIntent = mSearchController.createMarketSearchIntent(query);
- }
+ mMarketSearchIntent = mSearchController.createMarketSearchIntent(query);
}
/**
@@ -461,26 +449,17 @@
switch (viewType) {
case SECTION_BREAK_VIEW_TYPE:
return new ViewHolder(new View(parent.getContext()));
- case ICON_VIEW_TYPE: {
- BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
- R.layout.all_apps_icon, parent, false);
- icon.setOnTouchListener(mTouchListener);
- icon.setOnClickListener(mIconClickListener);
- icon.setOnLongClickListener(mIconLongClickListener);
- icon.setLongPressTimeout(ViewConfiguration.get(parent.getContext())
- .getLongPressTimeout());
- icon.setFocusable(true);
- return new ViewHolder(icon);
- }
+ case ICON_VIEW_TYPE:
case PREDICTION_ICON_VIEW_TYPE: {
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
- R.layout.all_apps_prediction_bar_icon, parent, false);
+ viewType == ICON_VIEW_TYPE ? R.layout.all_apps_icon :
+ R.layout.all_apps_prediction_bar_icon, parent, false);
icon.setOnTouchListener(mTouchListener);
icon.setOnClickListener(mIconClickListener);
icon.setOnLongClickListener(mIconLongClickListener);
icon.setLongPressTimeout(ViewConfiguration.get(parent.getContext())
.getLongPressTimeout());
- icon.setFocusable(true);
+ icon.setOnFocusChangeListener(mIconFocusListener);
return new ViewHolder(icon);
}
case EMPTY_SEARCH_VIEW_TYPE:
@@ -511,12 +490,16 @@
AppInfo info = mApps.getAdapterItems().get(position).appInfo;
BubbleTextView icon = (BubbleTextView) holder.mContent;
icon.applyFromApplicationInfo(info);
+ icon.setAccessibilityDelegate(
+ LauncherAppState.getInstance().getAccessibilityDelegate());
break;
}
case PREDICTION_ICON_VIEW_TYPE: {
AppInfo info = mApps.getAdapterItems().get(position).appInfo;
BubbleTextView icon = (BubbleTextView) holder.mContent;
icon.applyFromApplicationInfo(info);
+ icon.setAccessibilityDelegate(
+ LauncherAppState.getInstance().getAccessibilityDelegate());
break;
}
case EMPTY_SEARCH_VIEW_TYPE:
@@ -529,10 +512,8 @@
TextView searchView = (TextView) holder.mContent;
if (mMarketSearchIntent != null) {
searchView.setVisibility(View.VISIBLE);
- searchView.setContentDescription(mMarketSearchMessage);
searchView.setGravity(mApps.hasNoFilteredResults() ? Gravity.CENTER :
Gravity.START | Gravity.CENTER_VERTICAL);
- searchView.setText(mMarketSearchMessage);
} else {
searchView.setVisibility(View.GONE);
}
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
index a4bea8d..ac35932 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
@@ -49,6 +49,9 @@
protected DefaultAppSearchAlgorithm mSearchAlgorithm;
protected InputMethodManager mInputMethodManager;
+ public void setVisibility(int visibility) {
+ mInput.setVisibility(visibility);
+ }
/**
* Sets the references to the apps model and the search result callback.
*/
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
new file mode 100644
index 0000000..7f047d5
--- /dev/null
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -0,0 +1,375 @@
+package com.android.launcher3.allapps;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.PagedView;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.util.TouchController;
+
+/**
+ * Handles AllApps view transition.
+ * 1) Slides all apps view using direct manipulation
+ * 2) When finger is released, animate to either top or bottom accordingly.
+ *
+ * Algorithm:
+ * If release velocity > THRES1, snap according to the direction of movement.
+ * If release velocity < THRES1, snap according to either top or bottom depending on whether it's
+ * closer to top or closer to the page indicator.
+ */
+public class AllAppsTransitionController implements TouchController, VerticalPullDetector.Listener {
+
+ private static final String TAG = "AllAppsTrans";
+ private static final boolean DBG = false;
+
+ private final Interpolator mAccelInterpolator = new AccelerateInterpolator(1f);
+
+ private static final float ANIMATION_DURATION = 2000;
+ private static final float FINAL_ALPHA = .65f;
+
+ private AllAppsContainerView mAppsView;
+ private Workspace mWorkspace;
+ private Hotseat mHotseat;
+ private Drawable mHotseatBackground;
+ private float mHotseatAlpha;
+
+ private final Launcher mLauncher;
+ private final VerticalPullDetector mDetector;
+
+ // Animation in this class is controlled by a single variable {@link mProgressTransY}.
+ // Visually, it represents top y coordinate of the all apps container. Using the
+ // {@link mTranslation} as the denominator, this fraction value ranges in [0, 1].
+ private float mProgressTransY; // numerator
+ private float mTranslation = -1; // denominator
+
+ private static final float RECATCH_REJECTION_FRACTION = .0875f;
+
+ private long mAnimationDuration;
+ private float mCurY;
+
+ private AnimatorSet mCurrentAnimation;
+ private boolean mNoIntercept;
+
+ public AllAppsTransitionController(Launcher launcher) {
+ mLauncher = launcher;
+ mDetector = new VerticalPullDetector(launcher);
+ mDetector.setListener(this);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ init();
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mNoIntercept = false;
+ if (mLauncher.getWorkspace().isInOverviewMode() || mLauncher.isWidgetsViewVisible()) {
+ mNoIntercept = true;
+ } else if (mLauncher.isAllAppsVisible() &&
+ !mAppsView.shouldContainerScroll(ev.getX(), ev.getY())) {
+ mNoIntercept = true;
+ } else {
+ mDetector.setDetectableScrollConditions(mLauncher.isAllAppsVisible() /* down */,
+ isInDisallowRecatchTopZone(), isInDisallowRecatchBottomZone());
+ }
+ }
+ if (mNoIntercept) {
+ return false;
+ }
+ mDetector.onTouchEvent(ev);
+ return mDetector.shouldIntercept();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return mDetector.onTouchEvent(ev);
+ }
+
+ private boolean isInDisallowRecatchTopZone() {
+ return mProgressTransY / mTranslation < RECATCH_REJECTION_FRACTION;
+ }
+
+ private boolean isInDisallowRecatchBottomZone() {
+ return mProgressTransY / mTranslation > 1 - RECATCH_REJECTION_FRACTION;
+ }
+
+ private void init() {
+ if (mAppsView != null) {
+ return;
+ }
+ mAppsView = mLauncher.getAppsView();
+ mHotseat = mLauncher.getHotseat();
+ mWorkspace = mLauncher.getWorkspace();
+
+ if (mHotseatBackground == null) {
+ mHotseatBackground = mHotseat.getBackground();
+ mHotseatAlpha = mHotseatBackground.getAlpha() / 255f;
+ }
+ }
+
+ @Override
+ public void onScrollStart(boolean start) {
+ cancelAnimation();
+ mCurrentAnimation = LauncherAnimUtils.createAnimatorSet();
+ preparePull(start);
+ mCurY = mAppsView.getTranslationY();
+ }
+
+ /**
+ * @param start {@code true} if start of new drag.
+ */
+ public void preparePull(boolean start) {
+ mHotseat.setVisibility(View.VISIBLE);
+ mHotseat.bringToFront();
+ if (start) {
+ if (!mLauncher.isAllAppsVisible()) {
+ mHotseat.setBackground(null);
+ mAppsView.setVisibility(View.VISIBLE);
+ mAppsView.getContentView().setVisibility(View.VISIBLE);
+ mAppsView.getContentView().setBackground(null);
+ mAppsView.getRevealView().setVisibility(View.VISIBLE);
+ mAppsView.getRevealView().setAlpha(mHotseatAlpha);
+ mAppsView.setSearchBarVisible(false);
+
+ if (mTranslation < 0) {
+ mTranslation = mHotseat.getTop();
+ setProgress(mTranslation);
+ }
+ } else {
+ // TODO: get rid of this workaround to override state change by workspace transition
+ mWorkspace.onLauncherTransitionPrepare(mLauncher, false, false);
+ View child = ((CellLayout) mWorkspace.getChildAt(mWorkspace.getNextPage()))
+ .getShortcutsAndWidgets();
+ child.setVisibility(View.VISIBLE);
+ child.setAlpha(1f);
+
+ mAppsView.setSearchBarVisible(false);
+ setLightStatusBar(false);
+ }
+ }
+ }
+
+ private void setLightStatusBar(boolean enable) {
+ int systemUiFlags = mLauncher.getWindow().getDecorView().getSystemUiVisibility();
+ if (enable) {
+ mLauncher.getWindow().getDecorView().setSystemUiVisibility(systemUiFlags
+ | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+
+ } else {
+ mLauncher.getWindow().getDecorView().setSystemUiVisibility(systemUiFlags
+ & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+
+ }
+ }
+
+ @Override
+ public boolean onScroll(float displacement, float velocity) {
+ if (mAppsView == null) {
+ return false; // early termination.
+ }
+ if (0 <= mCurY + displacement && mCurY + displacement < mTranslation) {
+ setProgress(mCurY + displacement);
+ }
+ return true;
+ }
+
+ /**
+ * @param progress y value of the border between hotseat and all apps
+ */
+ public void setProgress(float progress) {
+ mProgressTransY = progress;
+ float alpha = calcAlphaAllApps(progress);
+ float workspaceHotseatAlpha = 1 - alpha;
+
+ mAppsView.getRevealView().setAlpha(Math.min(FINAL_ALPHA, Math.max(mHotseatAlpha, alpha)));
+ mAppsView.getContentView().setAlpha(alpha);
+ mAppsView.setTranslationY(progress);
+ mWorkspace.setWorkspaceTranslation(View.TRANSLATION_Y, -mTranslation + progress,
+ mAccelInterpolator.getInterpolation(workspaceHotseatAlpha));
+ mWorkspace.setHotseatTranslation(
+ View.TRANSLATION_Y, -mTranslation + progress, workspaceHotseatAlpha);
+ }
+
+ public float getProgress() {
+ return mProgressTransY;
+ }
+
+ private float calcAlphaAllApps(float progress) {
+ return ((mTranslation - progress)/mTranslation);
+ }
+
+ @Override
+ public void onScrollEnd(float velocity, boolean fling) {
+ if (mAppsView == null) {
+ return; // early termination.
+ }
+
+ if (fling) {
+ if (velocity < 0) {
+ calculateDuration(velocity, mAppsView.getTranslationY());
+ showAppsView(); // Flinging in UP direction
+ } else {
+ calculateDuration(velocity, Math.abs(mTranslation - mAppsView.getTranslationY()));
+ showWorkspace(); // Flinging in DOWN direction
+ }
+ // snap to top or bottom using the release velocity
+ } else {
+ if (mAppsView.getTranslationY() > mTranslation / 2) {
+ calculateDuration(velocity, Math.abs(mTranslation - mAppsView.getTranslationY()));
+ showWorkspace(); // Released in the bottom half
+ } else {
+ calculateDuration(velocity, Math.abs(mAppsView.getTranslationY()));
+ showAppsView(); // Released in the top half
+ }
+ }
+ }
+
+ private void calculateDuration(float velocity, float disp) {
+ // TODO: make these values constants after tuning.
+ float velocityDivisor = Math.max(1.5f, Math.abs(0.25f * velocity));
+ float travelDistance = Math.max(0.2f, disp / mTranslation);
+ mAnimationDuration = (long) Math.max(100, ANIMATION_DURATION / velocityDivisor * travelDistance);
+ if (DBG) {
+ Log.d(TAG, String.format("calculateDuration=%d, v=%f, d=%f", mAnimationDuration, velocity, disp));
+ }
+ }
+
+ /**
+ * Depending on the current state of the launcher, either just
+ * 1) animate
+ * 2) animate and do all the state updates.
+ */
+ private void showAppsView() {
+ if (mLauncher.isAllAppsVisible()) {
+ animateToAllApps(mCurrentAnimation, mAnimationDuration);
+ mCurrentAnimation.start();
+ } else {
+ mLauncher.showAppsView(true /* animated */, true /* resetListToTop */,
+ true /* updatePredictedApps */, false /* focusSearchBar */);
+ }
+ }
+
+ /**
+ * Depending on the current state of the launcher, either just
+ * 1) animate
+ * 2) animate and do all the state updates.
+ */
+ private void showWorkspace() {
+ if (mLauncher.isAllAppsVisible()) {
+ mLauncher.showWorkspace(true /* animated */);
+ } else {
+ animateToWorkspace(mCurrentAnimation, mAnimationDuration);
+ mCurrentAnimation.start();
+ }
+ }
+
+ public void animateToAllApps(AnimatorSet animationOut, long duration) {
+ if ((mAppsView = mLauncher.getAppsView()) == null || animationOut == null){
+ return;
+ }
+ if (mDetector.isRestingState()) {
+ preparePull(true);
+ mAnimationDuration = duration;
+ }
+ mCurY = mAppsView.getTranslationY();
+ final float fromAllAppsTop = mAppsView.getTranslationY();
+ final float toAllAppsTop = 0;
+
+ ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress",
+ fromAllAppsTop, toAllAppsTop);
+ driftAndAlpha.setDuration(mAnimationDuration);
+ driftAndAlpha.setInterpolator(new PagedView.ScrollInterpolator());
+ animationOut.play(driftAndAlpha);
+
+ animationOut.addListener(new AnimatorListenerAdapter() {
+ boolean canceled = false;
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ canceled = true;
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (canceled) {
+ return;
+ } else {
+ finishPullUp();
+ cleanUpAnimation();
+ mDetector.finishedScrolling();
+ }
+ }});
+ mCurrentAnimation = animationOut;
+ }
+
+ private void finishPullUp() {
+ mAppsView.setSearchBarVisible(true);
+ mHotseat.setVisibility(View.INVISIBLE);
+ setProgress(0f);
+ setLightStatusBar(true);
+ }
+
+ public void animateToWorkspace(AnimatorSet animationOut, long duration) {
+ if ((mAppsView = mLauncher.getAppsView()) == null || animationOut == null){
+ return;
+ }
+ if(mDetector.isRestingState()) {
+ preparePull(true);
+ mAnimationDuration = duration;
+ }
+ final float fromAllAppsTop = mAppsView.getTranslationY();
+ final float toAllAppsTop = mTranslation;
+
+ ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress",
+ fromAllAppsTop, toAllAppsTop);
+ driftAndAlpha.setDuration(mAnimationDuration);
+ driftAndAlpha.setInterpolator(new PagedView.ScrollInterpolator());
+ animationOut.play(driftAndAlpha);
+
+ animationOut.addListener(new AnimatorListenerAdapter() {
+ boolean canceled = false;
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ canceled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (canceled) {
+ return;
+ } else {
+ finishPullDown();
+ cleanUpAnimation();
+ mDetector.finishedScrolling();
+ }
+ }});
+ mCurrentAnimation = animationOut;
+ }
+
+ public void finishPullDown() {
+ mAppsView.setVisibility(View.INVISIBLE);
+ mHotseat.setBackground(mHotseatBackground);
+ mHotseat.setVisibility(View.VISIBLE);
+ setProgress(mTranslation);
+ setLightStatusBar(false);
+ }
+
+ private void cancelAnimation() {
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.cancel();
+ mCurrentAnimation = null;
+ }
+ }
+
+ private void cleanUpAnimation() {
+ mCurrentAnimation = null;
+ }
+}
diff --git a/src/com/android/launcher3/allapps/VerticalPullDetector.java b/src/com/android/launcher3/allapps/VerticalPullDetector.java
new file mode 100644
index 0000000..7df63e0
--- /dev/null
+++ b/src/com/android/launcher3/allapps/VerticalPullDetector.java
@@ -0,0 +1,244 @@
+package com.android.launcher3.allapps;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+/**
+ * One dimensional scroll gesture detector for all apps container pull up interaction.
+ */
+public class VerticalPullDetector {
+
+ private static final String TAG = "ScrollGesture";
+ private static final boolean DBG = false;
+
+ private float mTouchSlop;
+ private boolean mScrollDown; // if false, only scroll up will be reported.
+ private boolean mDisallowRecatchFromTop;
+ private boolean mDisallowRecatchFromBottom;
+
+ /**
+ * The minimum release velocity in pixels per millisecond that triggers fling..
+ */
+ private static final float RELEASE_VELOCITY_PX_MS = 1.0f;
+
+ /**
+ * The time constant used to calculate dampening in the low-pass filter of scroll velocity.
+ * Cutoff frequency is set at 10 Hz.
+ */
+ public static final float SCROLL_VELOCITY_DAMPENING_RC = 1000f / (2f * (float) Math.PI * 10);
+
+ /* Scroll state, this is set to true during dragging and animation. */
+ private State mState = State.NONE;
+ enum State {NONE, DRAG, SCROLLING};
+
+ private void setState(State newState) {
+ if (DBG) {
+ Log.d(TAG, mState + "->" + newState);
+ }
+ mState = newState;
+ }
+
+ public boolean shouldIntercept() {
+ return mState == State.DRAG;
+ }
+
+ public boolean isRestingState() {
+ return mState == State.NONE;
+ }
+
+ private float mDownX;
+ private float mDownY;
+ private float mDownMillis;
+
+ private float mLastY;
+ private float mLastMillis;
+
+ private float mVelocity;
+ private float mLastDisplacement;
+ private float mDisplacementY;
+ private float mDisplacementX;
+
+ /* scroll started during previous animation */
+ private boolean mSubtractSlop = true;
+
+ /* Client of this gesture detector can register a callback. */
+ Listener mListener;
+
+ public void setListener(Listener l) {
+ mListener = l;
+ }
+
+ interface Listener{
+ void onScrollStart(boolean start);
+ boolean onScroll(float displacement, float velocity);
+ void onScrollEnd(float velocity, boolean fling);
+ }
+
+ public VerticalPullDetector(Context context) {
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ }
+
+ public void setDetectableScrollConditions(boolean scrollDown, boolean disallowRecatchFromTop,
+ boolean disallowRecatchFromBottom) {
+ mScrollDown = scrollDown;
+ mDisallowRecatchFromTop = disallowRecatchFromTop;
+ mDisallowRecatchFromBottom = disallowRecatchFromBottom;
+ }
+
+ private boolean shouldScrollStart() {
+ float deltaY = Math.abs(mDisplacementY);
+ float deltaX = Math.max(Math.abs(mDisplacementX), 1);
+ if (mScrollDown && mDisplacementY > mTouchSlop) {
+ if (deltaY > deltaX) {
+ return true;
+ }
+ }
+ if (!mScrollDown && mDisplacementY < -mTouchSlop) {
+ if (deltaY > deltaX) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean shouldRecatchScrollStart() {
+ if (!mDisallowRecatchFromBottom && !mDisallowRecatchFromTop) {
+ return true;
+ }
+ if (mDisallowRecatchFromTop && mDisplacementY > mTouchSlop) {
+ mDisallowRecatchFromTop = false;
+ return true;
+ }
+ if (mDisallowRecatchFromBottom && mDisplacementY < -mTouchSlop) {
+ mDisallowRecatchFromBottom = false;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean onTouchEvent(MotionEvent ev) {
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mDownMillis = ev.getDownTime();
+ mDownX = ev.getX();
+ mDownY = ev.getY();
+ mLastDisplacement = 0;
+ mVelocity = 0;
+ if (mState == State.SCROLLING && shouldRecatchScrollStart()){
+ reportScrollStart(true /* recatch */);
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ mDisplacementX = ev.getX() - mDownX;
+ mDisplacementY = ev.getY() - mDownY;
+ mVelocity = computeVelocity(ev, mVelocity);
+
+ if (mState == State.SCROLLING && shouldRecatchScrollStart()){
+ setState(State.DRAG);
+ reportScrollStart(true /* recatch */);
+ }
+ if (mState == State.NONE && shouldScrollStart()) {
+ setState(State.DRAG);
+ reportScrollStart(false /* recatch */);
+ }
+ if (mState == State.DRAG && mListener != null) {
+ reportScroll();
+ }
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ // These are synthetic events and there is no need to update internal values.
+ if (mState == State.DRAG && mListener != null) {
+ reportScrollEnd();
+ }
+ break;
+ default:
+ //TODO: add multi finger tracking by tracking active pointer.
+ break;
+ }
+ // Do house keeping.
+ mLastDisplacement = mDisplacementY;
+
+ mLastY = ev.getY();
+ mLastMillis = ev.getEventTime();
+
+ return true;
+ }
+
+ public void finishedScrolling() {
+ setState(State.NONE);
+ }
+
+ private boolean reportScrollStart(boolean recatch) {
+ mListener.onScrollStart(!recatch);
+ mSubtractSlop = !recatch;
+ if (DBG) {
+ Log.d(TAG, "onScrollStart recatch:" + recatch);
+ }
+ return true;
+ }
+
+ private boolean reportScroll() {
+ float delta = mDisplacementY - mLastDisplacement;
+ if (delta != 0) {
+ if (DBG) {
+ Log.d(TAG, String.format("onScroll disp=%.1f, velocity=%.1f",
+ mDisplacementY, mVelocity));
+ }
+ float subtractDisplacement = 0f;
+ if (mSubtractSlop) {
+ if (mDisplacementY > 0) {
+ subtractDisplacement = mTouchSlop;
+ } else {
+ subtractDisplacement = -mTouchSlop;
+ }
+ }
+ return mListener.onScroll(mDisplacementY - subtractDisplacement, mVelocity);
+ }
+ return true;
+ }
+
+ private void reportScrollEnd() {
+ if (DBG) {
+ Log.d(TAG, String.format("onScrolEnd disp=%.1f, velocity=%.1f",
+ mDisplacementY, mVelocity));
+ }
+ mListener.onScrollEnd(mVelocity, Math.abs(mVelocity) > RELEASE_VELOCITY_PX_MS);
+ setState(State.SCROLLING);
+ }
+ /**
+ * Computes the damped velocity using the two motion events and the previous velocity.
+ */
+ private float computeVelocity(MotionEvent to, float previousVelocity) {
+ float delta = computeDelta(to);
+
+ float deltaTimeMillis = to.getEventTime() - mLastMillis;
+ float velocity = (deltaTimeMillis > 0) ? (delta / deltaTimeMillis) : 0;
+ if (Math.abs(previousVelocity) < 0.001f) {
+ return velocity;
+ }
+
+ float alpha = computeDampeningFactor(deltaTimeMillis);
+ return interpolate(previousVelocity, velocity, alpha);
+ }
+
+ private float computeDelta(MotionEvent to) {
+ return to.getY() - mLastY;
+ }
+
+ /**
+ * Returns a time-dependent dampening factor using delta time.
+ */
+ private static float computeDampeningFactor(float deltaTime) {
+ return deltaTime / (SCROLL_VELOCITY_DAMPENING_RC + deltaTime);
+ }
+
+ /**
+ * Returns the linear interpolation between two values
+ */
+ private static float interpolate(float from, float to, float alpha) {
+ return (1.0f - alpha) * from + alpha * to;
+ }
+}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index f4470f3..af5ff58 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -46,6 +46,7 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.TouchController;
import java.util.ArrayList;
import java.util.HashSet;
@@ -53,7 +54,7 @@
/**
* Class for initiating a drag within a view or across multiple views.
*/
-public class DragController implements DragDriver.EventListener {
+public class DragController implements DragDriver.EventListener, TouchController {
private static final String TAG = "Launcher.DragController";
/** Indicates the drag is a move. */
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 33ce683..765ad64 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -29,7 +29,6 @@
import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -43,6 +42,7 @@
import android.widget.TextView;
import com.android.launcher3.AppWidgetResizeFrame;
+import com.android.launcher3.BaseContainerView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.ItemInfo;
@@ -51,15 +51,19 @@
import com.android.launcher3.LauncherAppWidgetHostView;
import com.android.launcher3.PinchToOverviewListener;
import com.android.launcher3.R;
-import com.android.launcher3.SearchDropTargetBar;
+import com.android.launcher3.DropTargetBar;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.TouchController;
import java.util.ArrayList;
@@ -108,6 +112,8 @@
// Related to adjacent page hints
private final Rect mScrollChildPosition = new Rect();
+ private final ViewGroupFocusHelper mFocusIndicatorHelper;
+
private boolean mInScrollArea;
private boolean mShowPageHints;
private Drawable mLeftHoverDrawable;
@@ -117,6 +123,11 @@
// Related to pinch-to-go-to-overview gesture.
private PinchToOverviewListener mPinchListener = null;
+
+ // Handles all apps pull up interaction
+ private AllAppsTransitionController mAllAppsController;
+
+ private TouchController mActiveController;
/**
* Used to create a new DragLayer from XML.
*
@@ -136,17 +147,24 @@
mLeftHoverDrawableActive = res.getDrawable(R.drawable.page_hover_left_active);
mRightHoverDrawableActive = res.getDrawable(R.drawable.page_hover_right_active);
mIsRtl = Utilities.isRtl(res);
+ mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
}
- public void setup(Launcher launcher, DragController controller) {
+ public void setup(Launcher launcher, DragController dragController,
+ AllAppsTransitionController allAppsTransitionController) {
mLauncher = launcher;
- mDragController = controller;
+ mDragController = dragController;
+ mAllAppsController = allAppsTransitionController;
boolean isAccessibilityEnabled = ((AccessibilityManager) mLauncher.getSystemService(
Context.ACCESSIBILITY_SERVICE)).isEnabled();
onAccessibilityStateChanged(isAccessibilityEnabled);
}
+ public ViewGroupFocusHelper getFocusIndicatorHelper() {
+ return mFocusIndicatorHelper;
+ }
+
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
@@ -173,31 +191,17 @@
private boolean isEventOverFolderTextRegion(Folder folder, MotionEvent ev) {
getDescendantRectRelativeToSelf(folder.getEditTextRegion(), mHitRect);
- if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
- return true;
- }
- return false;
+ return mHitRect.contains((int) ev.getX(), (int) ev.getY());
}
private boolean isEventOverFolder(Folder folder, MotionEvent ev) {
getDescendantRectRelativeToSelf(folder, mHitRect);
- if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
- return true;
- }
- return false;
+ return mHitRect.contains((int) ev.getX(), (int) ev.getY());
}
private boolean isEventOverDropTargetBar(MotionEvent ev) {
- getDescendantRectRelativeToSelf(mLauncher.getSearchDropTargetBar(), mHitRect);
- if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
- return true;
- }
-
- getDescendantRectRelativeToSelf(mLauncher.getAppInfoDropTargetBar(), mHitRect);
- if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
- return true;
- }
- return false;
+ getDescendantRectRelativeToSelf(mLauncher.getDropTargetBar(), mHitRect);
+ return mHitRect.contains((int) ev.getX(), (int) ev.getY());
}
private boolean handleTouchDown(MotionEvent ev, boolean intercept) {
@@ -246,6 +250,7 @@
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
+
if (action == MotionEvent.ACTION_DOWN) {
if (handleTouchDown(ev, true)) {
return true;
@@ -258,11 +263,21 @@
}
clearAllResizeFrames();
- if (mPinchListener != null && mPinchListener.onInterceptTouchEvent(ev)) {
- // Stop listening for scrolling etc. (onTouchEvent() handles the rest of the pinch.)
+ if (mDragController.onInterceptTouchEvent(ev)) {
+ mActiveController = mDragController;
return true;
}
- return mDragController.onInterceptTouchEvent(ev);
+ if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && mAllAppsController.onInterceptTouchEvent(ev)) {
+ mActiveController = mAllAppsController;
+ return true;
+ }
+
+ if (mPinchListener != null && mPinchListener.onInterceptTouchEvent(ev)) {
+ // Stop listening for scrolling etc. (onTouchEvent() handles the rest of the pinch.)
+ mActiveController = mPinchListener;
+ return true;
+ }
+ return false;
}
@Override
@@ -334,7 +349,7 @@
return super.onRequestSendAccessibilityEvent(child, event);
}
- if (isInAccessibleDrag() && child instanceof SearchDropTargetBar) {
+ if (isInAccessibleDrag() && child instanceof DropTargetBar) {
return super.onRequestSendAccessibilityEvent(child, event);
}
// Skip propagating onRequestSendAccessibilityEvent all for other children
@@ -352,8 +367,7 @@
childrenForAccessibility.add(currentFolder);
if (isInAccessibleDrag()) {
- childrenForAccessibility.add(mLauncher.getSearchDropTargetBar());
- childrenForAccessibility.add(mLauncher.getAppInfoDropTargetBar());
+ childrenForAccessibility.add(mLauncher.getDropTargetBar());
}
} else {
super.addChildrenForAccessibility(childrenForAccessibility);
@@ -375,11 +389,6 @@
int x = (int) ev.getX();
int y = (int) ev.getY();
- // This is only reached if a pinch was started from onInterceptTouchEvent();
- // this continues sending events for it.
- if (mPinchListener != null) {
- mPinchListener.onTouchEvent(ev);
- }
if (action == MotionEvent.ACTION_DOWN) {
if (handleTouchDown(ev, false)) {
@@ -406,7 +415,10 @@
}
}
if (handled) return true;
- return mDragController.onTouchEvent(ev);
+ if (mActiveController != null) {
+ return mActiveController.onTouchEvent(ev);
+ }
+ return false;
}
@Override
@@ -950,13 +962,19 @@
canvas.save();
if (currCellLayout != null && currCellLayout != mLauncher.getHotseat().getLayout()) {
// Cut a hole in the darkening scrim on the page that should be highlighted, if any.
- getDescendantRectRelativeToSelf(currCellLayout, mHighlightRect);
+ float scale = getDescendantRectRelativeToSelf(currCellLayout, mHighlightRect);
+ Rect backBounds = currCellLayout.getBackgroundBounds();
+ mHighlightRect.left += (int) (backBounds.left * scale);
+ mHighlightRect.top += (int) (backBounds.top * scale);
+ mHighlightRect.right = (int) (mHighlightRect.left + backBounds.width() * scale);
+ mHighlightRect.bottom = (int) (mHighlightRect.top + backBounds.height() * scale);
canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE);
}
canvas.drawColor((alpha << 24) | SCRIM_COLOR);
canvas.restore();
}
+ mFocusIndicatorHelper.draw(canvas);
super.dispatchDraw(canvas);
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 6df296e..93238de 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -98,7 +98,7 @@
private static final String TAG = "Launcher.Folder";
/**
- * We avoid measuring {@link #mContentWrapper} with a 0 width or height, as this
+ * We avoid measuring {@link #mContent} with a 0 width or height, as this
* results in CellLayout being measured as UNSPECIFIED, which it does not support.
*/
private static final int MIN_CONTENT_DIMEN = 5;
@@ -147,7 +147,6 @@
@Thunk FolderIcon mFolderIcon;
@Thunk FolderPagedView mContent;
- @Thunk View mContentWrapper;
public ExtendedEditText mFolderName;
private PageIndicatorDots mPageIndicator;
@@ -226,7 +225,6 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mContentWrapper = findViewById(R.id.folder_content_wrapper);
mContent = (FolderPagedView) findViewById(R.id.folder_content);
mContent.setFolder(this);
@@ -318,7 +316,7 @@
@Override
public void enableAccessibleDrag(boolean enable) {
- mLauncher.getSearchDropTargetBar().enableAccessibleDrag(enable);
+ mLauncher.getDropTargetBar().enableAccessibleDrag(enable);
for (int i = 0; i < mContent.getChildCount(); i++) {
mContent.getPageAt(i).enableAccessibleDrag(enable, CellLayout.FOLDER_ACCESSIBILITY_DRAG);
}
@@ -562,8 +560,8 @@
reveal.setDuration(mMaterialExpandDuration);
reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
- mContentWrapper.setAlpha(0f);
- Animator iconsAlpha = ObjectAnimator.ofFloat(mContentWrapper, "alpha", 0f, 1f);
+ mContent.setAlpha(0f);
+ Animator iconsAlpha = ObjectAnimator.ofFloat(mContent, "alpha", 0f, 1f);
iconsAlpha.setDuration(mMaterialExpandDuration);
iconsAlpha.setStartDelay(mMaterialExpandStagger);
iconsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
@@ -581,12 +579,12 @@
openFolderAnim = anim;
- mContentWrapper.setLayerType(LAYER_TYPE_HARDWARE, null);
+ mContent.setLayerType(LAYER_TYPE_HARDWARE, null);
mFooter.setLayerType(LAYER_TYPE_HARDWARE, null);
onCompleteRunnable = new Runnable() {
@Override
public void run() {
- mContentWrapper.setLayerType(LAYER_TYPE_NONE, null);
+ mContent.setLayerType(LAYER_TYPE_NONE, null);
mFooter.setLayerType(LAYER_TYPE_NONE, null);
}
};
@@ -1054,7 +1052,7 @@
int top = Math.min(Math.max(sTempRect.top, centeredTop),
sTempRect.top + sTempRect.height() - height);
- int distFromEdgeOfScreen = grid.getWorkspacePadding(isLayoutRtl()).left + getPaddingLeft();
+ int distFromEdgeOfScreen = grid.getWorkspacePadding().left + getPaddingLeft();
if (grid.isPhone && (grid.availableWidthPx - width) < 4 * distFromEdgeOfScreen) {
// Center the folder if it is very close to being centered anyway, by virtue of
@@ -1093,7 +1091,7 @@
private int getContentAreaHeight() {
DeviceProfile grid = mLauncher.getDeviceProfile();
- Rect workspacePadding = grid.getWorkspacePadding(mContent.mIsRtl);
+ Rect workspacePadding = grid.getWorkspacePadding();
int maxContentAreaHeight = grid.availableHeightPx -
workspacePadding.top - workspacePadding.bottom -
mFooterHeight;
@@ -1122,7 +1120,7 @@
int contentAreaHeightSpec = MeasureSpec.makeMeasureSpec(contentHeight, MeasureSpec.EXACTLY);
mContent.setFixedSize(contentWidth, contentHeight);
- mContentWrapper.measure(contentAreaWidthSpec, contentAreaHeightSpec);
+ mContent.measure(contentAreaWidthSpec, contentAreaHeightSpec);
if (mContent.getChildCount() > 0) {
int cellIconGap = (mContent.getPageAt(0).getCellWidth()
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index d8b83ad..c56e4e5 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -18,6 +18,7 @@
import android.annotation.SuppressLint;
import android.content.Context;
+import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
@@ -30,7 +31,6 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener;
-import com.android.launcher3.FocusIndicatorView;
import com.android.launcher3.IconCache;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
@@ -45,6 +45,7 @@
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.pageindicators.PageIndicator;
+import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -73,6 +74,7 @@
private final LayoutInflater mInflater;
private final IconCache mIconCache;
+ private final ViewGroupFocusHelper mFocusIndicatorHelper;
@Thunk final HashMap<View, Runnable> mPendingAnimations = new HashMap<>();
@@ -90,7 +92,6 @@
private int mGridCountY;
private Folder mFolder;
- private FocusIndicatorView mFocusIndicatorView;
private PagedFolderKeyEventListener mKeyListener;
private PageIndicator mPageIndicator;
@@ -112,11 +113,11 @@
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
setEdgeGlowColor(getResources().getColor(R.color.folder_edge_effect_color));
+ mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
}
public void setFolder(Folder folder) {
mFolder = folder;
- mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator);
mKeyListener = new PagedFolderKeyEventListener(folder);
mPageIndicator = (PageIndicator) folder.findViewById(R.id.folder_page_indicator);
}
@@ -162,6 +163,12 @@
}
}
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ mFocusIndicatorHelper.draw(canvas);
+ super.dispatchDraw(canvas);
+ }
+
/**
* Binds items to the layout.
* @return list of items that could not be bound, probably because we hit the max size limit.
@@ -226,7 +233,7 @@
textView.applyFromShortcutInfo(item, mIconCache);
textView.setOnClickListener(mFolder);
textView.setOnLongClickListener(mFolder);
- textView.setOnFocusChangeListener(mFocusIndicatorView);
+ textView.setOnFocusChangeListener(mFocusIndicatorHelper);
textView.setOnKeyListener(mKeyListener);
textView.setLayoutParams(new CellLayout.LayoutParams(
diff --git a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
new file mode 100644
index 0000000..7672f5a
--- /dev/null
+++ b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
@@ -0,0 +1,242 @@
+/*
+ * 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.keyboard;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.RectEvaluator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.TargetApi;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Build.VERSION_CODES;
+import android.util.Property;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+
+import com.android.launcher3.R;
+
+/**
+ * A helper class to draw background of a focused view.
+ */
+@TargetApi(VERSION_CODES.LOLLIPOP)
+public abstract class FocusIndicatorHelper implements
+ OnFocusChangeListener, AnimatorUpdateListener {
+
+ private static final float MIN_VISIBLE_ALPHA = 0.2f;
+ private static final long ANIM_DURATION = 150;
+
+ public static final Property<FocusIndicatorHelper, Float> ALPHA =
+ new Property<FocusIndicatorHelper, Float>(Float.TYPE, "alpha") {
+ @Override
+ public void set(FocusIndicatorHelper object, Float value) {
+ object.setAlpha(value);
+ }
+
+ @Override
+ public Float get(FocusIndicatorHelper object) {
+ return object.mAlpha;
+ }
+ };
+
+ public static final Property<FocusIndicatorHelper, Float> SHIFT =
+ new Property<FocusIndicatorHelper, Float>(
+ Float.TYPE, "shift") {
+
+ @Override
+ public void set(FocusIndicatorHelper object, Float value) {
+ object.mShift = value;
+ }
+
+ @Override
+ public Float get(FocusIndicatorHelper object) {
+ return object.mShift;
+ }
+ };
+
+ private static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
+ private static final Rect sTempRect1 = new Rect();
+ private static final Rect sTempRect2 = new Rect();
+
+ private final View mContainer;
+ private final Paint mPaint;
+ private final int mMaxAlpha;
+
+ private final Rect mDirtyRect = new Rect();
+ private boolean mIsDirty = false;
+
+ private View mLastFocusedView;
+
+ private View mCurrentView;
+ private View mTargetView;
+ /**
+ * The fraction indicating the position of the focusRect between {@link #mCurrentView}
+ * & {@link #mTargetView}
+ */
+ private float mShift;
+
+ private ObjectAnimator mCurrentAnimation;
+ private float mAlpha;
+
+ public FocusIndicatorHelper(View container) {
+ mContainer = container;
+
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ int color = container.getResources().getColor(R.color.focused_background);
+ mMaxAlpha = Color.alpha(color);
+ mPaint.setColor(0xFF000000 | color);
+
+ setAlpha(0);
+ mShift = 0;
+ }
+
+ protected void setAlpha(float alpha) {
+ mAlpha = alpha;
+ mPaint.setAlpha((int) (mAlpha * mMaxAlpha));
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ invalidateDirty();
+ }
+
+ protected void invalidateDirty() {
+ if (mIsDirty) {
+ mContainer.invalidate(mDirtyRect);
+ mIsDirty = false;
+ }
+
+ Rect newRect = getDrawRect();
+ if (newRect != null) {
+ mContainer.invalidate(newRect);
+ }
+ }
+
+ public void draw(Canvas c) {
+ if (mAlpha > 0) {
+ Rect newRect = getDrawRect();
+ if (newRect != null) {
+ mDirtyRect.set(newRect);
+ c.drawRect(mDirtyRect, mPaint);
+ mIsDirty = true;
+ }
+ }
+ }
+
+ private Rect getDrawRect() {
+ if (mCurrentView != null) {
+ viewToRect(mCurrentView, sTempRect1);
+
+ if (mShift > 0 && mTargetView != null) {
+ viewToRect(mTargetView, sTempRect2);
+ return RECT_EVALUATOR.evaluate(mShift, sTempRect1, sTempRect2);
+ } else {
+ return sTempRect1;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (hasFocus) {
+ endCurrentAnimation();
+
+ if (mAlpha > MIN_VISIBLE_ALPHA) {
+ mTargetView = v;
+
+ mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
+ PropertyValuesHolder.ofFloat(ALPHA, 1),
+ PropertyValuesHolder.ofFloat(SHIFT, 1));
+ mCurrentAnimation.addListener(new ViewSetListener(v, true));
+ } else {
+ setCurrentView(v);
+
+ mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
+ PropertyValuesHolder.ofFloat(ALPHA, 1));
+ }
+
+ mLastFocusedView = v;
+ } else {
+ if (mLastFocusedView == v) {
+ mLastFocusedView = null;
+ endCurrentAnimation();
+ mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
+ PropertyValuesHolder.ofFloat(ALPHA, 0));
+ mCurrentAnimation.addListener(new ViewSetListener(null, false));
+ }
+ }
+
+ // invalidate once
+ invalidateDirty();
+
+ mLastFocusedView = hasFocus ? v : null;
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.addUpdateListener(this);
+ mCurrentAnimation.setDuration(ANIM_DURATION).start();
+ }
+ }
+
+ protected void endCurrentAnimation() {
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.cancel();
+ mCurrentAnimation = null;
+ }
+ }
+
+ protected void setCurrentView(View v) {
+ mCurrentView = v;
+ mShift = 0;
+ mTargetView = null;
+ }
+
+ /**
+ * Gets the position of {@param v} relative to {@link #mContainer}.
+ */
+ public abstract void viewToRect(View v, Rect outRect);
+
+ private class ViewSetListener extends AnimatorListenerAdapter {
+ private final View mViewToSet;
+ private final boolean mCallOnCancel;
+ private boolean mCalled = false;
+
+ public ViewSetListener(View v, boolean callOnCancel) {
+ mViewToSet = v;
+ mCallOnCancel = callOnCancel;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (!mCallOnCancel) {
+ mCalled = true;
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mCalled) {
+ setCurrentView(mViewToSet);
+ mCalled = true;
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/keyboard/FocusedItemDecorator.java b/src/com/android/launcher3/keyboard/FocusedItemDecorator.java
new file mode 100644
index 0000000..9c80b0f
--- /dev/null
+++ b/src/com/android/launcher3/keyboard/FocusedItemDecorator.java
@@ -0,0 +1,52 @@
+/*
+ * 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.keyboard;
+
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.ItemDecoration;
+import android.support.v7.widget.RecyclerView.State;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+
+/**
+ * {@link ItemDecoration} for drawing and animating focused view background.
+ */
+public class FocusedItemDecorator extends ItemDecoration {
+
+ private FocusIndicatorHelper mHelper;
+
+ public FocusedItemDecorator(View container) {
+ mHelper = new FocusIndicatorHelper(container) {
+
+ @Override
+ public void viewToRect(View v, Rect outRect) {
+ outRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
+ }
+ };
+ }
+
+ public OnFocusChangeListener getFocusListener() {
+ return mHelper;
+ }
+
+ @Override
+ public void onDraw(Canvas c, RecyclerView parent, State state) {
+ mHelper.draw(c);
+ }
+}
diff --git a/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
new file mode 100644
index 0000000..bd5c06e
--- /dev/null
+++ b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
@@ -0,0 +1,85 @@
+/*
+ * 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.keyboard;
+
+import android.graphics.Rect;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+
+import com.android.launcher3.PagedView;
+
+/**
+ * {@link FocusIndicatorHelper} for a generic view group.
+ */
+public class ViewGroupFocusHelper extends FocusIndicatorHelper {
+
+ private final View mContainer;
+
+ public ViewGroupFocusHelper(View container) {
+ super(container);
+ mContainer = container;
+ }
+
+ @Override
+ public void viewToRect(View v, Rect outRect) {
+ outRect.left = 0;
+ outRect.top = 0;
+
+ computeLocationRelativeToContainer(v, outRect);
+
+ // If a view is scaled, its position will also shift accordingly. For optimization, only
+ // consider this for the last node.
+ outRect.left += (1 - v.getScaleX()) * v.getWidth() / 2;
+ outRect.top += (1 - v.getScaleY()) * v.getHeight() / 2;
+
+ outRect.right = outRect.left + (int) (v.getScaleX() * v.getWidth());
+ outRect.bottom = outRect.top + (int) (v.getScaleY() * v.getHeight());
+ }
+
+ private void computeLocationRelativeToContainer(View child, Rect outRect) {
+ View parent = (View) child.getParent();
+ outRect.left += child.getLeft();
+ outRect.top += child.getTop();
+
+ if (parent != mContainer) {
+ if (parent instanceof PagedView) {
+ PagedView page = (PagedView) parent;
+ outRect.left -= page.getScrollForPage(page.indexOfChild(child));
+ }
+
+ computeLocationRelativeToContainer(parent, outRect);
+ }
+ }
+
+ /**
+ * Sets the alpha of this FocusIndicatorHelper to 0 when a view with this listener
+ * receives focus.
+ */
+ public View.OnFocusChangeListener getHideIndicatorOnFocusListener() {
+ return new OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (hasFocus) {
+ endCurrentAnimation();
+ setCurrentView(null);
+ setAlpha(0);
+ invalidateDirty();
+ }
+ }
+ };
+ }
+}
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index 68d9b8c..8629e92 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -40,6 +40,15 @@
private static File sLogsDirectory = null;
public static void setDir(File logsDir) {
+ if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ synchronized (DATE_FORMAT) {
+ // If the target directory changes, stop any active thread.
+ if (sHandler != null && !logsDir.equals(sLogsDirectory)) {
+ ((HandlerThread) sHandler.getLooper().getThread()).quit();
+ sHandler = null;
+ }
+ }
+ }
sLogsDirectory = logsDir;
}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index f900790..dd11bde 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -22,6 +22,7 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
import com.android.launcher3.backup.nano.BackupProtos;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.PackageInstallerCompat;
@@ -221,7 +222,7 @@
// {@link #mCarryOver}, to prevent an infinite loop. If no item could be removed,
// break the loop and abort migration by throwing an exception.
OptimalPlacementSolution placement = new OptimalPlacementSolution(
- new GridOccupancy(mTrgX, mTrgY), deepCopy(mCarryOver), true);
+ new GridOccupancy(mTrgX, mTrgY), deepCopy(mCarryOver), 0, true);
placement.find();
if (placement.finalPlacedItems.size() > 0) {
long newScreenId = LauncherSettings.Settings.call(
@@ -262,13 +263,16 @@
* Migrate a particular screen id.
* Strategy:
* 1) For all possible combinations of row and column, pick the one which causes the least
- * data loss: {@link #tryRemove(int, int, ArrayList, float[])}
+ * data loss: {@link #tryRemove(int, int, int, ArrayList, float[])}
* 2) Maintain a list of all lost items before this screen, and add any new item lost from
* this screen to that list as well.
* 3) If all those items from the above list can be placed on this screen, place them
* (otherwise they are placed on a new screen).
*/
private void migrateScreen(long screenId) {
+ // If we are migrating the first screen, do not touch the first row.
+ int startY = screenId == Workspace.FIRST_SCREEN_ID ? 1 : 0;
+
ArrayList<DbEntry> items = loadWorkspaceEntries(screenId);
int removedCol = Integer.MAX_VALUE;
@@ -286,10 +290,10 @@
// Try removing all possible combinations
for (int x = 0; x < mSrcX; x++) {
- for (int y = 0; y < mSrcY; y++) {
+ for (int y = startY; y < mSrcY; y++) {
// Use a deep copy when trying out a particular combination as it can change
// the underlying object.
- ArrayList<DbEntry> itemsOnScreen = tryRemove(x, y, deepCopy(items), outLoss);
+ ArrayList<DbEntry> itemsOnScreen = tryRemove(x, y, startY, deepCopy(items), outLoss);
if ((outLoss[0] < removeWt) || ((outLoss[0] == removeWt) && (outLoss[1] < moveWt))) {
removeWt = outLoss[0];
@@ -338,12 +342,13 @@
if (!mCarryOver.isEmpty() && removeWt == 0) {
// No new items were removed in this step. Try placing all the items on this screen.
GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY);
+ occupied.markCells(0, 0, mTrgX, startY, true);
for (DbEntry item : finalItems) {
occupied.markCells(item, true);
}
OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied,
- deepCopy(mCarryOver), true);
+ deepCopy(mCarryOver), startY, true);
placement.find();
if (placement.lowestWeightLoss == 0) {
// All items got placed
@@ -375,9 +380,10 @@
* @param outLoss array of size 2. The first entry is filled with weight loss, and the second
* with the overall item movement.
*/
- private ArrayList<DbEntry> tryRemove(int col, int row, ArrayList<DbEntry> items,
- float[] outLoss) {
+ private ArrayList<DbEntry> tryRemove(int col, int row, int startY,
+ ArrayList<DbEntry> items, float[] outLoss) {
GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY);
+ occupied.markCells(0, 0, mTrgX, startY, true);
col = mShouldRemoveX ? col : Integer.MAX_VALUE;
row = mShouldRemoveY ? row : Integer.MAX_VALUE;
@@ -399,7 +405,8 @@
}
}
- OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied, removedItems);
+ OptimalPlacementSolution placement =
+ new OptimalPlacementSolution(occupied, removedItems, startY);
placement.find();
finalItems.addAll(placement.finalPlacedItems);
outLoss[0] = placement.lowestWeightLoss;
@@ -415,19 +422,24 @@
// linear placement.
private final boolean ignoreMove;
+ // The first row in the grid from where the placement should start.
+ private final int startY;
+
float lowestWeightLoss = Float.MAX_VALUE;
float lowestMoveCost = Float.MAX_VALUE;
ArrayList<DbEntry> finalPlacedItems;
- public OptimalPlacementSolution(GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace) {
- this(occupied, itemsToPlace, false);
+ public OptimalPlacementSolution(
+ GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace, int startY) {
+ this(occupied, itemsToPlace, startY, false);
}
public OptimalPlacementSolution(GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace,
- boolean ignoreMove) {
+ int startY, boolean ignoreMove) {
this.occupied = occupied;
this.itemsToPlace = itemsToPlace;
this.ignoreMove = ignoreMove;
+ this.startY = startY;
// Sort the items such that larger widgets appear first followed by 1x1 items
Collections.sort(this.itemsToPlace);
@@ -477,7 +489,7 @@
int myW = me.spanX;
int myH = me.spanY;
- for (int y = 0; y < mTrgY; y++) {
+ for (int y = startY; y < mTrgY; y++) {
for (int x = 0; x < mTrgX; x++) {
float newMoveCost = moveCost;
if (x != myX) {
@@ -547,7 +559,7 @@
int newDistance = Integer.MAX_VALUE;
int newX = Integer.MAX_VALUE, newY = Integer.MAX_VALUE;
- for (int y = 0; y < mTrgY; y++) {
+ for (int y = startY; y < mTrgY; y++) {
for (int x = 0; x < mTrgX; x++) {
if (!occupied.cells[x][y]) {
int dist = ignoreMove ? 0 :
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index 7d84a0c..99af93b 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -24,7 +24,6 @@
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.graphics.Canvas;
-import android.view.animation.Interpolator;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Paint.Style;
@@ -33,6 +32,7 @@
import android.util.Property;
import android.view.View;
import android.view.ViewOutlineProvider;
+import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
import com.android.launcher3.R;
@@ -224,8 +224,14 @@
@Override
public void setActiveMarker(int activePage) {
- mActivePage = activePage;
- invalidate();
+ if (mActivePage != activePage) {
+ mActivePage = activePage;
+
+ // Simulate a scroll change
+ int totalScroll = mNumPages - 1;
+ int currentScroll = mIsRtl ? (totalScroll - mActivePage) : mActivePage;
+ setScroll(currentScroll, totalScroll);
+ }
}
@Override
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLine.java b/src/com/android/launcher3/pageindicators/PageIndicatorLine.java
index a358e7b..aec708c 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorLine.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorLine.java
@@ -14,7 +14,6 @@
import android.util.AttributeSet;
import android.util.Log;
import android.util.Property;
-import android.view.View;
import android.view.ViewConfiguration;
import com.android.launcher3.Utilities;
@@ -28,16 +27,29 @@
public class PageIndicatorLine extends PageIndicator {
private static final String TAG = "PageIndicatorLine";
- private static final int LINE_FADE_DURATION = ViewConfiguration.getScrollBarFadeDuration();
+ private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration();
private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
public static final int WHITE_ALPHA = (int) (0.70f * 255);
public static final int BLACK_ALPHA = (int) (0.65f * 255);
- private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private static final int LINE_ALPHA_ANIMATOR_INDEX = 0;
+ private static final int NUM_PAGES_ANIMATOR_INDEX = 1;
+ private static final int TOTAL_SCROLL_ANIMATOR_INDEX = 2;
- private ValueAnimator mLineAlphaAnimator;
- private int mAlpha = 0;
- private float mProgress = 0f;
+ private ValueAnimator[] mAnimators = new ValueAnimator[3];
+
+ private final Handler mDelayedLineFadeHandler = new Handler(Looper.getMainLooper());
+
+ private boolean mShouldAutoHide = true;
+
+ // The alpha of the line when it is showing.
+ private int mActiveAlpha = 0;
+ // The alpha that the line is being animated to or already at (either 0 or mActiveAlpha).
+ private int mToAlpha;
+ // A float value representing the number of pages, to allow for an animation when it changes.
+ private float mNumPagesFloat;
+ private int mCurrentScroll;
+ private int mTotalScroll;
private Paint mLinePaint;
private static final Property<PageIndicatorLine, Integer> PAINT_ALPHA
@@ -54,6 +66,34 @@
}
};
+ private static final Property<PageIndicatorLine, Float> NUM_PAGES
+ = new Property<PageIndicatorLine, Float>(Float.class, "num_pages") {
+ @Override
+ public Float get(PageIndicatorLine obj) {
+ return obj.mNumPagesFloat;
+ }
+
+ @Override
+ public void set(PageIndicatorLine obj, Float numPages) {
+ obj.mNumPagesFloat = numPages;
+ obj.invalidate();
+ }
+ };
+
+ private static final Property<PageIndicatorLine, Integer> TOTAL_SCROLL
+ = new Property<PageIndicatorLine, Integer>(Integer.class, "total_scroll") {
+ @Override
+ public Integer get(PageIndicatorLine obj) {
+ return obj.mTotalScroll;
+ }
+
+ @Override
+ public void set(PageIndicatorLine obj, Integer totalScroll) {
+ obj.mTotalScroll = totalScroll;
+ obj.invalidate();
+ }
+ };
+
private Runnable mHideLineRunnable = new Runnable() {
@Override
public void run() {
@@ -77,13 +117,15 @@
@Override
protected void onDraw(Canvas canvas) {
- if (mNumPages == 0) {
+ if (mTotalScroll == 0 || mNumPagesFloat == 0) {
return;
}
+ // Compute and draw line rect.
+ float progress = Utilities.boundToRange(((float) mCurrentScroll) / mTotalScroll, 0f, 1f);
int availableWidth = canvas.getWidth();
- int lineWidth = availableWidth / mNumPages;
- int lineLeft = (int) (mProgress * (availableWidth - lineWidth));
+ int lineWidth = (int) (availableWidth / mNumPagesFloat);
+ int lineLeft = (int) (progress * (availableWidth - lineWidth));
int lineRight = lineLeft + lineWidth;
canvas.drawRect(lineLeft, 0, lineRight, canvas.getHeight(), mLinePaint);
}
@@ -93,13 +135,25 @@
if (getAlpha() == 0) {
return;
}
- animateLineToAlpha(mAlpha);
- mProgress = Utilities.boundToRange(((float) currentScroll) / totalScroll, 0f, 1f);;
- invalidate();
+ animateLineToAlpha(mActiveAlpha);
- // Hide after a brief period.
- mHandler.removeCallbacksAndMessages(null);
- mHandler.postDelayed(mHideLineRunnable, LINE_FADE_DELAY);
+ mCurrentScroll = currentScroll;
+ if (mTotalScroll == 0) {
+ mTotalScroll = totalScroll;
+ } else if (mTotalScroll != totalScroll) {
+ animateToTotalScroll(totalScroll);
+ } else {
+ invalidate();
+ }
+
+ if (mShouldAutoHide) {
+ hideAfterDelay();
+ }
+ }
+
+ private void hideAfterDelay() {
+ mDelayedLineFadeHandler.removeCallbacksAndMessages(null);
+ mDelayedLineFadeHandler.postDelayed(mHideLineRunnable, LINE_FADE_DELAY);
}
@Override
@@ -108,7 +162,18 @@
@Override
protected void onPageCountChanged() {
- invalidate();
+ if (Float.compare(mNumPages, mNumPagesFloat) != 0) {
+ animateToNumPages(mNumPages);
+ }
+ }
+
+ public void setShouldAutoHide(boolean shouldAutoHide) {
+ mShouldAutoHide = shouldAutoHide;
+ if (shouldAutoHide && mLinePaint.getAlpha() > 0) {
+ hideAfterDelay();
+ } else if (!shouldAutoHide) {
+ mDelayedLineFadeHandler.removeCallbacksAndMessages(null);
+ }
}
/**
@@ -122,9 +187,9 @@
if (color != Color.TRANSPARENT) {
color = ColorUtils.setAlphaComponent(color, 255);
if (color == Color.BLACK) {
- mAlpha = BLACK_ALPHA;
+ mActiveAlpha = BLACK_ALPHA;
} else if (color == Color.WHITE) {
- mAlpha = WHITE_ALPHA;
+ mActiveAlpha = WHITE_ALPHA;
} else {
Log.e(TAG, "Setting workspace page indicators to an unsupported color: #"
+ Integer.toHexString(color));
@@ -135,22 +200,44 @@
}
private void animateLineToAlpha(int alpha) {
- if (mLineAlphaAnimator != null) {
- // An animation is already running, so ignore the new animation request unless we are
- // trying to hide the line, in which case we always allow the animation.
- if (alpha != 0) {
- return;
- }
- mLineAlphaAnimator.cancel();
+ if (alpha == mToAlpha) {
+ // Ignore the new animation if it is going to the same alpha as the current animation.
+ return;
}
- mLineAlphaAnimator = ObjectAnimator.ofInt(this, PAINT_ALPHA, alpha);
- mLineAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+ mToAlpha = alpha;
+ setupAndRunAnimation(ObjectAnimator.ofInt(this, PAINT_ALPHA, alpha),
+ LINE_ALPHA_ANIMATOR_INDEX);
+ }
+
+ private void animateToNumPages(int numPages) {
+ setupAndRunAnimation(ObjectAnimator.ofFloat(this, NUM_PAGES, numPages),
+ NUM_PAGES_ANIMATOR_INDEX);
+ }
+
+ private void animateToTotalScroll(int totalScroll) {
+ setupAndRunAnimation(ObjectAnimator.ofInt(this, TOTAL_SCROLL, totalScroll),
+ TOTAL_SCROLL_ANIMATOR_INDEX);
+ }
+
+ /**
+ * Starts the given animator and stores it in the provided index in {@link #mAnimators} until
+ * the animation ends.
+ *
+ * If an animator is already at the index (i.e. it is already playing), it is canceled and
+ * replaced with the new animator.
+ */
+ private void setupAndRunAnimation(ValueAnimator animator, final int animatorIndex) {
+ if (mAnimators[animatorIndex] != null) {
+ mAnimators[animatorIndex].cancel();
+ }
+ mAnimators[animatorIndex] = animator;
+ mAnimators[animatorIndex].addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- mLineAlphaAnimator = null;
+ mAnimators[animatorIndex] = null;
}
});
- mLineAlphaAnimator.setDuration(LINE_FADE_DURATION);
- mLineAlphaAnimator.start();
+ mAnimators[animatorIndex].setDuration(LINE_ANIMATE_DURATION);
+ mAnimators[animatorIndex].start();
}
}
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
new file mode 100644
index 0000000..9d8b6b3
--- /dev/null
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -0,0 +1,137 @@
+/*
+ * 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.provider;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherProvider.DatabaseHelper;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.logging.FileLog;
+
+import java.io.InvalidObjectException;
+
+/**
+ * Utility class to update DB schema after it has been restored.
+ *
+ * This task is executed when Launcher starts for the first time and not immediately after restore.
+ * This helps keep the model consistent if the launcher updates between restore and first startup.
+ */
+public class RestoreDbTask {
+
+ private static final String TAG = "RestoreDbTask";
+ private static final String RESTORE_TASK_PENDING = "restore_task_pending";
+
+ private static final String INFO_COLUMN_NAME = "name";
+ private static final String INFO_COLUMN_DEFAULT_VALUE = "dflt_value";
+
+ public static boolean performRestore(DatabaseHelper helper) {
+ SQLiteDatabase db = helper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ new RestoreDbTask().sanitizeDB(helper, db);
+ db.setTransactionSuccessful();
+ return true;
+ } catch (Exception e) {
+ FileLog.e(TAG, "Failed to verify db", e);
+ return false;
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ /**
+ * Makes the following changes in the provider DB.
+ * 1. Removes all entries belonging to a managed profile as managed profiles
+ * cannot be restored.
+ * 2. Marks all entries as restored. The flags are updated during first load or as
+ * the restored apps get installed.
+ * 3. If the user serial for primary profile is different than that of the previous device,
+ * update the entries to the new profile id.
+ */
+ private void sanitizeDB(DatabaseHelper helper, SQLiteDatabase db) throws Exception {
+ long oldProfileId = getDefaultProfileId(db);
+ // Delete all entries which do not belong to the main user
+ int itemsDeleted = db.delete(
+ Favorites.TABLE_NAME, "profileId != ?", new String[]{Long.toString(oldProfileId)});
+ if (itemsDeleted > 0) {
+ FileLog.d(TAG, itemsDeleted + " items belonging to a managed profile, were deleted");
+ }
+
+ // Mark all items as restored.
+ ContentValues values = new ContentValues();
+ values.put(Favorites.RESTORED, 1);
+ db.update(Favorites.TABLE_NAME, values, null, null);
+
+ // Mark widgets with appropriate restore flag
+ values.put(Favorites.RESTORED,
+ LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
+ LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
+ LauncherAppWidgetInfo.FLAG_UI_NOT_READY);
+ db.update(Favorites.TABLE_NAME, values, "itemType = ?",
+ new String[]{Integer.toString(Favorites.ITEM_TYPE_APPWIDGET)});
+
+ long myProfileId = helper.getDefaultUserSerial();
+ if (Utilities.longCompare(oldProfileId, myProfileId) != 0) {
+ FileLog.d(TAG, "Changing primary user id from " + oldProfileId + " to " + myProfileId);
+ migrateProfileId(db, myProfileId);
+ }
+ }
+
+ /**
+ * Updates profile id of all entries and changes the default value for the column.
+ */
+ protected void migrateProfileId(SQLiteDatabase db, long newProfileId) {
+ // Update existing entries.
+ ContentValues values = new ContentValues();
+ values.put(Favorites.PROFILE_ID, newProfileId);
+ db.update(Favorites.TABLE_NAME, values, null, null);
+
+ // Change default value of the column.
+ db.execSQL("ALTER TABLE favorites RENAME TO favorites_old;");
+ Favorites.addTableToDb(db, newProfileId, false);
+ db.execSQL("INSERT INTO favorites SELECT * FROM favorites_old;");
+ db.execSQL("DROP TABLE favorites_old;");
+ }
+
+ /**
+ * Returns the profile id used in the favorites table of the provided db.
+ */
+ protected long getDefaultProfileId(SQLiteDatabase db) throws Exception {
+ try (Cursor c = db.rawQuery("PRAGMA table_info (favorites)", null)){
+ int nameIndex = c.getColumnIndex(INFO_COLUMN_NAME);
+ while (c.moveToNext()) {
+ if (Favorites.PROFILE_ID.equals(c.getString(nameIndex))) {
+ return c.getLong(c.getColumnIndex(INFO_COLUMN_DEFAULT_VALUE));
+ }
+ }
+ throw new InvalidObjectException("Table does not have a profile id column");
+ }
+ }
+
+ public static boolean isPending(Context context) {
+ return Utilities.getPrefs(context).getBoolean(RESTORE_TASK_PENDING, false);
+ }
+
+ public static void setPending(Context context, boolean isPending) {
+ Utilities.getPrefs(context).edit().putBoolean(RESTORE_TASK_PENDING, isPending).commit();
+ }
+}
diff --git a/src/com/android/launcher3/util/GridOccupancy.java b/src/com/android/launcher3/util/GridOccupancy.java
index 3f5f0b4..6a10b0a 100644
--- a/src/com/android/launcher3/util/GridOccupancy.java
+++ b/src/com/android/launcher3/util/GridOccupancy.java
@@ -77,7 +77,7 @@
public void markCells(int cellX, int cellY, int spanX, int spanY, boolean value) {
if (cellX < 0 || cellY < 0) return;
for (int x = cellX; x < cellX + spanX && x < mCountX; x++) {
- for (int y = cellY; y < cellY + spanY && y < mCountX; y++) {
+ for (int y = cellY; y < cellY + spanY && y < mCountY; y++) {
cells[x][y] = value;
}
}
diff --git a/src/com/android/launcher3/util/TouchController.java b/src/com/android/launcher3/util/TouchController.java
new file mode 100644
index 0000000..d1409c8
--- /dev/null
+++ b/src/com/android/launcher3/util/TouchController.java
@@ -0,0 +1,8 @@
+package com.android.launcher3.util;
+
+import android.view.MotionEvent;
+
+public interface TouchController {
+ boolean onTouchEvent(MotionEvent ev);
+ boolean onInterceptTouchEvent(MotionEvent ev);
+}
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 9ec0340..3ad03ae 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -207,4 +207,9 @@
}
return "";
}
+
+ @Override
+ public CharSequence getAccessibilityClassName() {
+ return WidgetCell.class.getName();
+ }
}
diff --git a/src_config/com/android/launcher3/config/FeatureFlags.java b/src_config/com/android/launcher3/config/FeatureFlags.java
index 34c6663..2b9e6ce 100644
--- a/src_config/com/android/launcher3/config/FeatureFlags.java
+++ b/src_config/com/android/launcher3/config/FeatureFlags.java
@@ -29,4 +29,5 @@
public static boolean LAUNCHER3_LEGACY_FOLDER_ICON = false;
public static boolean LAUNCHER3_USE_SYSTEM_DRAG_DRIVER = false;
public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false;
+ public static boolean LAUNCHER3_ALL_APPS_PULL_UP = true;
}
diff --git a/tests/src/com/android/launcher3/BindWidgetTest.java b/tests/src/com/android/launcher3/BindWidgetTest.java
index 34b1174..5c5069f 100644
--- a/tests/src/com/android/launcher3/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/BindWidgetTest.java
@@ -1,38 +1,30 @@
package com.android.launcher3;
import android.annotation.TargetApi;
-import android.app.SearchManager;
import android.appwidget.AppWidgetHost;
-import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Build;
import android.os.Bundle;
-import android.os.ParcelFileDescriptor;
-import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiSelector;
-import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.ui.LauncherInstrumentationTestCase;
import com.android.launcher3.util.ManagedProfileHeuristic;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetHostViewLoader;
-import java.io.FileInputStream;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
/**
* Tests for bind widget flow.
@@ -41,12 +33,8 @@
*/
@LargeTest
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
-public class BindWidgetTest extends InstrumentationTestCase {
+public class BindWidgetTest extends LauncherInstrumentationTestCase {
- private static final long DEFAULT_TIMEOUT = 6000;
-
- private UiDevice mDevice;
- private Context mTargetContext;
private ContentResolver mResolver;
private AppWidgetManagerCompat mWidgetManager;
@@ -59,23 +47,9 @@
protected void setUp() throws Exception {
super.setUp();
- mDevice = UiDevice.getInstance(getInstrumentation());
- mTargetContext = getInstrumentation().getTargetContext();
mResolver = mTargetContext.getContentResolver();
mWidgetManager = AppWidgetManagerCompat.getInstance(mTargetContext);
-
- // Check bind widget permission
- String pkg = mTargetContext.getPackageName();
- if (mTargetContext.getPackageManager().checkPermission(
- pkg, android.Manifest.permission.BIND_APPWIDGET)
- != PackageManager.PERMISSION_GRANTED) {
- ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation().executeShellCommand(
- "appwidget grantbind --package " + pkg);
- // Read the input stream fully.
- FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
- while (fis.read() != -1);
- fis.close();
- }
+ grantWidgetPermission();
// Clear all existing data
LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
@@ -231,10 +205,13 @@
*/
private void setupAndVerifyContents(
LauncherAppWidgetInfo item, Class<?> widgetClass, String desc) {
- // Add new screen
- long screenId = LauncherSettings.Settings.call(
- mResolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
- .getLong(LauncherSettings.Settings.EXTRA_VALUE);
+ long screenId = Workspace.FIRST_SCREEN_ID;
+ // Update the screen id counter for the provider.
+ LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
+
+ if (screenId > Workspace.FIRST_SCREEN_ID) {
+ screenId = Workspace.FIRST_SCREEN_ID;
+ }
ContentValues v = new ContentValues();
v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, 0);
@@ -264,61 +241,14 @@
throw new IllegalArgumentException(t);
}
// Launch the home activity
- getInstrumentation().getContext().startActivity(new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setPackage(mTargetContext.getPackageName())
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
-
+ startLauncher();
// Verify UI
UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
.className(widgetClass);
if (desc != null) {
selector = selector.description(desc);
}
- assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_TIMEOUT));
- }
-
- /**
- * Finds a widget provider which can fit on the home screen.
- * @param hasConfigureScreen if true, a provider with a config screen is returned.
- */
- private LauncherAppWidgetProviderInfo findWidgetProvider(final boolean hasConfigureScreen) {
- LauncherAppWidgetProviderInfo info = getOnUiThread(new Callable<LauncherAppWidgetProviderInfo>() {
- @Override
- public LauncherAppWidgetProviderInfo call() throws Exception {
- InvariantDeviceProfile idv =
- LauncherAppState.getInstance().getInvariantDeviceProfile();
-
- ComponentName searchComponent = ((SearchManager) mTargetContext
- .getSystemService(Context.SEARCH_SERVICE)).getGlobalSearchActivity();
- String searchPackage = searchComponent == null
- ? null : searchComponent.getPackageName();
-
- for (AppWidgetProviderInfo info :
- AppWidgetManagerCompat.getInstance(mTargetContext).getAllProviders()) {
- if ((info.configure != null) ^ hasConfigureScreen) {
- continue;
- }
- // Exclude the widgets in search package, as Launcher already binds them in
- // QSB, so they can cause conflicts.
- if (info.provider.getPackageName().equals(searchPackage)) {
- continue;
- }
- LauncherAppWidgetProviderInfo widgetInfo = LauncherAppWidgetProviderInfo
- .fromProviderInfo(mTargetContext, info);
- if (widgetInfo.minSpanX >= idv.numColumns
- || widgetInfo.minSpanY >= idv.numRows) {
- continue;
- }
- return widgetInfo;
- }
- return null;
- }
- });
- if (info == null) {
- throw new IllegalArgumentException("No valid widget provider");
- }
- return info;
+ assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT));
}
/**
@@ -336,7 +266,7 @@
item.minSpanY = info.minSpanY;
item.user = mWidgetManager.getUser(info);
item.cellX = 0;
- item.cellY = 0;
+ item.cellY = 1;
item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
if (bindWidget) {
@@ -392,30 +322,12 @@
item.minSpanX = 2;
item.minSpanY = 2;
item.cellX = 0;
- item.cellY = 0;
+ item.cellY = 1;
item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
return item;
}
/**
- * Runs the callback on the UI thread and returns the result.
- */
- private <T> T getOnUiThread(final Callable<T> callback) {
- final AtomicReference<T> result = new AtomicReference<>(null);
- try {
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- result.set(callback.call());
- } catch (Exception e) { }
- }
- });
- } catch (Throwable t) { }
- return result.get();
- }
-
- /**
* Blocks the current thread until all the jobs in the main worker thread are complete.
*/
private void waitUntilLoaderIdle() throws InterruptedException {
diff --git a/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java b/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
index 476eb0a..35f686f 100644
--- a/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
+++ b/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
@@ -116,75 +116,4 @@
// Add more tests for other devices, however, running them once on a single device is enough
// for verifying that for a platform version, the WindowManager and DisplayMetrics is
// working as intended.
-
- /**
- * Make sure that the height for the QSB is what we expect in normal mode.
- */
- public void testQsbNormalHeight() {
- Resources resources = getContext().getResources();
- DeviceProfile landscapeProfile = mInvariantProfile.landscapeProfile;
- DeviceProfile portraitProfile = mInvariantProfile.portraitProfile;
- landscapeProfile.setSearchBarHeight(LauncherCallbacks.SEARCH_BAR_HEIGHT_NORMAL);
- portraitProfile.setSearchBarHeight(LauncherCallbacks.SEARCH_BAR_HEIGHT_NORMAL);
- Rect portraitBounds = portraitProfile.getSearchBarBounds(true); // RTL shouldn't matter.
- int portraitHeight = (int) Utilities.dpiFromPx(portraitBounds.height(),
- resources.getDisplayMetrics());
- Rect landscapeBounds = landscapeProfile.getSearchBarBounds(true); // RTL shouldn't matter.
- int landscapeHeight = (int) Utilities.dpiFromPx(landscapeBounds.height(),
- resources.getDisplayMetrics());
- if (portraitProfile.isTablet) {
- assertEquals(8 + 48 + 24, portraitHeight);
- } else {
- assertEquals(8 + 48 + 12, portraitHeight);
- }
- // Make sure the height that we pass in the widget options bundle is the height of the
- // search bar + 8dps padding top and bottom.
- Point portraitDimens = portraitProfile.getSearchBarDimensForWidgetOpts(resources);
- int portraitWidgetOptsHeight = portraitDimens.y;
- Point landscapeDimens = landscapeProfile.getSearchBarDimensForWidgetOpts(resources);
- int landscapeWidgetOptsHeight = landscapeDimens.y;
- assertEquals(8 + 48 + 8, (int) Utilities.dpiFromPx(portraitWidgetOptsHeight,
- resources.getDisplayMetrics()));
- if (!landscapeProfile.isVerticalBarLayout()) {
- assertEquals(portraitHeight, landscapeHeight);
- assertEquals(portraitWidgetOptsHeight, landscapeWidgetOptsHeight);
- }
- }
-
- /**
- * Make sure that the height for the QSB is what we expect in tall mode.
- */
- public void testQsbTallHeight() {
- Resources resources = getContext().getResources();
- DeviceProfile landscapeProfile = mInvariantProfile.landscapeProfile;
- DeviceProfile portraitProfile = mInvariantProfile.portraitProfile;
- landscapeProfile.setSearchBarHeight(LauncherCallbacks.SEARCH_BAR_HEIGHT_TALL);
- portraitProfile.setSearchBarHeight(LauncherCallbacks.SEARCH_BAR_HEIGHT_TALL);
- Rect portraitBounds = portraitProfile.getSearchBarBounds(true); // RTL shouldn't matter.
- int portraitHeight = (int) Utilities.dpiFromPx(portraitBounds.height(),
- resources.getDisplayMetrics());
- Rect landscapeBounds = landscapeProfile.getSearchBarBounds(true); // RTL shouldn't matter.
- int landscapeHeight = (int) Utilities.dpiFromPx(landscapeBounds.height(),
- resources.getDisplayMetrics());
- if (portraitProfile.isPhone) {
- // This fails on some devices due to http://b/26884580 (portraitHeight is 101, not 100).
- // TODO: Remove the comparision against 101 once b/26884580 is fixed
- // assertEquals(4 + 94 + 2, portraitHeight);
- assertTrue(portraitHeight == (4 + 94 + 2) || portraitHeight == (4 + 95 + 2));
- } else {
- assertEquals(8 + 94 + 24, portraitHeight);
- }
- // Make sure the height that we pass in the widget options bundle is the height of the
- // search bar + 8dps padding top and bottom.
- Point portraitDimens = portraitProfile.getSearchBarDimensForWidgetOpts(resources);
- int portraitWidgetOptsHeight = portraitDimens.y;
- Point landscapeDimens = landscapeProfile.getSearchBarDimensForWidgetOpts(resources);
- int landscapeWidgetOptsHeight = landscapeDimens.y;
- assertEquals(8 + 94 + 8, (int) Utilities.dpiFromPx(portraitWidgetOptsHeight,
- resources.getDisplayMetrics()));
- if (!landscapeProfile.isVerticalBarLayout()) {
- assertEquals(portraitHeight, landscapeHeight);
- assertEquals(portraitWidgetOptsHeight, landscapeWidgetOptsHeight);
- }
- }
}
diff --git a/tests/src/com/android/launcher3/QuickAddWidgetTest.java b/tests/src/com/android/launcher3/QuickAddWidgetTest.java
deleted file mode 100644
index 0078294..0000000
--- a/tests/src/com/android/launcher3/QuickAddWidgetTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package com.android.launcher3;
-
-import android.content.Intent;
-import android.graphics.Point;
-import android.os.SystemClock;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.Direction;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-
-import java.util.List;
-
-/**
- * Add an arbitrary widget from the widget picker very quickly to test potential race conditions.
- */
-@LargeTest
-public class QuickAddWidgetTest extends InstrumentationTestCase {
- // Disabled because it's flaky and not particularly useful. But this class could still be useful
- // as an example if we want other UI tests in the future.
- private static final boolean DISABLED = true;
-
- private UiDevice mDevice;
- private String mTargetPackage;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mDevice = UiDevice.getInstance(getInstrumentation());
-
- // Set Launcher3 as home.
- mTargetPackage = getInstrumentation().getTargetContext().getPackageName();
- Intent homeIntent = new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setPackage(mTargetPackage)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getInstrumentation().getContext().startActivity(homeIntent);
- mDevice.wait(Until.hasObject(By.pkg(mTargetPackage).depth(0)), 3000);
- }
-
- public void testAddWidgetQuickly() throws Exception {
- if (DISABLED) return;
- mDevice.pressMenu(); // Enter overview mode.
- mDevice.wait(Until.findObject(By.text("Widgets")), 3000).click();
- UiObject2 calendarWidget = getWidgetByName("Clock");
- Point center = calendarWidget.getVisibleCenter();
- // Touch widget just long enough to pick it up (longPressTimeout), then let go immediately.
- getInstrumentation().sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(),
- SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, center.x, center.y, 0));
- Thread.sleep(ViewConfiguration.getLongPressTimeout() + 50);
- getInstrumentation().sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(),
- SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, center.x, center.y, 0));
-
- assertTrue("Drag was never started", isOnHomescreen());
- }
-
- private UiObject2 getWidgetByName(String name) {
- UiObject2 widgetsList = mDevice.wait(Until.findObject(By.res(mTargetPackage,
- "widgets_list_view")), 3000);
- do {
- UiObject2 widget = getVisibleWidgetByName(name);
- if (widget != null) {
- return widget;
- }
- } while (widgetsList.scroll(Direction.DOWN, 1f));
- return getVisibleWidgetByName(name);
- }
-
- private UiObject2 getVisibleWidgetByName(String name) {
- List<UiObject2> visibleWidgets = mDevice.wait(Until.findObjects(By.clazz(
- "android.widget.LinearLayout")), 3000);
- for (UiObject2 widget : visibleWidgets) {
- if (widget.hasObject(By.text(name))) {
- return widget;
- }
- }
- return null;
- }
-
- private boolean isOnHomescreen() {
- return mDevice.wait(Until.hasObject(By.res(mTargetPackage, "hotseat")), 3000);
- }
-}
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
index ccd8f73..ebf06ba 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -208,6 +208,57 @@
}});
}
+ public void testWorkspace_first_row_blocked() throws Exception {
+ // The first screen has one item on the 4th column which needs moving, as the first row
+ // will be kept empty.
+ long[][][] ids = createGrid(new int[][][]{{
+ { -1, -1, -1, -1},
+ { 3, 1, 7, 0},
+ { 8, 7, 7, -1},
+ { 5, 2, 7, -1},
+ }}, 0);
+
+ new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
+ new Point(4, 4), new Point(3, 4)).migrateWorkspace();
+
+ // Items in the second column of the first screen should get placed on a new screen.
+ verifyWorkspace(new long[][][] {{
+ { -1, -1, -1},
+ {ids[0][1][0], ids[0][1][1], ids[0][1][2]},
+ {ids[0][2][0], ids[0][2][1], ids[0][2][2]},
+ {ids[0][3][0], ids[0][3][1], ids[0][3][2]},
+ }, {
+ {ids[0][1][3]},
+ }});
+ }
+
+ public void testWorkspace_items_moved_to_empty_first_row() throws Exception {
+ // Items will get moved to the next screen to keep the first screen empty.
+ long[][][] ids = createGrid(new int[][][]{{
+ { -1, -1, -1, -1},
+ { 0, 1, 0, 0},
+ { 8, 7, 7, -1},
+ { 5, 6, 7, -1},
+ }}, 0);
+
+ new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
+ new Point(4, 4), new Point(3, 3)).migrateWorkspace();
+
+ // Items in the second column of the first screen should get placed on a new screen.
+ verifyWorkspace(new long[][][] {{
+ { -1, -1, -1},
+ {ids[0][2][0], ids[0][2][1], ids[0][2][2]},
+ {ids[0][3][0], ids[0][3][1], ids[0][3][2]},
+ }, {
+ {ids[0][1][1], ids[0][1][0], ids[0][1][2]},
+ {ids[0][1][3]},
+ }});
+ }
+
+ private long[][][] createGrid(int[][][] typeArray) throws Exception {
+ return createGrid(typeArray, 1);
+ }
+
/**
* Initializes the DB with dummy elements to represent the provided grid structure.
* @param typeArray A 3d array of item types. {@see #addItem(int, long, long, int, int)} for
@@ -215,14 +266,18 @@
* two represent the workspace grid.
* @return the same grid representation where each entry is the corresponding item id.
*/
- private long[][][] createGrid(int[][][] typeArray) throws Exception {
+ private long[][][] createGrid(int[][][] typeArray, long startScreen) throws Exception {
+ LauncherSettings.Settings.call(getMockContentResolver(),
+ LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
long[][][] ids = new long[typeArray.length][][];
for (int i = 0; i < typeArray.length; i++) {
// Add screen to DB
- long screenId = LauncherSettings.Settings.call(getMockContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
- .getLong(LauncherSettings.Settings.EXTRA_VALUE);
+ long screenId = startScreen + i;
+
+ // Keep the screen id counter up to date
+ LauncherSettings.Settings.call(getMockContentResolver(),
+ LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
ContentValues v = new ContentValues();
v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
@@ -270,7 +325,8 @@
} else {
assertEquals(1, c.getCount());
c.moveToNext();
- assertEquals(id, c.getLong(0));
+ assertEquals(String.format("Failed to verify item ad %d %d, %d", i, y, x),
+ id, c.getLong(0));
total++;
}
c.close();
diff --git a/tests/src/com/android/launcher3/LauncherBackupAgentTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
similarity index 88%
rename from tests/src/com/android/launcher3/LauncherBackupAgentTest.java
rename to tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index 020a557..29f738b 100644
--- a/tests/src/com/android/launcher3/LauncherBackupAgentTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -1,4 +1,4 @@
-package com.android.launcher3;
+package com.android.launcher3.provider;
import android.content.ContentValues;
import android.database.Cursor;
@@ -10,14 +10,14 @@
import com.android.launcher3.LauncherSettings.Favorites;
/**
- * Tests for {@link LauncherBackupAgent}
+ * Tests for {@link RestoreDbTask}
*/
@MediumTest
-public class LauncherBackupAgentTest extends AndroidTestCase {
+public class RestoreDbTaskTest extends AndroidTestCase {
public void testGetProfileId() throws Exception {
SQLiteDatabase db = new MyDatabaseHelper(23).getWritableDatabase();
- assertEquals(23, new LauncherBackupAgent().getDefaultProfileId(db));
+ assertEquals(23, new RestoreDbTask().getDefaultProfileId(db));
}
public void testMigrateProfileId() throws Exception {
@@ -32,7 +32,7 @@
// Verify item add
assertEquals(5, getCount(db, "select * from favorites where profileId = 42"));
- new LauncherBackupAgent().migrateProfileId(db, 33);
+ new RestoreDbTask().migrateProfileId(db, 33);
// verify data migrated
assertEquals(0, getCount(db, "select * from favorites where profileId = 42"));
diff --git a/tests/src/com/android/launcher3/ui/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/AddWidgetTest.java
new file mode 100644
index 0000000..a0ca60c
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/AddWidgetTest.java
@@ -0,0 +1,64 @@
+package com.android.launcher3.ui;
+
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiObject2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.view.View;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.Workspace.ItemOperator;
+import com.android.launcher3.util.Condition;
+import com.android.launcher3.util.Wait;
+import com.android.launcher3.widget.WidgetCell;
+
+/**
+ * Test to add widget from widget tray
+ */
+@LargeTest
+public class AddWidgetTest extends LauncherInstrumentationTestCase {
+
+ private LauncherAppWidgetProviderInfo widgetInfo;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ widgetInfo = findWidgetProvider(false /* hasConfigureScreen */);
+ }
+
+ public void testDragIcon_portrait() throws Throwable {
+ lockRotation(true);
+ performTest();
+ }
+
+ public void testDragIcon_landscape() throws Throwable {
+ lockRotation(false);
+ performTest();
+ }
+
+ private void performTest() throws Throwable {
+ clearHomescreen();
+ Launcher launcher = startLauncher();
+
+ // Open widget tray and wait for load complete.
+ final UiObject2 widgetContainer = openWidgetsTray();
+ assertTrue(Wait.atMost(Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT));
+
+ // Drag widget to homescreen
+ UiObject2 widget = scrollAndFind(widgetContainer, By.clazz(WidgetCell.class)
+ .hasDescendant(By.text(widgetInfo.getLabel(mTargetContext.getPackageManager()))));
+ dragToWorkspace(widget);
+
+ assertNotNull(launcher.getWorkspace().getFirstMatch(new ItemOperator() {
+ @Override
+ public boolean evaluate(ItemInfo info, View view) {
+ return info instanceof LauncherAppWidgetInfo &&
+ ((LauncherAppWidgetInfo) info).providerName.equals(widgetInfo.provider);
+ }
+ }));
+ }
+}
diff --git a/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java b/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java
new file mode 100644
index 0000000..abe6b95
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java
@@ -0,0 +1,52 @@
+package com.android.launcher3.ui;
+
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.util.Condition;
+import com.android.launcher3.util.Wait;
+
+/**
+ * Test for verifying apps is launched from all-apps
+ */
+@LargeTest
+public class AllAppsAppLaunchTest extends LauncherInstrumentationTestCase {
+
+ private LauncherActivityInfoCompat mSettingsApp;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext)
+ .getActivityList("com.android.settings", UserHandleCompat.myUserHandle()).get(0);
+ }
+
+ public void testAppLauncher_portrait() throws Exception {
+ lockRotation(true);
+ performTest();
+ }
+
+ public void testAppLauncher_landscape() throws Exception {
+ lockRotation(false);
+ performTest();
+ }
+
+ private void performTest() throws Exception {
+ startLauncher();
+
+ // Open all apps and wait for load complete
+ final UiObject2 appsContainer = openAllApps();
+ assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
+
+ // Open settings app and verify app launched
+ scrollAndFind(appsContainer, By.text(mSettingsApp.getLabel().toString())).click();
+ assertTrue(mDevice.wait(Until.hasObject(By.pkg(
+ mSettingsApp.getComponentName().getPackageName()).depth(0)), DEFAULT_UI_TIMEOUT));
+ }
+}
diff --git a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
new file mode 100644
index 0000000..56fc90a
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
@@ -0,0 +1,57 @@
+package com.android.launcher3.ui;
+
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.util.Condition;
+import com.android.launcher3.util.Wait;
+
+/**
+ * Test for dragging an icon from all-apps to homescreen.
+ */
+@LargeTest
+public class AllAppsIconToHomeTest extends LauncherInstrumentationTestCase {
+
+ private LauncherActivityInfoCompat mSettingsApp;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext)
+ .getActivityList("com.android.settings", UserHandleCompat.myUserHandle()).get(0);
+ }
+
+ public void testDragIcon_portrait() throws Throwable {
+ lockRotation(true);
+ performTest();
+ }
+
+ public void testDragIcon_landscape() throws Throwable {
+ lockRotation(false);
+ performTest();
+ }
+
+ private void performTest() throws Throwable {
+ clearHomescreen();
+ startLauncher();
+
+ // Open all apps and wait for load complete.
+ final UiObject2 appsContainer = openAllApps();
+ assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
+
+ // Drag icon to homescreen.
+ UiObject2 icon = scrollAndFind(appsContainer, By.text(mSettingsApp.getLabel().toString()));
+ dragToWorkspace(icon);
+
+ // Verify that the icon works on homescreen.
+ mDevice.findObject(By.text(mSettingsApp.getLabel().toString())).click();
+ assertTrue(mDevice.wait(Until.hasObject(By.pkg(
+ mSettingsApp.getComponentName().getPackageName()).depth(0)), DEFAULT_UI_TIMEOUT));
+ }
+}
diff --git a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
new file mode 100644
index 0000000..a59f0ff
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
@@ -0,0 +1,266 @@
+package com.android.launcher3.ui;
+
+import android.app.SearchManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Point;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.Direction;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.test.InstrumentationTestCase;
+import android.view.MotionEvent;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherClings;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.util.ManagedProfileHeuristic;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Locale;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Base class for all instrumentation tests providing various utility methods.
+ */
+public class LauncherInstrumentationTestCase extends InstrumentationTestCase {
+
+ public static final long DEFAULT_UI_TIMEOUT = 3000;
+
+ protected UiDevice mDevice;
+ protected Context mTargetContext;
+ protected String mTargetPackage;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mDevice = UiDevice.getInstance(getInstrumentation());
+ mTargetContext = getInstrumentation().getTargetContext();
+ mTargetPackage = mTargetContext.getPackageName();
+ }
+
+ protected void lockRotation(boolean naturalOrientation) throws RemoteException {
+ Utilities.getPrefs(mTargetContext)
+ .edit()
+ .putBoolean(Utilities.ALLOW_ROTATION_PREFERENCE_KEY, !naturalOrientation)
+ .commit();
+
+ if (naturalOrientation) {
+ mDevice.setOrientationNatural();
+ } else {
+ mDevice.setOrientationRight();
+ }
+ }
+
+ /**
+ * Starts the launcher activity in the target package and returns the Launcher instance.
+ */
+ protected Launcher startLauncher() {
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setPackage(mTargetPackage)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return (Launcher) getInstrumentation().startActivitySync(homeIntent);
+ }
+
+ /**
+ * Grants the launcher permission to bind widgets.
+ */
+ protected void grantWidgetPermission() throws IOException {
+ // Check bind widget permission
+ if (mTargetContext.getPackageManager().checkPermission(
+ mTargetPackage, android.Manifest.permission.BIND_APPWIDGET)
+ != PackageManager.PERMISSION_GRANTED) {
+ ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation().executeShellCommand(
+ "appwidget grantbind --package " + mTargetPackage);
+ // Read the input stream fully.
+ FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ while (fis.read() != -1);
+ fis.close();
+ }
+ }
+
+ /**
+ * Opens all apps and returns the recycler view
+ */
+ protected UiObject2 openAllApps() {
+ mDevice.wait(Until.findObject(
+ By.desc(mTargetContext.getString(R.string.all_apps_button_label))), DEFAULT_UI_TIMEOUT).click();
+ return findViewById(R.id.apps_list_view);
+ }
+
+ /**
+ * Opens widget tray and returns the recycler view.
+ */
+ protected UiObject2 openWidgetsTray() {
+ mDevice.pressMenu(); // Enter overview mode.
+ mDevice.wait(Until.findObject(
+ By.text(mTargetContext.getString(R.string.widget_button_text)
+ .toUpperCase(Locale.getDefault()))), DEFAULT_UI_TIMEOUT).click();
+ return findViewById(R.id.widgets_list_view);
+ }
+
+ /**
+ * Scrolls the {@param container} until it finds an object matching {@param condition}.
+ * @return the matching object.
+ */
+ protected UiObject2 scrollAndFind(UiObject2 container, BySelector condition) {
+ do {
+ UiObject2 widget = container.findObject(condition);
+ if (widget != null) {
+ return widget;
+ }
+ } while (container.scroll(Direction.DOWN, 1f));
+ return container.findObject(condition);
+ }
+
+ /**
+ * Drags an icon to the center of homescreen.
+ */
+ protected void dragToWorkspace(UiObject2 icon) {
+ Point center = icon.getVisibleCenter();
+
+ // Action Down
+ sendPointer(MotionEvent.ACTION_DOWN, center);
+
+ // Wait until "Remove/Delete target is visible
+ assertNotNull(findViewById(R.id.delete_target_text));
+
+ Point moveLocation = findViewById(R.id.drag_layer).getVisibleCenter();
+
+ // Move to center
+ while(!moveLocation.equals(center)) {
+ center.x = getNextMoveValue(moveLocation.x, center.x);
+ center.y = getNextMoveValue(moveLocation.y, center.y);
+ sendPointer(MotionEvent.ACTION_MOVE, center);
+ }
+ sendPointer(MotionEvent.ACTION_UP, center);
+
+ // Wait until remove target is gone.
+ mDevice.wait(Until.gone(getSelectorForId(R.id.delete_target_text)), DEFAULT_UI_TIMEOUT);
+ }
+
+ private int getNextMoveValue(int targetValue, int oldValue) {
+ if (targetValue - oldValue > 10) {
+ return oldValue + 10;
+ } else if (targetValue - oldValue < -10) {
+ return oldValue - 10;
+ } else {
+ return targetValue;
+ }
+ }
+
+ private void sendPointer(int action, Point point) {
+ MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(),
+ SystemClock.uptimeMillis(), action, point.x, point.y, 0);
+ getInstrumentation().sendPointerSync(event);
+ event.recycle();
+ }
+
+ /**
+ * Removes all icons from homescreen and hotseat.
+ */
+ public void clearHomescreen() throws Throwable {
+ LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
+ LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+ LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
+ LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
+ LauncherClings.markFirstRunClingDismissed(mTargetContext);
+ ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mTargetContext);
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ // Reset the loader state
+ LauncherAppState.getInstance().getModel().resetLoadedState(true, true);
+ }
+ });
+ }
+
+ /**
+ * Runs the callback on the UI thread and returns the result.
+ */
+ protected <T> T getOnUiThread(final Callable<T> callback) {
+ final AtomicReference<T> result = new AtomicReference<>(null);
+ try {
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ result.set(callback.call());
+ } catch (Exception e) { }
+ }
+ });
+ } catch (Throwable t) { }
+ return result.get();
+ }
+
+ /**
+ * Finds a widget provider which can fit on the home screen.
+ * @param hasConfigureScreen if true, a provider with a config screen is returned.
+ */
+ protected LauncherAppWidgetProviderInfo findWidgetProvider(final boolean hasConfigureScreen) {
+ LauncherAppWidgetProviderInfo info = getOnUiThread(new Callable<LauncherAppWidgetProviderInfo>() {
+ @Override
+ public LauncherAppWidgetProviderInfo call() throws Exception {
+ InvariantDeviceProfile idv =
+ LauncherAppState.getInstance().getInvariantDeviceProfile();
+
+ ComponentName searchComponent = ((SearchManager) mTargetContext
+ .getSystemService(Context.SEARCH_SERVICE)).getGlobalSearchActivity();
+ String searchPackage = searchComponent == null
+ ? null : searchComponent.getPackageName();
+
+ for (AppWidgetProviderInfo info :
+ AppWidgetManagerCompat.getInstance(mTargetContext).getAllProviders()) {
+ if ((info.configure != null) ^ hasConfigureScreen) {
+ continue;
+ }
+ // Exclude the widgets in search package, as Launcher already binds them in
+ // QSB, so they can cause conflicts.
+ if (info.provider.getPackageName().equals(searchPackage)) {
+ continue;
+ }
+ LauncherAppWidgetProviderInfo widgetInfo = LauncherAppWidgetProviderInfo
+ .fromProviderInfo(mTargetContext, info);
+ if (widgetInfo.minSpanX >= idv.numColumns
+ || widgetInfo.minSpanY >= idv.numRows) {
+ continue;
+ }
+ return widgetInfo;
+ }
+ return null;
+ }
+ });
+ if (info == null) {
+ throw new IllegalArgumentException("No valid widget provider");
+ }
+ return info;
+ }
+
+ protected UiObject2 findViewById(int id) {
+ return mDevice.wait(Until.findObject(getSelectorForId(id)), DEFAULT_UI_TIMEOUT);
+ }
+
+ protected BySelector getSelectorForId(int id) {
+ String name = mTargetContext.getResources().getResourceEntryName(id);
+ return By.res(mTargetPackage, name);
+ }
+}
diff --git a/tests/src/com/android/launcher3/RotationPreferenceTest.java b/tests/src/com/android/launcher3/ui/RotationPreferenceTest.java
similarity index 74%
rename from tests/src/com/android/launcher3/RotationPreferenceTest.java
rename to tests/src/com/android/launcher3/ui/RotationPreferenceTest.java
index 7259d35..e84ad04 100644
--- a/tests/src/com/android/launcher3/RotationPreferenceTest.java
+++ b/tests/src/com/android/launcher3/ui/RotationPreferenceTest.java
@@ -1,24 +1,20 @@
-package com.android.launcher3;
+package com.android.launcher3.ui;
-import android.content.Context;
-import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Rect;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiSelector;
-import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.MediumTest;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+
/**
* Test for auto rotate preference.
*/
@MediumTest
-public class RotationPreferenceTest extends InstrumentationTestCase {
-
- private UiDevice mDevice;
- private Context mTargetContext;
- private String mTargetPackage;
+public class RotationPreferenceTest extends LauncherInstrumentationTestCase {
private SharedPreferences mPrefs;
private boolean mOriginalRotationValue;
@@ -48,7 +44,7 @@
setRotationEnabled(false);
mDevice.setOrientationRight();
- goToLauncher();
+ startLauncher();
Rect hotseat = getHotseatBounds();
assertTrue(hotseat.width() > hotseat.height());
@@ -62,21 +58,12 @@
setRotationEnabled(true);
mDevice.setOrientationRight();
- goToLauncher();
+ startLauncher();
Rect hotseat = getHotseatBounds();
assertTrue(hotseat.width() < hotseat.height());
}
- private void goToLauncher() {
- Intent homeIntent = new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setPackage(mTargetPackage)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getInstrumentation().getContext().startActivity(homeIntent);
- mDevice.findObject(new UiSelector().packageName(mTargetPackage)).waitForExists(6000);
- }
-
private void setRotationEnabled(boolean enabled) {
mPrefs.edit().putBoolean(Utilities.ALLOW_ROTATION_PREFERENCE_KEY, enabled).commit();
}
diff --git a/tests/src/com/android/launcher3/util/Condition.java b/tests/src/com/android/launcher3/util/Condition.java
new file mode 100644
index 0000000..e9ee67c
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/Condition.java
@@ -0,0 +1,54 @@
+package com.android.launcher3.util;
+
+import android.support.test.uiautomator.UiObject2;
+
+import com.android.launcher3.MainThreadExecutor;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public abstract class Condition {
+
+ public abstract boolean isTrue() throws Throwable;
+
+ /**
+ * Converts the condition to be run on UI thread.
+ */
+ public static Condition runOnUiThread(final Condition condition) {
+ final MainThreadExecutor executor = new MainThreadExecutor();
+ return new Condition() {
+ @Override
+ public boolean isTrue() throws Throwable {
+ final AtomicBoolean value = new AtomicBoolean(false);
+ final Throwable[] exceptions = new Throwable[1];
+ final CountDownLatch latch = new CountDownLatch(1);
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ value.set(condition.isTrue());
+ } catch (Throwable e) {
+ exceptions[0] = e;
+ }
+
+ }
+ });
+ latch.await(1, TimeUnit.SECONDS);
+ if (exceptions[0] != null) {
+ throw exceptions[0];
+ }
+ return value.get();
+ }
+ };
+ }
+
+ public static Condition minChildCount(final UiObject2 obj, final int childCount) {
+ return new Condition() {
+ @Override
+ public boolean isTrue() {
+ return obj.getChildCount() >= childCount;
+ }
+ };
+ }
+}
diff --git a/tests/src/com/android/launcher3/util/TestLauncherProvider.java b/tests/src/com/android/launcher3/util/TestLauncherProvider.java
index bd3e86c..6ca2121 100644
--- a/tests/src/com/android/launcher3/util/TestLauncherProvider.java
+++ b/tests/src/com/android/launcher3/util/TestLauncherProvider.java
@@ -37,7 +37,7 @@
}
@Override
- protected long getDefaultUserSerial() {
+ public long getDefaultUserSerial() {
return 0;
}
diff --git a/tests/src/com/android/launcher3/util/Wait.java b/tests/src/com/android/launcher3/util/Wait.java
new file mode 100644
index 0000000..02a1913
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/Wait.java
@@ -0,0 +1,30 @@
+package com.android.launcher3.util;
+
+import android.os.SystemClock;
+
+/**
+ * A utility class for waiting for a condition to be true.
+ */
+public class Wait {
+
+ private static final long DEFAULT_SLEEP_MS = 200;
+
+ public static boolean atMost(Condition condition, long timeout) {
+ return atMost(condition, timeout, DEFAULT_SLEEP_MS);
+ }
+
+ public static boolean atMost(Condition condition, long timeout, long sleepMillis) {
+ long endTime = SystemClock.uptimeMillis() + timeout;
+ while (SystemClock.uptimeMillis() < endTime) {
+ try {
+ if (condition.isTrue()) {
+ return true;
+ }
+ } catch (Throwable t) {
+ // Ignore
+ }
+ SystemClock.sleep(sleepMillis);
+ }
+ return false;
+ }
+}