Merge "Fixing header jump" into sc-v2-dev am: 48b012b148
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/15550092 Change-Id: Ie59fd030a058a75346869e854ee3549fb1977580
This commit is contained in:
+6
-9
@@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!-- Copyright (C) 2021 The Android Open Source Project
|
||||||
Copyright (C) 2021 The Android Open Source Project
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -14,13 +13,11 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="rectangle">
|
android:shape="rectangle" >
|
||||||
<solid android:color="@color/surface" />
|
<solid android:color="?android:attr/colorBackground" />
|
||||||
<corners
|
<corners
|
||||||
android:topLeftRadius="?android:attr/dialogCornerRadius"
|
android:topLeftRadius="@dimen/dialogCornerRadius"
|
||||||
android:topRightRadius="?android:attr/dialogCornerRadius"
|
android:topRightRadius="@dimen/dialogCornerRadius" />
|
||||||
android:bottomLeftRadius="0dp"
|
|
||||||
android:bottomRightRadius="0dp"
|
|
||||||
/>
|
|
||||||
</shape>
|
</shape>
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Copyright (C) 2021 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.
|
|
||||||
-->
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item>
|
|
||||||
<shape android:shape="rectangle">
|
|
||||||
<solid android:color="?android:attr/colorBackground" />
|
|
||||||
<padding android:top="16dp"/>
|
|
||||||
</shape>
|
|
||||||
</item>
|
|
||||||
<item android:gravity="center">
|
|
||||||
<shape android:shape="rectangle">
|
|
||||||
<solid android:color="?android:attr/textColorSecondary" />
|
|
||||||
<size android:width="48dp" android:height="2dp" />
|
|
||||||
</shape>
|
|
||||||
</item>
|
|
||||||
</layer-list>
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Copyright (C) 2021 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.
|
|
||||||
-->
|
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:shape="rectangle">
|
|
||||||
<solid android:color="@color/surface" />
|
|
||||||
<corners
|
|
||||||
android:topLeftRadius="@dimen/default_dialog_corner_radius"
|
|
||||||
android:topRightRadius="@dimen/default_dialog_corner_radius"
|
|
||||||
android:bottomLeftRadius="0dp"
|
|
||||||
android:bottomRightRadius="0dp"
|
|
||||||
/>
|
|
||||||
</shape>
|
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
android:id="@+id/widgets_bottom_sheet"
|
android:id="@+id/widgets_bottom_sheet"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/widgets_bottom_sheet_background"
|
android:background="@drawable/bg_rounded_corner_bottom_sheet"
|
||||||
android:paddingTop="16dp"
|
android:paddingTop="16dp"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
<View
|
<View
|
||||||
|
|||||||
@@ -19,13 +19,21 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:theme="?attr/widgetsTheme" >
|
android:theme="?attr/widgetsTheme">
|
||||||
|
|
||||||
<com.android.launcher3.views.TopRoundedCornerView
|
<com.android.launcher3.views.SpringRelativeLayout
|
||||||
android:id="@+id/container"
|
android:id="@+id/container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="?android:attr/colorBackground">
|
android:background="@drawable/bg_widgets_full_sheet">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/collapse_handle"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="2dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:background="?android:attr/textColorSecondary"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/no_widgets_text"
|
android:id="@+id/no_widgets_text"
|
||||||
@@ -35,6 +43,7 @@
|
|||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:fontFamily="sans-serif-medium"
|
android:fontFamily="sans-serif-medium"
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
|
android:layout_below="@id/search_and_recommendations_container"
|
||||||
tools:text="No widgets available" />
|
tools:text="No widgets available" />
|
||||||
|
|
||||||
<!-- Fast scroller popup -->
|
<!-- Fast scroller popup -->
|
||||||
@@ -57,9 +66,10 @@
|
|||||||
android:id="@+id/search_widgets_list_view"
|
android:id="@+id/search_widgets_list_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/collapse_handle"
|
||||||
android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
|
android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:clipToPadding="false" />
|
android:clipToPadding="false" />
|
||||||
|
|
||||||
</com.android.launcher3.views.TopRoundedCornerView>
|
</com.android.launcher3.views.SpringRelativeLayout>
|
||||||
</com.android.launcher3.widget.picker.WidgetsFullSheet>
|
</com.android.launcher3.widget.picker.WidgetsFullSheet>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
|
android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:paddingTop="@dimen/widget_picker_view_pager_top_padding"
|
android:layout_below="@id/collapse_handle"
|
||||||
android:descendantFocusability="afterDescendants"
|
android:descendantFocusability="afterDescendants"
|
||||||
launcher:pageIndicator="@+id/tabs">
|
launcher:pageIndicator="@+id/tabs">
|
||||||
|
|
||||||
@@ -40,5 +40,84 @@
|
|||||||
|
|
||||||
</com.android.launcher3.workprofile.PersonalWorkPagedView>
|
</com.android.launcher3.workprofile.PersonalWorkPagedView>
|
||||||
|
|
||||||
<include layout="@layout/widgets_personal_work_tabs"/>
|
<!-- SearchAndRecommendationsView contains the tab layout as well -->
|
||||||
|
<com.android.launcher3.widget.picker.SearchAndRecommendationsView
|
||||||
|
android:id="@+id/search_and_recommendations_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
|
||||||
|
android:layout_below="@id/collapse_handle"
|
||||||
|
android:paddingBottom="0dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:text="@string/widget_button_text"/>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/search_bar_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:elevation="0.1dp"
|
||||||
|
android:background="?android:attr/colorBackground"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
android:clipToPadding="false">
|
||||||
|
<include layout="@layout/widgets_search_bar" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
|
||||||
|
android:id="@+id/recommended_widget_table"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:background="@drawable/widgets_recommendation_background"
|
||||||
|
android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip
|
||||||
|
android:id="@+id/tabs"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingVertical="8dp"
|
||||||
|
android:paddingLeft="@dimen/widget_tabs_horizontal_padding"
|
||||||
|
android:paddingRight="@dimen/widget_tabs_horizontal_padding"
|
||||||
|
android:background="?android:attr/colorBackground"
|
||||||
|
style="@style/TextHeadline">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/tab_personal"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginEnd="@dimen/widget_tabs_button_horizontal_padding"
|
||||||
|
android:layout_marginVertical="@dimen/widget_apps_tabs_vertical_padding"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@drawable/all_apps_tabs_background"
|
||||||
|
android:text="@string/widgets_full_sheet_personal_tab"
|
||||||
|
android:textColor="@color/all_apps_tab_text"
|
||||||
|
android:textSize="14sp"
|
||||||
|
style="?android:attr/borderlessButtonStyle" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/tab_work"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginEnd="@dimen/widget_tabs_button_horizontal_padding"
|
||||||
|
android:layout_marginVertical="@dimen/widget_apps_tabs_vertical_padding"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:background="@drawable/all_apps_tabs_background"
|
||||||
|
android:text="@string/widgets_full_sheet_work_tab"
|
||||||
|
android:textColor="@color/all_apps_tab_text"
|
||||||
|
android:textSize="14sp"
|
||||||
|
style="?android:attr/borderlessButtonStyle" />
|
||||||
|
</com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip>
|
||||||
|
|
||||||
|
</com.android.launcher3.widget.picker.SearchAndRecommendationsView>
|
||||||
</merge>
|
</merge>
|
||||||
@@ -13,10 +13,54 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
<com.android.launcher3.widget.picker.WidgetsRecyclerView
|
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
<com.android.launcher3.widget.picker.WidgetsRecyclerView
|
||||||
android:id="@+id/primary_widgets_list_view"
|
android:id="@+id/primary_widgets_list_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_below="@id/collapse_handle"
|
||||||
android:layout_height="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false" />
|
android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
|
||||||
|
android:clipToPadding="false" />
|
||||||
|
|
||||||
|
<!-- SearchAndRecommendationsView without the tab layout as well -->
|
||||||
|
<com.android.launcher3.widget.picker.SearchAndRecommendationsView
|
||||||
|
android:id="@+id/search_and_recommendations_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
|
||||||
|
android:layout_below="@id/collapse_handle"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:text="@string/widget_button_text"/>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/search_bar_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:elevation="0.1dp"
|
||||||
|
android:background="?android:attr/colorBackground"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
android:clipToPadding="false">
|
||||||
|
<include layout="@layout/widgets_search_bar" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
|
||||||
|
android:id="@+id/recommended_widget_table"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:background="@drawable/widgets_recommendation_background"
|
||||||
|
android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</com.android.launcher3.widget.picker.SearchAndRecommendationsView>
|
||||||
|
|
||||||
|
</merge>
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Copyright (C) 2021 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.widget.picker.SearchAndRecommendationsView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/search_and_recommendations_container"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
|
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:id="@+id/collapse_handle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="18dp"
|
|
||||||
android:elevation="0.1dp"
|
|
||||||
android:background="@drawable/bg_widgets_picker_handle"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/title"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:textSize="24sp"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
android:text="@string/widget_button_text"/>
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/search_bar_container"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:elevation="0.1dp"
|
|
||||||
android:background="?android:attr/colorBackground"
|
|
||||||
android:paddingBottom="8dp"
|
|
||||||
android:clipToPadding="false">
|
|
||||||
<include layout="@layout/widgets_search_bar" />
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
|
|
||||||
android:id="@+id/recommended_widget_table"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:background="@drawable/widgets_recommendation_background"
|
|
||||||
android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
|
|
||||||
android:visibility="gone" />
|
|
||||||
</com.android.launcher3.widget.picker.SearchAndRecommendationsView>
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
~ Copyright (C) 2021 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.workprofile.PersonalWorkSlidingTabStrip
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/tabs"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="@dimen/all_apps_header_pill_height"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_marginHorizontal="@dimen/widget_tabs_horizontal_margin"
|
|
||||||
style="@style/TextHeadline">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/tab_personal"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_marginEnd="@dimen/widget_tabs_button_horizontal_padding"
|
|
||||||
android:layout_marginVertical="@dimen/widget_apps_tabs_vertical_padding"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:background="@drawable/all_apps_tabs_background"
|
|
||||||
android:text="@string/widgets_full_sheet_personal_tab"
|
|
||||||
android:textColor="@color/all_apps_tab_text"
|
|
||||||
android:textSize="14sp"
|
|
||||||
style="?android:attr/borderlessButtonStyle" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/tab_work"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_marginEnd="@dimen/widget_tabs_button_horizontal_padding"
|
|
||||||
android:layout_marginVertical="@dimen/widget_apps_tabs_vertical_padding"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:background="@drawable/all_apps_tabs_background"
|
|
||||||
android:text="@string/widgets_full_sheet_work_tab"
|
|
||||||
android:textColor="@color/all_apps_tab_text"
|
|
||||||
android:textSize="14sp"
|
|
||||||
style="?android:attr/borderlessButtonStyle" />
|
|
||||||
</com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip>
|
|
||||||
@@ -143,8 +143,7 @@
|
|||||||
<dimen name="widget_cell_font_size">14sp</dimen>
|
<dimen name="widget_cell_font_size">14sp</dimen>
|
||||||
|
|
||||||
<dimen name="widget_tabs_button_horizontal_padding">4dp</dimen>
|
<dimen name="widget_tabs_button_horizontal_padding">4dp</dimen>
|
||||||
<dimen name="widget_tabs_horizontal_margin">32dp</dimen>
|
<dimen name="widget_tabs_horizontal_padding">16dp</dimen>
|
||||||
<dimen name="widget_apps_header_pill_height">48dp</dimen>
|
|
||||||
<dimen name="widget_apps_tabs_vertical_padding">6dp</dimen>
|
<dimen name="widget_apps_tabs_vertical_padding">6dp</dimen>
|
||||||
|
|
||||||
<dimen name="recommended_widgets_table_vertical_padding">8dp</dimen>
|
<dimen name="recommended_widgets_table_vertical_padding">8dp</dimen>
|
||||||
@@ -178,8 +177,6 @@
|
|||||||
<dimen name="widget_picker_education_tip_max_width">308dp</dimen>
|
<dimen name="widget_picker_education_tip_max_width">308dp</dimen>
|
||||||
<dimen name="widget_picker_education_tip_min_margin">4dp</dimen>
|
<dimen name="widget_picker_education_tip_min_margin">4dp</dimen>
|
||||||
|
|
||||||
<dimen name="widget_picker_view_pager_top_padding">10dp</dimen>
|
|
||||||
|
|
||||||
<!-- Padding applied to shortcut previews -->
|
<!-- Padding applied to shortcut previews -->
|
||||||
<dimen name="shortcut_preview_padding_left">0dp</dimen>
|
<dimen name="shortcut_preview_padding_left">0dp</dimen>
|
||||||
<dimen name="shortcut_preview_padding_right">0dp</dimen>
|
<dimen name="shortcut_preview_padding_right">0dp</dimen>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<item type="id" name="apps_list_view_work" />
|
<item type="id" name="apps_list_view_work" />
|
||||||
<item type="id" name="tag_widget_entry" />
|
<item type="id" name="tag_widget_entry" />
|
||||||
|
<item type="id" name="view_type_widgets_space" />
|
||||||
<item type="id" name="view_type_widgets_list" />
|
<item type="id" name="view_type_widgets_list" />
|
||||||
<item type="id" name="view_type_widgets_header" />
|
<item type="id" name="view_type_widgets_header" />
|
||||||
<item type="id" name="view_type_widgets_search_header" />
|
<item type="id" name="view_type_widgets_search_header" />
|
||||||
|
|||||||
+1
-1
@@ -82,7 +82,7 @@ public final class WidgetsListAdapterTest {
|
|||||||
mTestProfile.numColumns = 5;
|
mTestProfile.numColumns = 5;
|
||||||
mUserHandle = Process.myUserHandle();
|
mUserHandle = Process.myUserHandle();
|
||||||
mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache,
|
mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache,
|
||||||
mIconCache, null, null);
|
mIconCache, () -> 0, null, null);
|
||||||
mAdapter.registerAdapterDataObserver(mListener);
|
mAdapter.registerAdapterDataObserver(mListener);
|
||||||
|
|
||||||
doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0))
|
doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0))
|
||||||
|
|||||||
+1
-12
@@ -41,7 +41,6 @@ import com.android.launcher3.model.WidgetItem;
|
|||||||
import com.android.launcher3.model.data.PackageItemInfo;
|
import com.android.launcher3.model.data.PackageItemInfo;
|
||||||
import com.android.launcher3.testing.TestActivity;
|
import com.android.launcher3.testing.TestActivity;
|
||||||
import com.android.launcher3.util.PackageUserKey;
|
import com.android.launcher3.util.PackageUserKey;
|
||||||
import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
|
|
||||||
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
|
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
|
||||||
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
|
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
|
||||||
|
|
||||||
@@ -79,8 +78,6 @@ public final class WidgetsListHeaderViewHolderBinderTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private DeviceProfile mDeviceProfile;
|
private DeviceProfile mDeviceProfile;
|
||||||
@Mock
|
@Mock
|
||||||
private DatabaseWidgetPreviewLoader mWidgetPreviewLoader;
|
|
||||||
@Mock
|
|
||||||
private OnHeaderClickListener mOnHeaderClickListener;
|
private OnHeaderClickListener mOnHeaderClickListener;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -99,18 +96,10 @@ public final class WidgetsListHeaderViewHolderBinderTest {
|
|||||||
ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
|
ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
|
||||||
return componentWithLabel.getComponent().getShortClassName();
|
return componentWithLabel.getComponent().getShortClassName();
|
||||||
}).when(mIconCache).getTitleNoCache(any());
|
}).when(mIconCache).getTitleNoCache(any());
|
||||||
|
|
||||||
WidgetsListAdapter widgetsListAdapter = new WidgetsListAdapter(mContext,
|
|
||||||
LayoutInflater.from(mTestActivity),
|
|
||||||
mWidgetPreviewLoader,
|
|
||||||
mIconCache,
|
|
||||||
/* iconClickListener= */ view -> {},
|
|
||||||
/* iconLongClickListener= */ view -> false);
|
|
||||||
mViewHolderBinder = new WidgetsListHeaderViewHolderBinder(
|
mViewHolderBinder = new WidgetsListHeaderViewHolderBinder(
|
||||||
LayoutInflater.from(mTestActivity),
|
LayoutInflater.from(mTestActivity),
|
||||||
mOnHeaderClickListener,
|
mOnHeaderClickListener,
|
||||||
new WidgetsListDrawableFactory(mTestActivity),
|
new WidgetsListDrawableFactory(mTestActivity));
|
||||||
widgetsListAdapter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|||||||
+1
-12
@@ -41,7 +41,6 @@ import com.android.launcher3.model.WidgetItem;
|
|||||||
import com.android.launcher3.model.data.PackageItemInfo;
|
import com.android.launcher3.model.data.PackageItemInfo;
|
||||||
import com.android.launcher3.testing.TestActivity;
|
import com.android.launcher3.testing.TestActivity;
|
||||||
import com.android.launcher3.util.PackageUserKey;
|
import com.android.launcher3.util.PackageUserKey;
|
||||||
import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
|
|
||||||
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
|
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
|
||||||
import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
|
import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
|
||||||
|
|
||||||
@@ -79,8 +78,6 @@ public final class WidgetsListSearchHeaderViewHolderBinderTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private DeviceProfile mDeviceProfile;
|
private DeviceProfile mDeviceProfile;
|
||||||
@Mock
|
@Mock
|
||||||
private DatabaseWidgetPreviewLoader mWidgetPreviewLoader;
|
|
||||||
@Mock
|
|
||||||
private OnHeaderClickListener mOnHeaderClickListener;
|
private OnHeaderClickListener mOnHeaderClickListener;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -99,18 +96,10 @@ public final class WidgetsListSearchHeaderViewHolderBinderTest {
|
|||||||
ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
|
ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
|
||||||
return componentWithLabel.getComponent().getShortClassName();
|
return componentWithLabel.getComponent().getShortClassName();
|
||||||
}).when(mIconCache).getTitleNoCache(any());
|
}).when(mIconCache).getTitleNoCache(any());
|
||||||
|
|
||||||
WidgetsListAdapter widgetsListAdapter = new WidgetsListAdapter(mContext,
|
|
||||||
LayoutInflater.from(mTestActivity),
|
|
||||||
mWidgetPreviewLoader,
|
|
||||||
mIconCache,
|
|
||||||
/* iconClickListener= */ view -> {},
|
|
||||||
/* iconLongClickListener= */ view -> false);
|
|
||||||
mViewHolderBinder = new WidgetsListSearchHeaderViewHolderBinder(
|
mViewHolderBinder = new WidgetsListSearchHeaderViewHolderBinder(
|
||||||
LayoutInflater.from(mTestActivity),
|
LayoutInflater.from(mTestActivity),
|
||||||
mOnHeaderClickListener,
|
mOnHeaderClickListener,
|
||||||
new WidgetsListDrawableFactory(mTestActivity),
|
new WidgetsListDrawableFactory(mTestActivity));
|
||||||
widgetsListAdapter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|||||||
+1
-8
@@ -107,19 +107,12 @@ public final class WidgetsListTableViewHolderBinderTest {
|
|||||||
return componentWithLabel.getComponent().getShortClassName();
|
return componentWithLabel.getComponent().getShortClassName();
|
||||||
}).when(mIconCache).getTitleNoCache(any());
|
}).when(mIconCache).getTitleNoCache(any());
|
||||||
|
|
||||||
WidgetsListAdapter widgetsListAdapter = new WidgetsListAdapter(mContext,
|
|
||||||
LayoutInflater.from(mTestActivity),
|
|
||||||
mWidgetPreviewLoader,
|
|
||||||
mIconCache,
|
|
||||||
/* iconClickListener= */ view -> {},
|
|
||||||
/* iconLongClickListener= */ view -> false);
|
|
||||||
mViewHolderBinder = new WidgetsListTableViewHolderBinder(
|
mViewHolderBinder = new WidgetsListTableViewHolderBinder(
|
||||||
LayoutInflater.from(mTestActivity),
|
LayoutInflater.from(mTestActivity),
|
||||||
mOnIconClickListener,
|
mOnIconClickListener,
|
||||||
mOnLongClickListener,
|
mOnLongClickListener,
|
||||||
new CachingWidgetPreviewLoader(mWidgetPreviewLoader),
|
new CachingWidgetPreviewLoader(mWidgetPreviewLoader),
|
||||||
new WidgetsListDrawableFactory(mTestActivity),
|
new WidgetsListDrawableFactory(mTestActivity));
|
||||||
widgetsListAdapter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|||||||
@@ -17,8 +17,12 @@ package com.android.launcher3.recyclerview;
|
|||||||
|
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.IntDef;
|
||||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and populates views with data
|
* Creates and populates views with data
|
||||||
*
|
*
|
||||||
@@ -26,6 +30,15 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
|||||||
* @param <V> A subclass of {@link ViewHolder} which holds references to views.
|
* @param <V> A subclass of {@link ViewHolder} which holds references to views.
|
||||||
*/
|
*/
|
||||||
public interface ViewHolderBinder<T, V extends ViewHolder> {
|
public interface ViewHolderBinder<T, V extends ViewHolder> {
|
||||||
|
|
||||||
|
int POSITION_DEFAULT = 0;
|
||||||
|
int POSITION_FIRST = 1 << 0;
|
||||||
|
int POSITION_LAST = 1 << 1;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@IntDef(value = {POSITION_DEFAULT, POSITION_FIRST, POSITION_LAST}, flag = true)
|
||||||
|
@interface ListPosition {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new view, and attach it to the parent {@link ViewGroup}. Then, populates UI
|
* Creates a new view, and attach it to the parent {@link ViewGroup}. Then, populates UI
|
||||||
* references in a {@link ViewHolder}.
|
* references in a {@link ViewHolder}.
|
||||||
@@ -33,7 +46,7 @@ public interface ViewHolderBinder<T, V extends ViewHolder> {
|
|||||||
V newViewHolder(ViewGroup parent);
|
V newViewHolder(ViewGroup parent);
|
||||||
|
|
||||||
/** Populate UI references in {@link ViewHolder} with data. */
|
/** Populate UI references in {@link ViewHolder} with data. */
|
||||||
void bindViewHolder(V viewHolder, T data, int position);
|
void bindViewHolder(V viewHolder, T data, @ListPosition int position);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the view is recycled. Views are recycled in batches once they are sufficiently
|
* Called when the view is recycled. Views are recycled in batches once they are sufficiently
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import android.util.AttributeSet;
|
|||||||
import android.util.Property;
|
import android.util.Property;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.view.animation.Interpolator;
|
import android.view.animation.Interpolator;
|
||||||
|
|
||||||
import com.android.launcher3.AbstractFloatingView;
|
import com.android.launcher3.AbstractFloatingView;
|
||||||
@@ -68,7 +69,7 @@ public abstract class AbstractSlideInView<T extends Context & ActivityContext>
|
|||||||
protected final SingleAxisSwipeDetector mSwipeDetector;
|
protected final SingleAxisSwipeDetector mSwipeDetector;
|
||||||
protected final ObjectAnimator mOpenCloseAnimator;
|
protected final ObjectAnimator mOpenCloseAnimator;
|
||||||
|
|
||||||
protected View mContent;
|
protected ViewGroup mContent;
|
||||||
protected final View mColorScrim;
|
protected final View mColorScrim;
|
||||||
protected Interpolator mScrollInterpolator;
|
protected Interpolator mScrollInterpolator;
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import android.view.ViewConfiguration;
|
|||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
@@ -131,7 +130,6 @@ public class RecyclerViewFastScroller extends View {
|
|||||||
|
|
||||||
protected BaseRecyclerView mRv;
|
protected BaseRecyclerView mRv;
|
||||||
private RecyclerView.OnScrollListener mOnScrollListener;
|
private RecyclerView.OnScrollListener mOnScrollListener;
|
||||||
@Nullable private OnFastScrollChangeListener mOnFastScrollChangeListener;
|
|
||||||
|
|
||||||
private int mDownX;
|
private int mDownX;
|
||||||
private int mDownY;
|
private int mDownY;
|
||||||
@@ -208,7 +206,6 @@ public class RecyclerViewFastScroller extends View {
|
|||||||
int rvCurrentOffsetY = mRv.getCurrentScrollY();
|
int rvCurrentOffsetY = mRv.getCurrentScrollY();
|
||||||
if (mRvOffsetY != rvCurrentOffsetY) {
|
if (mRvOffsetY != rvCurrentOffsetY) {
|
||||||
mRvOffsetY = mRv.getCurrentScrollY();
|
mRvOffsetY = mRv.getCurrentScrollY();
|
||||||
notifyScrollChanged();
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -216,7 +213,6 @@ public class RecyclerViewFastScroller extends View {
|
|||||||
mThumbOffsetY = y;
|
mThumbOffsetY = y;
|
||||||
invalidate();
|
invalidate();
|
||||||
mRvOffsetY = mRv.getCurrentScrollY();
|
mRvOffsetY = mRv.getCurrentScrollY();
|
||||||
notifyScrollChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getThumbOffsetY() {
|
public int getThumbOffsetY() {
|
||||||
@@ -461,23 +457,4 @@ public class RecyclerViewFastScroller extends View {
|
|||||||
public void setIsRecyclerViewFirstChildInParent(boolean isRecyclerViewFirstChildInParent) {
|
public void setIsRecyclerViewFirstChildInParent(boolean isRecyclerViewFirstChildInParent) {
|
||||||
mIsRecyclerViewFirstChildInParent = isRecyclerViewFirstChildInParent;
|
mIsRecyclerViewFirstChildInParent = isRecyclerViewFirstChildInParent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOnFastScrollChangeListener(
|
|
||||||
@Nullable OnFastScrollChangeListener onFastScrollChangeListener) {
|
|
||||||
mOnFastScrollChangeListener = onFastScrollChangeListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyScrollChanged() {
|
|
||||||
if (mOnFastScrollChangeListener != null) {
|
|
||||||
mOnFastScrollChangeListener.onScrollChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A callback that is invoked when there is a scroll change in {@link RecyclerViewFastScroller}.
|
|
||||||
*/
|
|
||||||
public interface OnFastScrollChangeListener {
|
|
||||||
/** Called when the recycler view scroll has changed. */
|
|
||||||
void onScrollChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 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.views;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Path;
|
|
||||||
import android.graphics.RectF;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
|
|
||||||
import com.android.launcher3.util.Themes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* View with top rounded corners.
|
|
||||||
*/
|
|
||||||
public class TopRoundedCornerView extends SpringRelativeLayout {
|
|
||||||
|
|
||||||
private final RectF mRect = new RectF();
|
|
||||||
private final Path mClipPath = new Path();
|
|
||||||
private float[] mRadii;
|
|
||||||
|
|
||||||
public TopRoundedCornerView(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
||||||
super(context, attrs, defStyleAttr);
|
|
||||||
|
|
||||||
float radius = Themes.getDialogCornerRadius(context);
|
|
||||||
mRadii = new float[] {radius, radius, radius, radius, 0, 0, 0, 0};
|
|
||||||
}
|
|
||||||
|
|
||||||
public TopRoundedCornerView(Context context, AttributeSet attrs) {
|
|
||||||
this(context, attrs, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void draw(Canvas canvas) {
|
|
||||||
canvas.save();
|
|
||||||
canvas.clipPath(mClipPath);
|
|
||||||
super.draw(canvas);
|
|
||||||
canvas.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
||||||
mRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
|
|
||||||
mClipPath.reset();
|
|
||||||
mClipPath.addRoundRect(mRect, mRadii, Path.Direction.CW);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.widget.model;
|
||||||
|
|
||||||
|
import com.android.launcher3.model.data.PackageItemInfo;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entry representing the top empty space
|
||||||
|
*/
|
||||||
|
public class WidgetListSpaceEntry extends WidgetsListBaseEntry {
|
||||||
|
|
||||||
|
public WidgetListSpaceEntry() {
|
||||||
|
super(new PackageItemInfo(""), "", Collections.EMPTY_LIST);
|
||||||
|
mPkgItem.title = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRank() {
|
||||||
|
return RANK_WIDGETS_TOP_SPACE;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -73,11 +73,13 @@ public abstract class WidgetsListBaseEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Retention(SOURCE)
|
@Retention(SOURCE)
|
||||||
@IntDef({RANK_WIDGETS_LIST_HEADER, RANK_WIDGETS_LIST_SEARCH_HEADER, RANK_WIDGETS_LIST_CONTENT})
|
@IntDef({RANK_WIDGETS_TOP_SPACE, RANK_WIDGETS_LIST_HEADER, RANK_WIDGETS_LIST_SEARCH_HEADER,
|
||||||
|
RANK_WIDGETS_LIST_CONTENT})
|
||||||
public @interface Rank {
|
public @interface Rank {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final int RANK_WIDGETS_LIST_HEADER = 1;
|
public static final int RANK_WIDGETS_TOP_SPACE = 1;
|
||||||
public static final int RANK_WIDGETS_LIST_SEARCH_HEADER = 2;
|
public static final int RANK_WIDGETS_LIST_HEADER = 2;
|
||||||
public static final int RANK_WIDGETS_LIST_CONTENT = 3;
|
public static final int RANK_WIDGETS_LIST_SEARCH_HEADER = 3;
|
||||||
|
public static final int RANK_WIDGETS_LIST_CONTENT = 4;
|
||||||
}
|
}
|
||||||
|
|||||||
+138
-288
@@ -15,297 +15,148 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.launcher3.widget.picker;
|
package com.android.launcher3.widget.picker;
|
||||||
|
|
||||||
import android.animation.ValueAnimator;
|
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
|
||||||
import android.graphics.Point;
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.util.FloatProperty;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup.MarginLayoutParams;
|
import android.view.ViewGroup;
|
||||||
import android.widget.RelativeLayout;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.android.launcher3.views.RecyclerViewFastScroller;
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.widget.picker.WidgetsFullSheet.SearchAndRecommendationViewHolder;
|
import com.android.launcher3.widget.picker.WidgetsSpaceViewHolderBinder.EmptySpaceView;
|
||||||
import com.android.launcher3.workprofile.PersonalWorkPagedView;
|
import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A controller which measures & updates {@link WidgetsFullSheet}'s views padding, margin and
|
* A controller which measures & updates {@link WidgetsFullSheet}'s views padding, margin and
|
||||||
* vertical displacement upon scrolling.
|
* vertical displacement upon scrolling.
|
||||||
*/
|
*/
|
||||||
final class SearchAndRecommendationsScrollController implements
|
final class SearchAndRecommendationsScrollController implements
|
||||||
RecyclerViewFastScroller.OnFastScrollChangeListener, ValueAnimator.AnimatorUpdateListener {
|
RecyclerView.OnChildAttachStateChangeListener {
|
||||||
private final boolean mHasWorkProfile;
|
|
||||||
private final SearchAndRecommendationViewHolder mViewHolder;
|
|
||||||
private final View mSearchAndRecommendationViewParent;
|
|
||||||
private final WidgetsRecyclerView mPrimaryRecyclerView;
|
|
||||||
private final WidgetsRecyclerView mSearchRecyclerView;
|
|
||||||
private final TextView mNoWidgetsView;
|
|
||||||
private final int mTabsHeight;
|
|
||||||
private final ValueAnimator mAnimator = ValueAnimator.ofInt(0, 0);
|
|
||||||
private final Point mTempOffset = new Point();
|
|
||||||
private int mBottomInset;
|
|
||||||
|
|
||||||
// The following are only non null if mHasWorkProfile is true.
|
private static final FloatProperty<SearchAndRecommendationsScrollController> SCROLL_OFFSET =
|
||||||
@Nullable private final WidgetsRecyclerView mWorkRecyclerView;
|
new FloatProperty<SearchAndRecommendationsScrollController>("scrollAnimOffset") {
|
||||||
@Nullable private final View mPrimaryWorkTabsView;
|
@Override
|
||||||
@Nullable private final PersonalWorkPagedView mPrimaryWorkViewPager;
|
public void setValue(SearchAndRecommendationsScrollController controller, float offset) {
|
||||||
|
controller.mScrollOffset = offset;
|
||||||
|
controller.updateHeaderScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Float get(SearchAndRecommendationsScrollController controller) {
|
||||||
|
return controller.mScrollOffset;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final MotionEventProxyMethod INTERCEPT_PROXY = ViewGroup::onInterceptTouchEvent;
|
||||||
|
private static final MotionEventProxyMethod TOUCH_PROXY = ViewGroup::onTouchEvent;
|
||||||
|
|
||||||
|
final SearchAndRecommendationsView mContainer;
|
||||||
|
final View mSearchBarContainer;
|
||||||
|
final WidgetsSearchBar mSearchBar;
|
||||||
|
final TextView mHeaderTitle;
|
||||||
|
final WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
|
||||||
|
@Nullable final View mTabBar;
|
||||||
|
|
||||||
private WidgetsRecyclerView mCurrentRecyclerView;
|
private WidgetsRecyclerView mCurrentRecyclerView;
|
||||||
private int mCurrentRecyclerViewScrollY = 0;
|
private EmptySpaceView mCurrentEmptySpaceView;
|
||||||
|
|
||||||
private OnContentChangeListener mOnContentChangeListener = () -> onScrollChanged();
|
private float mLastScroll = 0;
|
||||||
|
private float mScrollOffset = 0;
|
||||||
/**
|
private Animator mOffsetAnimator;
|
||||||
* The vertical distance, in pixels, until the search is pinned at the top of the screen when
|
|
||||||
* the user scrolls down the recycler view.
|
|
||||||
*/
|
|
||||||
private int mCollapsibleHeightForSearch = 0;
|
|
||||||
/**
|
|
||||||
* The vertical distance, in pixels, until the recommendation table disappears from the top of
|
|
||||||
* the screen when the user scrolls down the recycler view.
|
|
||||||
*/
|
|
||||||
private int mCollapsibleHeightForRecommendation = 0;
|
|
||||||
/**
|
|
||||||
* The vertical distance, in pixels, until the tabs is pinned at the top of the screen when the
|
|
||||||
* user scrolls down the recycler view.
|
|
||||||
*
|
|
||||||
* <p>Always 0 if there is no work profile.
|
|
||||||
*/
|
|
||||||
private int mCollapsibleHeightForTabs = 0;
|
|
||||||
|
|
||||||
private boolean mShouldForwardToRecyclerView = false;
|
private boolean mShouldForwardToRecyclerView = false;
|
||||||
|
|
||||||
|
private int mHeaderHeight;
|
||||||
|
|
||||||
SearchAndRecommendationsScrollController(
|
SearchAndRecommendationsScrollController(
|
||||||
boolean hasWorkProfile,
|
SearchAndRecommendationsView searchAndRecommendationContainer) {
|
||||||
int tabsHeight,
|
mContainer = searchAndRecommendationContainer;
|
||||||
SearchAndRecommendationViewHolder viewHolder,
|
mSearchBarContainer = mContainer.findViewById(R.id.search_bar_container);
|
||||||
WidgetsRecyclerView primaryRecyclerView,
|
mSearchBar = mContainer.findViewById(R.id.widgets_search_bar);
|
||||||
@Nullable WidgetsRecyclerView workRecyclerView,
|
mHeaderTitle = mContainer.findViewById(R.id.title);
|
||||||
WidgetsRecyclerView searchRecyclerView,
|
mRecommendedWidgetsTable = mContainer.findViewById(R.id.recommended_widget_table);
|
||||||
@Nullable View personalWorkTabsView,
|
mTabBar = mContainer.findViewById(R.id.tabs);
|
||||||
@Nullable PersonalWorkPagedView primaryWorkViewPager,
|
|
||||||
TextView noWidgetsView) {
|
mContainer.setSearchAndRecommendationScrollController(this);
|
||||||
mHasWorkProfile = hasWorkProfile;
|
|
||||||
mViewHolder = viewHolder;
|
|
||||||
mViewHolder.mContainer.setSearchAndRecommendationScrollController(this);
|
|
||||||
mSearchAndRecommendationViewParent = (View) mViewHolder.mContainer.getParent();
|
|
||||||
mPrimaryRecyclerView = primaryRecyclerView;
|
|
||||||
mWorkRecyclerView = workRecyclerView;
|
|
||||||
mSearchRecyclerView = searchRecyclerView;
|
|
||||||
mPrimaryWorkTabsView = personalWorkTabsView;
|
|
||||||
mPrimaryWorkViewPager = primaryWorkViewPager;
|
|
||||||
mTabsHeight = tabsHeight;
|
|
||||||
mNoWidgetsView = noWidgetsView;
|
|
||||||
setCurrentRecyclerView(mPrimaryRecyclerView, /* animateReset= */ false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCurrentRecyclerView(WidgetsRecyclerView currentRecyclerView) {
|
public void setCurrentRecyclerView(WidgetsRecyclerView currentRecyclerView) {
|
||||||
setCurrentRecyclerView(currentRecyclerView, /* animateReset= */ true);
|
boolean animateReset = mCurrentRecyclerView != null;
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets the current active {@link WidgetsRecyclerView}. */
|
|
||||||
private void setCurrentRecyclerView(WidgetsRecyclerView currentRecyclerView,
|
|
||||||
boolean animateReset) {
|
|
||||||
if (mCurrentRecyclerView == currentRecyclerView) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mCurrentRecyclerView != null) {
|
if (mCurrentRecyclerView != null) {
|
||||||
mCurrentRecyclerView.setOnContentChangeListener(null);
|
mCurrentRecyclerView.removeOnChildAttachStateChangeListener(this);
|
||||||
}
|
}
|
||||||
mCurrentRecyclerView = currentRecyclerView;
|
mCurrentRecyclerView = currentRecyclerView;
|
||||||
mCurrentRecyclerView.setOnContentChangeListener(mOnContentChangeListener);
|
mCurrentRecyclerView.addOnChildAttachStateChangeListener(this);
|
||||||
|
findCurrentEmptyView();
|
||||||
reset(animateReset);
|
reset(animateReset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public int getHeaderHeight() {
|
||||||
* Updates padding of {@link WidgetsFullSheet} contents to include {@code bottomInset} wherever
|
return mHeaderHeight;
|
||||||
* necessary.
|
}
|
||||||
*/
|
|
||||||
public boolean updateBottomInset(int bottomInset) {
|
private void updateHeaderScroll() {
|
||||||
mBottomInset = bottomInset;
|
mLastScroll = getCurrentScroll();
|
||||||
return updateMarginAndPadding();
|
mHeaderTitle.setTranslationY(mLastScroll);
|
||||||
|
mRecommendedWidgetsTable.setTranslationY(mLastScroll);
|
||||||
|
|
||||||
|
float searchYDisplacement = Math.max(mLastScroll, -mSearchBarContainer.getTop());
|
||||||
|
mSearchBarContainer.setTranslationY(searchYDisplacement);
|
||||||
|
|
||||||
|
if (mTabBar != null) {
|
||||||
|
float tabsDisplacement = Math.max(mLastScroll, -mTabBar.getTop()
|
||||||
|
+ mSearchBarContainer.getHeight());
|
||||||
|
mTabBar.setTranslationY(tabsDisplacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getCurrentScroll() {
|
||||||
|
return mScrollOffset + (mCurrentEmptySpaceView == null ? 0 : mCurrentEmptySpaceView.getY());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the margin and padding of {@link WidgetsFullSheet} to accumulate collapsible views.
|
* Updates the scrollable header height
|
||||||
*
|
*
|
||||||
* @return {@code true} if margins or/and padding of views in the search and recommendations
|
* @return {@code true} if the header height or dependent property changed.
|
||||||
* container have been updated.
|
|
||||||
*/
|
*/
|
||||||
public boolean updateMarginAndPadding() {
|
public boolean updateHeaderHeight() {
|
||||||
boolean hasMarginOrPaddingUpdated = false;
|
boolean hasSizeUpdated = false;
|
||||||
mCollapsibleHeightForSearch = measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle);
|
|
||||||
mCollapsibleHeightForRecommendation =
|
|
||||||
measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle)
|
|
||||||
+ measureHeightWithVerticalMargins(mViewHolder.mCollapseHandle)
|
|
||||||
+ measureHeightWithVerticalMargins((View) mViewHolder.mSearchBarContainer)
|
|
||||||
+ measureHeightWithVerticalMargins(mViewHolder.mRecommendedWidgetsTable);
|
|
||||||
|
|
||||||
int topContainerHeight = measureHeightWithVerticalMargins(mViewHolder.mContainer);
|
int headerHeight = mContainer.getMeasuredHeight();
|
||||||
int noWidgetsViewHeight = topContainerHeight - mBottomInset;
|
if (headerHeight != mHeaderHeight) {
|
||||||
|
mHeaderHeight = headerHeight;
|
||||||
if (mHasWorkProfile) {
|
hasSizeUpdated = true;
|
||||||
mCollapsibleHeightForTabs = measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle)
|
|
||||||
+ measureHeightWithVerticalMargins(mViewHolder.mRecommendedWidgetsTable);
|
|
||||||
// In a work profile setup, the full widget sheet contains the following views:
|
|
||||||
// ------- (pinned) -|
|
|
||||||
// Widgets (collapsible) -|---> LinearLayout for search & recommendations
|
|
||||||
// Search bar (pinned) -|
|
|
||||||
// Widgets recommendation (collapsible)-|
|
|
||||||
// Personal | Work (pinned)
|
|
||||||
// View Pager
|
|
||||||
//
|
|
||||||
// Views after the search & recommendations are not bound by RelativelyLayout param.
|
|
||||||
// To position them on the expected location, padding & margin are added to these views
|
|
||||||
|
|
||||||
// Tabs should have a padding of the height of the search & recommendations container.
|
|
||||||
RelativeLayout.LayoutParams tabsLayoutParams =
|
|
||||||
(RelativeLayout.LayoutParams) mPrimaryWorkTabsView.getLayoutParams();
|
|
||||||
tabsLayoutParams.topMargin = topContainerHeight;
|
|
||||||
mPrimaryWorkTabsView.setLayoutParams(tabsLayoutParams);
|
|
||||||
|
|
||||||
// Instead of setting the top offset directly, we split the top offset into two values:
|
|
||||||
// 1. topOffsetAfterAllViewsCollapsed: this is the top offset after all collapsible
|
|
||||||
// views are no longer visible on the screen.
|
|
||||||
// This value is set as the margin for the view pager.
|
|
||||||
// 2. mMaxCollapsibleDistance
|
|
||||||
// This value is set as the padding for the recycler views in order to work with
|
|
||||||
// clipToPadding="false", which is an attribute for not showing top / bottom padding
|
|
||||||
// when a recycler view has not reached the top or bottom of the list.
|
|
||||||
// e.g. a list of 10 entries, only 3 entries are visible at a time.
|
|
||||||
// case 1: recycler view is scrolled to the top. Top padding is visible/
|
|
||||||
// (top padding)
|
|
||||||
// item 1
|
|
||||||
// item 2
|
|
||||||
// item 3
|
|
||||||
//
|
|
||||||
// case 2: recycler view is scrolled to the middle. No padding is visible.
|
|
||||||
// item 4
|
|
||||||
// item 5
|
|
||||||
// item 6
|
|
||||||
//
|
|
||||||
// case 3: recycler view is scrolled to the end. bottom padding is visible.
|
|
||||||
// item 8
|
|
||||||
// item 9
|
|
||||||
// item 10
|
|
||||||
// (bottom padding): not set in this case.
|
|
||||||
//
|
|
||||||
// When the views are first inflated, the sum of topOffsetAfterAllViewsCollapsed and
|
|
||||||
// mMaxCollapsibleDistance should equal to the top container height.
|
|
||||||
int topOffsetAfterAllViewsCollapsed =
|
|
||||||
topContainerHeight + mTabsHeight - mCollapsibleHeightForTabs;
|
|
||||||
|
|
||||||
if (mPrimaryWorkTabsView.getVisibility() == View.VISIBLE) {
|
|
||||||
noWidgetsViewHeight += mTabsHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
RelativeLayout.LayoutParams viewPagerLayoutParams =
|
|
||||||
(RelativeLayout.LayoutParams) mPrimaryWorkViewPager.getLayoutParams();
|
|
||||||
if (viewPagerLayoutParams.topMargin != topOffsetAfterAllViewsCollapsed) {
|
|
||||||
viewPagerLayoutParams.topMargin = topOffsetAfterAllViewsCollapsed;
|
|
||||||
mPrimaryWorkViewPager.setLayoutParams(viewPagerLayoutParams);
|
|
||||||
hasMarginOrPaddingUpdated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mPrimaryRecyclerView.getPaddingTop() != mCollapsibleHeightForTabs) {
|
|
||||||
mPrimaryRecyclerView.setPadding(
|
|
||||||
mPrimaryRecyclerView.getPaddingLeft(),
|
|
||||||
mCollapsibleHeightForTabs,
|
|
||||||
mPrimaryRecyclerView.getPaddingRight(),
|
|
||||||
mPrimaryRecyclerView.getPaddingBottom());
|
|
||||||
hasMarginOrPaddingUpdated = true;
|
|
||||||
}
|
|
||||||
if (mWorkRecyclerView.getPaddingTop() != mCollapsibleHeightForTabs) {
|
|
||||||
mWorkRecyclerView.setPadding(
|
|
||||||
mWorkRecyclerView.getPaddingLeft(),
|
|
||||||
mCollapsibleHeightForTabs,
|
|
||||||
mWorkRecyclerView.getPaddingRight(),
|
|
||||||
mWorkRecyclerView.getPaddingBottom());
|
|
||||||
hasMarginOrPaddingUpdated = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (mPrimaryRecyclerView.getPaddingTop() != topContainerHeight) {
|
|
||||||
mPrimaryRecyclerView.setPadding(
|
|
||||||
mPrimaryRecyclerView.getPaddingLeft(),
|
|
||||||
topContainerHeight,
|
|
||||||
mPrimaryRecyclerView.getPaddingRight(),
|
|
||||||
mPrimaryRecyclerView.getPaddingBottom());
|
|
||||||
hasMarginOrPaddingUpdated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mSearchRecyclerView.getPaddingTop() != topContainerHeight) {
|
|
||||||
mSearchRecyclerView.setPadding(
|
|
||||||
mSearchRecyclerView.getPaddingLeft(),
|
|
||||||
topContainerHeight,
|
|
||||||
mSearchRecyclerView.getPaddingRight(),
|
|
||||||
mSearchRecyclerView.getPaddingBottom());
|
|
||||||
hasMarginOrPaddingUpdated = true;
|
|
||||||
}
|
|
||||||
if (mNoWidgetsView.getPaddingTop() != noWidgetsViewHeight) {
|
|
||||||
mNoWidgetsView.setPadding(
|
|
||||||
mNoWidgetsView.getPaddingLeft(),
|
|
||||||
noWidgetsViewHeight,
|
|
||||||
mNoWidgetsView.getPaddingRight(),
|
|
||||||
mNoWidgetsView.getPaddingBottom());
|
|
||||||
hasMarginOrPaddingUpdated = true;
|
|
||||||
}
|
|
||||||
return hasMarginOrPaddingUpdated;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onScrollChanged() {
|
|
||||||
int recyclerViewYOffset = mCurrentRecyclerView.getCurrentScrollY();
|
|
||||||
if (recyclerViewYOffset < 0) return;
|
|
||||||
mCurrentRecyclerViewScrollY = recyclerViewYOffset;
|
|
||||||
if (mAnimator.isStarted()) {
|
|
||||||
mAnimator.cancel();
|
|
||||||
}
|
|
||||||
applyVerticalTransition();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes the displacement of collapsible views (e.g. title & widget recommendations) and fixed
|
|
||||||
* views (e.g. recycler views, tabs) upon scrolling / content changes in the recycler view.
|
|
||||||
*/
|
|
||||||
private void applyVerticalTransition() {
|
|
||||||
if (mCollapsibleHeightForRecommendation > 0) {
|
|
||||||
int yDisplacement = Math.max(-mCurrentRecyclerViewScrollY,
|
|
||||||
-mCollapsibleHeightForRecommendation);
|
|
||||||
mViewHolder.mHeaderTitle.setTranslationY(yDisplacement);
|
|
||||||
mViewHolder.mRecommendedWidgetsTable.setTranslationY(yDisplacement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCollapsibleHeightForSearch > 0) {
|
if (mCurrentEmptySpaceView != null
|
||||||
int searchYDisplacement = Math.max(-mCurrentRecyclerViewScrollY,
|
&& mCurrentEmptySpaceView.setFixedHeight(mHeaderHeight)) {
|
||||||
-mCollapsibleHeightForSearch);
|
hasSizeUpdated = true;
|
||||||
mViewHolder.mSearchBarContainer.setTranslationY(searchYDisplacement);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mHasWorkProfile && mCollapsibleHeightForTabs > 0) {
|
|
||||||
int yDisplacementForTabs = Math.max(-mCurrentRecyclerViewScrollY,
|
|
||||||
-mCollapsibleHeightForTabs);
|
|
||||||
mPrimaryWorkTabsView.setTranslationY(yDisplacementForTabs);
|
|
||||||
}
|
}
|
||||||
|
return hasSizeUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Resets any previous view translation. */
|
/** Resets any previous view translation. */
|
||||||
public void reset(boolean animate) {
|
public void reset(boolean animate) {
|
||||||
if (mCurrentRecyclerViewScrollY == 0) {
|
if (mOffsetAnimator != null) {
|
||||||
return;
|
mOffsetAnimator.cancel();
|
||||||
}
|
mOffsetAnimator = null;
|
||||||
if (mAnimator.isStarted()) {
|
|
||||||
mAnimator.cancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (animate) {
|
mScrollOffset = 0;
|
||||||
mAnimator.setIntValues(mCurrentRecyclerViewScrollY, 0);
|
if (!animate) {
|
||||||
mAnimator.addUpdateListener(this);
|
updateHeaderScroll();
|
||||||
mAnimator.setDuration(300);
|
|
||||||
mAnimator.start();
|
|
||||||
} else {
|
} else {
|
||||||
mCurrentRecyclerViewScrollY = 0;
|
float startValue = mLastScroll - getCurrentScroll();
|
||||||
applyVerticalTransition();
|
mOffsetAnimator = ObjectAnimator.ofFloat(this, SCROLL_OFFSET, startValue, 0);
|
||||||
|
mOffsetAnimator.addListener(forEndCallback(() -> mOffsetAnimator = null));
|
||||||
|
mOffsetAnimator.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,61 +164,60 @@ final class SearchAndRecommendationsScrollController implements
|
|||||||
* Returns {@code true} if a touch event should be intercepted by this controller.
|
* Returns {@code true} if a touch event should be intercepted by this controller.
|
||||||
*/
|
*/
|
||||||
public boolean onInterceptTouchEvent(MotionEvent event) {
|
public boolean onInterceptTouchEvent(MotionEvent event) {
|
||||||
calculateMotionEventOffset(mTempOffset);
|
return (mShouldForwardToRecyclerView = proxyMotionEvent(event, INTERCEPT_PROXY));
|
||||||
event.offsetLocation(mTempOffset.x, mTempOffset.y);
|
|
||||||
try {
|
|
||||||
mShouldForwardToRecyclerView = mCurrentRecyclerView.onInterceptTouchEvent(event);
|
|
||||||
return mShouldForwardToRecyclerView;
|
|
||||||
} finally {
|
|
||||||
event.offsetLocation(-mTempOffset.x, -mTempOffset.y);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true} if this controller has intercepted and consumed a touch event.
|
* Returns {@code true} if this controller has intercepted and consumed a touch event.
|
||||||
*/
|
*/
|
||||||
public boolean onTouchEvent(MotionEvent event) {
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
if (mShouldForwardToRecyclerView) {
|
return mShouldForwardToRecyclerView && proxyMotionEvent(event, TOUCH_PROXY);
|
||||||
calculateMotionEventOffset(mTempOffset);
|
|
||||||
event.offsetLocation(mTempOffset.x, mTempOffset.y);
|
|
||||||
try {
|
|
||||||
return mCurrentRecyclerView.onTouchEvent(event);
|
|
||||||
} finally {
|
|
||||||
event.offsetLocation(-mTempOffset.x, -mTempOffset.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void calculateMotionEventOffset(Point p) {
|
private boolean proxyMotionEvent(MotionEvent event, MotionEventProxyMethod method) {
|
||||||
p.x = mViewHolder.mContainer.getLeft() - mCurrentRecyclerView.getLeft()
|
float dx = mCurrentRecyclerView.getLeft() - mContainer.getLeft();
|
||||||
- mSearchAndRecommendationViewParent.getLeft();
|
float dy = mCurrentRecyclerView.getTop() - mContainer.getTop();
|
||||||
p.y = mViewHolder.mContainer.getTop() - mCurrentRecyclerView.getTop()
|
event.offsetLocation(dx, dy);
|
||||||
- mSearchAndRecommendationViewParent.getTop();
|
try {
|
||||||
}
|
return method.proxyEvent(mCurrentRecyclerView, event);
|
||||||
|
} finally {
|
||||||
/** private the height, in pixel, + the vertical margins of a given view. */
|
event.offsetLocation(-dx, -dy);
|
||||||
private static int measureHeightWithVerticalMargins(View view) {
|
|
||||||
if (view.getVisibility() != View.VISIBLE) {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
|
|
||||||
return view.getMeasuredHeight() + marginLayoutParams.bottomMargin
|
|
||||||
+ marginLayoutParams.topMargin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationUpdate(ValueAnimator animation) {
|
public void onChildViewAttachedToWindow(@NonNull View view) {
|
||||||
mCurrentRecyclerViewScrollY = (Integer) animation.getAnimatedValue();
|
if (view instanceof EmptySpaceView) {
|
||||||
applyVerticalTransition();
|
findCurrentEmptyView();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* A listener to be notified when there is a content change in the recycler view that may affect
|
public void onChildViewDetachedFromWindow(@NonNull View view) {
|
||||||
* the relative position of the search and recommendation container.
|
if (view == mCurrentEmptySpaceView) {
|
||||||
*/
|
findCurrentEmptyView();
|
||||||
public interface OnContentChangeListener {
|
}
|
||||||
/** Notifies a content change in the recycler view. */
|
}
|
||||||
void onContentChanged();
|
|
||||||
|
private void findCurrentEmptyView() {
|
||||||
|
if (mCurrentEmptySpaceView != null) {
|
||||||
|
mCurrentEmptySpaceView.setOnYChangeCallback(null);
|
||||||
|
mCurrentEmptySpaceView = null;
|
||||||
|
}
|
||||||
|
int childCount = mCurrentRecyclerView.getChildCount();
|
||||||
|
for (int i = 0; i < childCount; i++) {
|
||||||
|
View view = mCurrentRecyclerView.getChildAt(i);
|
||||||
|
if (view instanceof EmptySpaceView) {
|
||||||
|
mCurrentEmptySpaceView = (EmptySpaceView) view;
|
||||||
|
mCurrentEmptySpaceView.setFixedHeight(getHeaderHeight());
|
||||||
|
mCurrentEmptySpaceView.setOnYChangeCallback(this::updateHeaderScroll);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface MotionEventProxyMethod {
|
||||||
|
|
||||||
|
boolean proxyEvent(ViewGroup view, MotionEvent event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,13 +57,12 @@ import com.android.launcher3.compat.AccessibilityManagerCompat;
|
|||||||
import com.android.launcher3.model.WidgetItem;
|
import com.android.launcher3.model.WidgetItem;
|
||||||
import com.android.launcher3.views.ArrowTipView;
|
import com.android.launcher3.views.ArrowTipView;
|
||||||
import com.android.launcher3.views.RecyclerViewFastScroller;
|
import com.android.launcher3.views.RecyclerViewFastScroller;
|
||||||
import com.android.launcher3.views.TopRoundedCornerView;
|
import com.android.launcher3.views.SpringRelativeLayout;
|
||||||
import com.android.launcher3.views.WidgetsEduView;
|
import com.android.launcher3.views.WidgetsEduView;
|
||||||
import com.android.launcher3.widget.BaseWidgetSheet;
|
import com.android.launcher3.widget.BaseWidgetSheet;
|
||||||
import com.android.launcher3.widget.LauncherAppWidgetHost.ProviderChangedListener;
|
import com.android.launcher3.widget.LauncherAppWidgetHost.ProviderChangedListener;
|
||||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||||
import com.android.launcher3.widget.picker.search.SearchModeListener;
|
import com.android.launcher3.widget.picker.search.SearchModeListener;
|
||||||
import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
|
|
||||||
import com.android.launcher3.widget.util.WidgetsTableUtils;
|
import com.android.launcher3.widget.util.WidgetsTableUtils;
|
||||||
import com.android.launcher3.workprofile.PersonalWorkPagedView;
|
import com.android.launcher3.workprofile.PersonalWorkPagedView;
|
||||||
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
|
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
|
||||||
@@ -79,7 +78,6 @@ import java.util.stream.IntStream;
|
|||||||
public class WidgetsFullSheet extends BaseWidgetSheet
|
public class WidgetsFullSheet extends BaseWidgetSheet
|
||||||
implements ProviderChangedListener, OnActivePageChangedListener,
|
implements ProviderChangedListener, OnActivePageChangedListener,
|
||||||
WidgetsRecyclerView.HeaderViewDimensionsProvider, SearchModeListener {
|
WidgetsRecyclerView.HeaderViewDimensionsProvider, SearchModeListener {
|
||||||
private static final String TAG = WidgetsFullSheet.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final long DEFAULT_OPEN_DURATION = 267;
|
private static final long DEFAULT_OPEN_DURATION = 267;
|
||||||
private static final long FADE_IN_DURATION = 150;
|
private static final long FADE_IN_DURATION = 150;
|
||||||
@@ -149,8 +147,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
};
|
};
|
||||||
|
|
||||||
private final int mTabsHeight;
|
private final int mTabsHeight;
|
||||||
private final int mViewPagerTopPadding;
|
|
||||||
private final int mSearchAndRecommendationContainerBottomMargin;
|
|
||||||
private final int mWidgetSheetContentHorizontalPadding;
|
private final int mWidgetSheetContentHorizontalPadding;
|
||||||
|
|
||||||
@Nullable private WidgetsRecyclerView mCurrentWidgetsRecyclerView;
|
@Nullable private WidgetsRecyclerView mCurrentWidgetsRecyclerView;
|
||||||
@@ -158,10 +154,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
private boolean mIsInSearchMode;
|
private boolean mIsInSearchMode;
|
||||||
private boolean mIsNoWidgetsViewNeeded;
|
private boolean mIsNoWidgetsViewNeeded;
|
||||||
private int mMaxSpansPerRow = DEFAULT_MAX_HORIZONTAL_SPANS;
|
private int mMaxSpansPerRow = DEFAULT_MAX_HORIZONTAL_SPANS;
|
||||||
private View mTabsView;
|
|
||||||
private TextView mNoWidgetsView;
|
private TextView mNoWidgetsView;
|
||||||
private SearchAndRecommendationViewHolder mSearchAndRecommendationViewHolder;
|
private SearchAndRecommendationsScrollController mSearchScrollController;
|
||||||
private SearchAndRecommendationsScrollController mSearchAndRecommendationsScrollController;
|
|
||||||
|
|
||||||
public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
|
public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
@@ -174,14 +168,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
mTabsHeight = mHasWorkProfile
|
mTabsHeight = mHasWorkProfile
|
||||||
? resources.getDimensionPixelSize(R.dimen.all_apps_header_pill_height)
|
? resources.getDimensionPixelSize(R.dimen.all_apps_header_pill_height)
|
||||||
: 0;
|
: 0;
|
||||||
mViewPagerTopPadding = mHasWorkProfile
|
|
||||||
? getContext().getResources()
|
|
||||||
.getDimensionPixelSize(R.dimen.widget_picker_view_pager_top_padding)
|
|
||||||
: 0;
|
|
||||||
mSearchAndRecommendationContainerBottomMargin = resources.getDimensionPixelSize(
|
|
||||||
mHasWorkProfile
|
|
||||||
? R.dimen.search_and_recommended_widgets_container_small_bottom_margin
|
|
||||||
: R.dimen.search_and_recommended_widgets_container_bottom_margin);
|
|
||||||
mWidgetSheetContentHorizontalPadding = 2 * resources.getDimensionPixelSize(
|
mWidgetSheetContentHorizontalPadding = 2 * resources.getDimensionPixelSize(
|
||||||
R.dimen.widget_cell_horizontal_padding);
|
R.dimen.widget_cell_horizontal_padding);
|
||||||
}
|
}
|
||||||
@@ -194,12 +180,11 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
protected void onFinishInflate() {
|
protected void onFinishInflate() {
|
||||||
super.onFinishInflate();
|
super.onFinishInflate();
|
||||||
mContent = findViewById(R.id.container);
|
mContent = findViewById(R.id.container);
|
||||||
TopRoundedCornerView springLayout = (TopRoundedCornerView) mContent;
|
|
||||||
|
|
||||||
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
|
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
|
||||||
int contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view
|
int contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view
|
||||||
: R.layout.widgets_full_sheet_recyclerview;
|
: R.layout.widgets_full_sheet_recyclerview;
|
||||||
layoutInflater.inflate(contentLayoutRes, springLayout, true);
|
layoutInflater.inflate(contentLayoutRes, mContent, true);
|
||||||
|
|
||||||
RecyclerViewFastScroller fastScroller = findViewById(R.id.fast_scroller);
|
RecyclerViewFastScroller fastScroller = findViewById(R.id.fast_scroller);
|
||||||
mAdapters.get(AdapterHolder.PRIMARY).setup(findViewById(R.id.primary_widgets_list_view));
|
mAdapters.get(AdapterHolder.PRIMARY).setup(findViewById(R.id.primary_widgets_list_view));
|
||||||
@@ -209,7 +194,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
mViewPager.initParentViews(this);
|
mViewPager.initParentViews(this);
|
||||||
mViewPager.getPageIndicator().setOnActivePageChangedListener(this);
|
mViewPager.getPageIndicator().setOnActivePageChangedListener(this);
|
||||||
mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.PRIMARY);
|
mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.PRIMARY);
|
||||||
mTabsView = findViewById(R.id.tabs);
|
|
||||||
findViewById(R.id.tab_personal)
|
findViewById(R.id.tab_personal)
|
||||||
.setOnClickListener((View view) -> mViewPager.snapToPage(0));
|
.setOnClickListener((View view) -> mViewPager.snapToPage(0));
|
||||||
findViewById(R.id.tab_work)
|
findViewById(R.id.tab_work)
|
||||||
@@ -220,33 +204,18 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
mViewPager = null;
|
mViewPager = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
layoutInflater.inflate(R.layout.widgets_full_sheet_search_and_recommendations, springLayout,
|
|
||||||
true);
|
|
||||||
mNoWidgetsView = findViewById(R.id.no_widgets_text);
|
mNoWidgetsView = findViewById(R.id.no_widgets_text);
|
||||||
mSearchAndRecommendationViewHolder = new SearchAndRecommendationViewHolder(
|
mSearchScrollController = new SearchAndRecommendationsScrollController(
|
||||||
findViewById(R.id.search_and_recommendations_container));
|
findViewById(R.id.search_and_recommendations_container));
|
||||||
TopRoundedCornerView.LayoutParams layoutParams =
|
mSearchScrollController.setCurrentRecyclerView(
|
||||||
(TopRoundedCornerView.LayoutParams)
|
findViewById(R.id.primary_widgets_list_view));
|
||||||
mSearchAndRecommendationViewHolder.mContainer.getLayoutParams();
|
mSearchScrollController.mRecommendedWidgetsTable.setWidgetCellLongClickListener(this);
|
||||||
layoutParams.bottomMargin = mSearchAndRecommendationContainerBottomMargin;
|
mSearchScrollController.mRecommendedWidgetsTable.setWidgetCellOnClickListener(this);
|
||||||
mSearchAndRecommendationViewHolder.mContainer.setLayoutParams(layoutParams);
|
|
||||||
mSearchAndRecommendationsScrollController = new SearchAndRecommendationsScrollController(
|
|
||||||
mHasWorkProfile,
|
|
||||||
mTabsHeight,
|
|
||||||
mSearchAndRecommendationViewHolder,
|
|
||||||
findViewById(R.id.primary_widgets_list_view),
|
|
||||||
mHasWorkProfile ? findViewById(R.id.work_widgets_list_view) : null,
|
|
||||||
findViewById(R.id.search_widgets_list_view),
|
|
||||||
mTabsView,
|
|
||||||
mViewPager,
|
|
||||||
mNoWidgetsView);
|
|
||||||
fastScroller.setOnFastScrollChangeListener(mSearchAndRecommendationsScrollController);
|
|
||||||
|
|
||||||
|
|
||||||
onRecommendedWidgetsBound();
|
onRecommendedWidgetsBound();
|
||||||
onWidgetsBound();
|
onWidgetsBound();
|
||||||
|
|
||||||
mSearchAndRecommendationViewHolder.mSearchBar.initialize(
|
mSearchScrollController.mSearchBar.initialize(
|
||||||
mActivityContext.getPopupDataProvider(), /* searchModeListener= */ this);
|
mActivityContext.getPopupDataProvider(), /* searchModeListener= */ this);
|
||||||
|
|
||||||
setUpEducationViewsIfNeeded();
|
setUpEducationViewsIfNeeded();
|
||||||
@@ -270,12 +239,13 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
reset();
|
reset();
|
||||||
resetExpandedHeaders();
|
resetExpandedHeaders();
|
||||||
mCurrentWidgetsRecyclerView = recyclerView;
|
mCurrentWidgetsRecyclerView = recyclerView;
|
||||||
mSearchAndRecommendationsScrollController.setCurrentRecyclerView(recyclerView);
|
mSearchScrollController.setCurrentRecyclerView(recyclerView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateRecyclerViewVisibility(AdapterHolder adapterHolder) {
|
private void updateRecyclerViewVisibility(AdapterHolder adapterHolder) {
|
||||||
boolean isWidgetAvailable = adapterHolder.mWidgetsListAdapter.getItemCount() > 0;
|
// The first item is always an empty space entry. Look for any more items.
|
||||||
|
boolean isWidgetAvailable = adapterHolder.mWidgetsListAdapter.hasVisibleEntries();
|
||||||
adapterHolder.mWidgetsRecyclerView.setVisibility(isWidgetAvailable ? VISIBLE : GONE);
|
adapterHolder.mWidgetsRecyclerView.setVisibility(isWidgetAvailable ? VISIBLE : GONE);
|
||||||
|
|
||||||
mNoWidgetsView.setText(
|
mNoWidgetsView.setText(
|
||||||
@@ -291,7 +261,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView.scrollToTop();
|
mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView.scrollToTop();
|
||||||
}
|
}
|
||||||
mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.scrollToTop();
|
mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.scrollToTop();
|
||||||
mSearchAndRecommendationsScrollController.reset(/* animate= */ true);
|
mSearchScrollController.reset(/* animate= */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -340,7 +310,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
if (mHasWorkProfile) {
|
if (mHasWorkProfile) {
|
||||||
setBottomPadding(mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView, insets.bottom);
|
setBottomPadding(mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView, insets.bottom);
|
||||||
}
|
}
|
||||||
mSearchAndRecommendationsScrollController.updateBottomInset(insets.bottom);
|
((MarginLayoutParams) mNoWidgetsView.getLayoutParams()).bottomMargin = insets.bottom;
|
||||||
|
|
||||||
if (insets.bottom > 0) {
|
if (insets.bottom > 0) {
|
||||||
setupNavBarColor();
|
setupNavBarColor();
|
||||||
} else {
|
} else {
|
||||||
@@ -360,7 +331,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onContentHorizontalMarginChanged(int contentHorizontalMarginInPx) {
|
protected void onContentHorizontalMarginChanged(int contentHorizontalMarginInPx) {
|
||||||
setContentViewChildHorizontalMargin(mSearchAndRecommendationViewHolder.mContainer,
|
setContentViewChildHorizontalMargin(mSearchScrollController.mContainer,
|
||||||
contentHorizontalMarginInPx);
|
contentHorizontalMarginInPx);
|
||||||
if (mViewPager == null) {
|
if (mViewPager == null) {
|
||||||
setContentViewChildHorizontalMargin(
|
setContentViewChildHorizontalMargin(
|
||||||
@@ -385,14 +356,14 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
doMeasure(widthMeasureSpec, heightMeasureSpec);
|
doMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
|
||||||
if (mSearchAndRecommendationsScrollController.updateMarginAndPadding()) {
|
if (mSearchScrollController.updateHeaderHeight()) {
|
||||||
doMeasure(widthMeasureSpec, heightMeasureSpec);
|
doMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateMaxSpansPerRow()) {
|
if (updateMaxSpansPerRow()) {
|
||||||
doMeasure(widthMeasureSpec, heightMeasureSpec);
|
doMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
|
||||||
if (mSearchAndRecommendationsScrollController.updateMarginAndPadding()) {
|
if (mSearchScrollController.updateHeaderHeight()) {
|
||||||
doMeasure(widthMeasureSpec, heightMeasureSpec);
|
doMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -455,7 +426,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
|
|
||||||
if (mHasWorkProfile) {
|
if (mHasWorkProfile) {
|
||||||
mViewPager.setVisibility(VISIBLE);
|
mViewPager.setVisibility(VISIBLE);
|
||||||
mTabsView.setVisibility(VISIBLE);
|
mSearchScrollController.mTabBar.setVisibility(VISIBLE);
|
||||||
AdapterHolder workUserAdapterHolder = mAdapters.get(AdapterHolder.WORK);
|
AdapterHolder workUserAdapterHolder = mAdapters.get(AdapterHolder.WORK);
|
||||||
workUserAdapterHolder.mWidgetsListAdapter.setWidgets(allWidgets);
|
workUserAdapterHolder.mWidgetsListAdapter.setWidgets(allWidgets);
|
||||||
onActivePageChanged(mViewPager.getCurrentPage());
|
onActivePageChanged(mViewPager.getCurrentPage());
|
||||||
@@ -465,9 +436,9 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
// Update recommended widgets section so that it occupies appropriate space on screen to
|
// Update recommended widgets section so that it occupies appropriate space on screen to
|
||||||
// leave enough space for presence/absence of mNoWidgetsView.
|
// leave enough space for presence/absence of mNoWidgetsView.
|
||||||
boolean isNoWidgetsViewNeeded =
|
boolean isNoWidgetsViewNeeded =
|
||||||
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.getItemCount() == 0
|
!mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.hasVisibleEntries()
|
||||||
|| (mHasWorkProfile && mAdapters.get(AdapterHolder.WORK)
|
|| (mHasWorkProfile && mAdapters.get(AdapterHolder.WORK)
|
||||||
.mWidgetsListAdapter.getItemCount() == 0);
|
.mWidgetsListAdapter.hasVisibleEntries());
|
||||||
if (mIsNoWidgetsViewNeeded != isNoWidgetsViewNeeded) {
|
if (mIsNoWidgetsViewNeeded != isNoWidgetsViewNeeded) {
|
||||||
mIsNoWidgetsViewNeeded = isNoWidgetsViewNeeded;
|
mIsNoWidgetsViewNeeded = isNoWidgetsViewNeeded;
|
||||||
onRecommendedWidgetsBound();
|
onRecommendedWidgetsBound();
|
||||||
@@ -491,8 +462,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
mViewPager.snapToPage(AdapterHolder.PRIMARY);
|
mViewPager.snapToPage(AdapterHolder.PRIMARY);
|
||||||
}
|
}
|
||||||
attachScrollbarToRecyclerView(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView);
|
attachScrollbarToRecyclerView(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView);
|
||||||
|
|
||||||
mSearchAndRecommendationsScrollController.updateMarginAndPadding();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -505,10 +474,10 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
private void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
|
private void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
|
||||||
mIsInSearchMode = isInSearchMode;
|
mIsInSearchMode = isInSearchMode;
|
||||||
if (isInSearchMode) {
|
if (isInSearchMode) {
|
||||||
mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable.setVisibility(GONE);
|
mSearchScrollController.mRecommendedWidgetsTable.setVisibility(GONE);
|
||||||
if (mHasWorkProfile) {
|
if (mHasWorkProfile) {
|
||||||
mViewPager.setVisibility(GONE);
|
mViewPager.setVisibility(GONE);
|
||||||
mTabsView.setVisibility(GONE);
|
mSearchScrollController.mTabBar.setVisibility(GONE);
|
||||||
} else {
|
} else {
|
||||||
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView.setVisibility(GONE);
|
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView.setVisibility(GONE);
|
||||||
}
|
}
|
||||||
@@ -536,8 +505,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
}
|
}
|
||||||
List<WidgetItem> recommendedWidgets =
|
List<WidgetItem> recommendedWidgets =
|
||||||
mActivityContext.getPopupDataProvider().getRecommendedWidgets();
|
mActivityContext.getPopupDataProvider().getRecommendedWidgets();
|
||||||
WidgetsRecommendationTableLayout table =
|
WidgetsRecommendationTableLayout table = mSearchScrollController.mRecommendedWidgetsTable;
|
||||||
mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable;
|
|
||||||
if (recommendedWidgets.size() > 0) {
|
if (recommendedWidgets.size() > 0) {
|
||||||
float noWidgetsViewHeight = 0;
|
float noWidgetsViewHeight = 0;
|
||||||
if (mIsNoWidgetsViewNeeded) {
|
if (mIsNoWidgetsViewNeeded) {
|
||||||
@@ -554,7 +522,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
makeMeasureSpec(mActivityContext.getDeviceProfile().availableHeightPx,
|
makeMeasureSpec(mActivityContext.getDeviceProfile().availableHeightPx,
|
||||||
MeasureSpec.EXACTLY));
|
MeasureSpec.EXACTLY));
|
||||||
float maxTableHeight = (mContent.getMeasuredHeight()
|
float maxTableHeight = (mContent.getMeasuredHeight()
|
||||||
- mTabsHeight - mViewPagerTopPadding - getHeaderViewHeight()
|
- mTabsHeight - getHeaderViewHeight()
|
||||||
- noWidgetsViewHeight) * RECOMMENDATION_TABLE_HEIGHT_RATIO;
|
- noWidgetsViewHeight) * RECOMMENDATION_TABLE_HEIGHT_RATIO;
|
||||||
|
|
||||||
List<ArrayList<WidgetItem>> recommendedWidgetsInTable =
|
List<ArrayList<WidgetItem>> recommendedWidgetsInTable =
|
||||||
@@ -617,10 +585,10 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
mNoIntercept = !getRecyclerView().shouldContainerScroll(ev, getPopupContainer());
|
mNoIntercept = !getRecyclerView().shouldContainerScroll(ev, getPopupContainer());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mSearchAndRecommendationViewHolder.mSearchBar.isSearchBarFocused()
|
if (mSearchScrollController.mSearchBar.isSearchBarFocused()
|
||||||
&& !getPopupContainer().isEventOverView(
|
&& !getPopupContainer().isEventOverView(
|
||||||
mSearchAndRecommendationViewHolder.mSearchBarContainer, ev)) {
|
mSearchScrollController.mSearchBarContainer, ev)) {
|
||||||
mSearchAndRecommendationViewHolder.mSearchBar.clearSearchBarFocus();
|
mSearchScrollController.mSearchBar.clearSearchBarFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.onControllerInterceptTouchEvent(ev);
|
return super.onControllerInterceptTouchEvent(ev);
|
||||||
@@ -661,10 +629,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getHeaderViewHeight() {
|
public int getHeaderViewHeight() {
|
||||||
return measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mCollapseHandle)
|
return measureHeightWithVerticalMargins(mSearchScrollController.mHeaderTitle)
|
||||||
+ measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mHeaderTitle)
|
+ measureHeightWithVerticalMargins(mSearchScrollController.mSearchBarContainer);
|
||||||
+ measureHeightWithVerticalMargins(
|
|
||||||
(View) mSearchAndRecommendationViewHolder.mSearchBarContainer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** private the height, in pixel, + the vertical margins of a given view. */
|
/** private the height, in pixel, + the vertical margins of a given view. */
|
||||||
@@ -681,14 +647,14 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
protected void onConfigurationChanged(Configuration newConfig) {
|
protected void onConfigurationChanged(Configuration newConfig) {
|
||||||
super.onConfigurationChanged(newConfig);
|
super.onConfigurationChanged(newConfig);
|
||||||
if (mIsInSearchMode) {
|
if (mIsInSearchMode) {
|
||||||
mSearchAndRecommendationViewHolder.mSearchBar.reset();
|
mSearchScrollController.mSearchBar.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onBackPressed() {
|
public boolean onBackPressed() {
|
||||||
if (mIsInSearchMode) {
|
if (mIsInSearchMode) {
|
||||||
mSearchAndRecommendationViewHolder.mSearchBar.reset();
|
mSearchScrollController.mSearchBar.reset();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return super.onBackPressed();
|
return super.onBackPressed();
|
||||||
@@ -701,11 +667,10 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable private View getViewToShowEducationTip() {
|
@Nullable private View getViewToShowEducationTip() {
|
||||||
if (mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable.getVisibility() == VISIBLE
|
if (mSearchScrollController.mRecommendedWidgetsTable.getVisibility() == VISIBLE
|
||||||
&& mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable.getChildCount() > 0
|
&& mSearchScrollController.mRecommendedWidgetsTable.getChildCount() > 0) {
|
||||||
) {
|
return ((ViewGroup) mSearchScrollController.mRecommendedWidgetsTable.getChildAt(0))
|
||||||
return ((ViewGroup) mSearchAndRecommendationViewHolder.mRecommendedWidgetsTable
|
.getChildAt(0);
|
||||||
.getChildAt(0)).getChildAt(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AdapterHolder adapterHolder = mAdapters.get(mIsInSearchMode
|
AdapterHolder adapterHolder = mAdapters.get(mIsInSearchMode
|
||||||
@@ -782,6 +747,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
LayoutInflater.from(context),
|
LayoutInflater.from(context),
|
||||||
apps.getWidgetCache(),
|
apps.getWidgetCache(),
|
||||||
apps.getIconCache(),
|
apps.getIconCache(),
|
||||||
|
this::getEmptySpaceHeight,
|
||||||
/* iconClickListener= */ WidgetsFullSheet.this,
|
/* iconClickListener= */ WidgetsFullSheet.this,
|
||||||
/* iconLongClickListener= */ WidgetsFullSheet.this);
|
/* iconLongClickListener= */ WidgetsFullSheet.this);
|
||||||
mWidgetsListAdapter.setHasStableIds(true);
|
mWidgetsListAdapter.setHasStableIds(true);
|
||||||
@@ -801,13 +767,17 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
mWidgetsListItemAnimator.setSupportsChangeAnimations(false);
|
mWidgetsListItemAnimator.setSupportsChangeAnimations(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getEmptySpaceHeight() {
|
||||||
|
return mSearchScrollController.getHeaderHeight();
|
||||||
|
}
|
||||||
|
|
||||||
void setup(WidgetsRecyclerView recyclerView) {
|
void setup(WidgetsRecyclerView recyclerView) {
|
||||||
mWidgetsRecyclerView = recyclerView;
|
mWidgetsRecyclerView = recyclerView;
|
||||||
mWidgetsRecyclerView.setAdapter(mWidgetsListAdapter);
|
mWidgetsRecyclerView.setAdapter(mWidgetsListAdapter);
|
||||||
mWidgetsRecyclerView.setItemAnimator(mWidgetsListItemAnimator);
|
mWidgetsRecyclerView.setItemAnimator(mWidgetsListItemAnimator);
|
||||||
mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this);
|
mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this);
|
||||||
mWidgetsRecyclerView.setEdgeEffectFactory(
|
mWidgetsRecyclerView.setEdgeEffectFactory(
|
||||||
((TopRoundedCornerView) mContent).createEdgeEffectFactory());
|
((SpringRelativeLayout) mContent).createEdgeEffectFactory());
|
||||||
// Recycler view binds to fast scroller when it is attached to screen. Make sure
|
// Recycler view binds to fast scroller when it is attached to screen. Make sure
|
||||||
// search recycler view is bound to fast scroller if user is in search mode at the time
|
// search recycler view is bound to fast scroller if user is in search mode at the time
|
||||||
// of attachment.
|
// of attachment.
|
||||||
@@ -818,29 +788,4 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
|||||||
mWidgetsListAdapter.setMaxHorizontalSpansPerRow(mMaxSpansPerRow);
|
mWidgetsListAdapter.setMaxHorizontalSpansPerRow(mMaxSpansPerRow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class SearchAndRecommendationViewHolder {
|
|
||||||
final SearchAndRecommendationsView mContainer;
|
|
||||||
final View mCollapseHandle;
|
|
||||||
final View mSearchBarContainer;
|
|
||||||
final WidgetsSearchBar mSearchBar;
|
|
||||||
final TextView mHeaderTitle;
|
|
||||||
final WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
|
|
||||||
|
|
||||||
SearchAndRecommendationViewHolder(
|
|
||||||
SearchAndRecommendationsView searchAndRecommendationContainer) {
|
|
||||||
mContainer = searchAndRecommendationContainer;
|
|
||||||
mCollapseHandle = mContainer.findViewById(R.id.collapse_handle);
|
|
||||||
mSearchBarContainer = mContainer.findViewById(R.id.search_bar_container);
|
|
||||||
mSearchBar = mContainer.findViewById(R.id.widgets_search_bar);
|
|
||||||
mHeaderTitle = mContainer.findViewById(R.id.title);
|
|
||||||
mRecommendedWidgetsTable = mContainer.findViewById(R.id.recommended_widget_table);
|
|
||||||
mRecommendedWidgetsTable.setWidgetCellOnTouchListener((view, event) -> {
|
|
||||||
getRecyclerView().onTouchEvent(event);
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
mRecommendedWidgetsTable.setWidgetCellLongClickListener(WidgetsFullSheet.this);
|
|
||||||
mRecommendedWidgetsTable.setWidgetCellOnClickListener(WidgetsFullSheet.this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
package com.android.launcher3.widget.picker;
|
package com.android.launcher3.widget.picker;
|
||||||
|
|
||||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_APP_EXPANDED;
|
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_APP_EXPANDED;
|
||||||
|
import static com.android.launcher3.recyclerview.ViewHolderBinder.POSITION_DEFAULT;
|
||||||
|
import static com.android.launcher3.recyclerview.ViewHolderBinder.POSITION_FIRST;
|
||||||
|
import static com.android.launcher3.recyclerview.ViewHolderBinder.POSITION_LAST;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
@@ -52,6 +55,7 @@ import com.android.launcher3.widget.CachingWidgetPreviewLoader;
|
|||||||
import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
|
import com.android.launcher3.widget.DatabaseWidgetPreviewLoader;
|
||||||
import com.android.launcher3.widget.WidgetCell;
|
import com.android.launcher3.widget.WidgetCell;
|
||||||
import com.android.launcher3.widget.WidgetPreviewLoader.WidgetPreviewLoadedCallback;
|
import com.android.launcher3.widget.WidgetPreviewLoader.WidgetPreviewLoadedCallback;
|
||||||
|
import com.android.launcher3.widget.model.WidgetListSpaceEntry;
|
||||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||||
import com.android.launcher3.widget.model.WidgetsListContentEntry;
|
import com.android.launcher3.widget.model.WidgetsListContentEntry;
|
||||||
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
|
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
|
||||||
@@ -64,6 +68,7 @@ import java.util.Comparator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.OptionalInt;
|
import java.util.OptionalInt;
|
||||||
|
import java.util.function.IntSupplier;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
@@ -84,6 +89,7 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
|||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
/** Uniquely identifies widgets list view type within the app. */
|
/** Uniquely identifies widgets list view type within the app. */
|
||||||
|
private static final int VIEW_TYPE_WIDGETS_SPACE = R.id.view_type_widgets_space;
|
||||||
private static final int VIEW_TYPE_WIDGETS_LIST = R.id.view_type_widgets_list;
|
private static final int VIEW_TYPE_WIDGETS_LIST = R.id.view_type_widgets_list;
|
||||||
private static final int VIEW_TYPE_WIDGETS_HEADER = R.id.view_type_widgets_header;
|
private static final int VIEW_TYPE_WIDGETS_HEADER = R.id.view_type_widgets_header;
|
||||||
private static final int VIEW_TYPE_WIDGETS_SEARCH_HEADER = R.id.view_type_widgets_search_header;
|
private static final int VIEW_TYPE_WIDGETS_SEARCH_HEADER = R.id.view_type_widgets_search_header;
|
||||||
@@ -97,7 +103,7 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
|||||||
private final WidgetListBaseRowEntryComparator mRowComparator =
|
private final WidgetListBaseRowEntryComparator mRowComparator =
|
||||||
new WidgetListBaseRowEntryComparator();
|
new WidgetListBaseRowEntryComparator();
|
||||||
|
|
||||||
private List<WidgetsListBaseEntry> mAllEntries = new ArrayList<>();
|
private final List<WidgetsListBaseEntry> mAllEntries = new ArrayList<>();
|
||||||
private ArrayList<WidgetsListBaseEntry> mVisibleEntries = new ArrayList<>();
|
private ArrayList<WidgetsListBaseEntry> mVisibleEntries = new ArrayList<>();
|
||||||
@Nullable private PackageUserKey mWidgetsContentVisiblePackageUserKey = null;
|
@Nullable private PackageUserKey mWidgetsContentVisiblePackageUserKey = null;
|
||||||
|
|
||||||
@@ -118,6 +124,7 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
|||||||
|
|
||||||
public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
|
public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
|
||||||
DatabaseWidgetPreviewLoader widgetPreviewLoader, IconCache iconCache,
|
DatabaseWidgetPreviewLoader widgetPreviewLoader, IconCache iconCache,
|
||||||
|
IntSupplier emptySpaceHeightProvider,
|
||||||
OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
|
OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mLauncher = Launcher.getLauncher(context);
|
mLauncher = Launcher.getLauncher(context);
|
||||||
@@ -126,22 +133,23 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
|||||||
WidgetsListDrawableFactory listDrawableFactory = new WidgetsListDrawableFactory(context);
|
WidgetsListDrawableFactory listDrawableFactory = new WidgetsListDrawableFactory(context);
|
||||||
mWidgetsListTableViewHolderBinder = new WidgetsListTableViewHolderBinder(
|
mWidgetsListTableViewHolderBinder = new WidgetsListTableViewHolderBinder(
|
||||||
layoutInflater, iconClickListener, iconLongClickListener,
|
layoutInflater, iconClickListener, iconLongClickListener,
|
||||||
mCachingPreviewLoader, listDrawableFactory, /* listAdapter= */ this);
|
mCachingPreviewLoader, listDrawableFactory);
|
||||||
mViewHolderBinders.put(VIEW_TYPE_WIDGETS_LIST, mWidgetsListTableViewHolderBinder);
|
mViewHolderBinders.put(VIEW_TYPE_WIDGETS_LIST, mWidgetsListTableViewHolderBinder);
|
||||||
mViewHolderBinders.put(
|
mViewHolderBinders.put(
|
||||||
VIEW_TYPE_WIDGETS_HEADER,
|
VIEW_TYPE_WIDGETS_HEADER,
|
||||||
new WidgetsListHeaderViewHolderBinder(
|
new WidgetsListHeaderViewHolderBinder(
|
||||||
layoutInflater,
|
layoutInflater,
|
||||||
/* onHeaderClickListener= */ this,
|
/* onHeaderClickListener= */ this,
|
||||||
listDrawableFactory,
|
listDrawableFactory));
|
||||||
/* listAdapter= */ this));
|
|
||||||
mViewHolderBinders.put(
|
mViewHolderBinders.put(
|
||||||
VIEW_TYPE_WIDGETS_SEARCH_HEADER,
|
VIEW_TYPE_WIDGETS_SEARCH_HEADER,
|
||||||
new WidgetsListSearchHeaderViewHolderBinder(
|
new WidgetsListSearchHeaderViewHolderBinder(
|
||||||
layoutInflater,
|
layoutInflater,
|
||||||
/* onHeaderClickListener= */ this,
|
/* onHeaderClickListener= */ this,
|
||||||
listDrawableFactory,
|
listDrawableFactory));
|
||||||
/* listAdapter= */ this));
|
mViewHolderBinders.put(
|
||||||
|
VIEW_TYPE_WIDGETS_SPACE,
|
||||||
|
new WidgetsSpaceViewHolderBinder(emptySpaceHeightProvider));
|
||||||
mShortcutPreviewPadding =
|
mShortcutPreviewPadding =
|
||||||
2 * context.getResources()
|
2 * context.getResources()
|
||||||
.getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
|
.getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
|
||||||
@@ -205,6 +213,14 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
|||||||
return mVisibleEntries.size();
|
return mVisibleEntries.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the adapter has entries which will be visible to the user
|
||||||
|
*/
|
||||||
|
public boolean hasVisibleEntries() {
|
||||||
|
// Account for the 1st space entry
|
||||||
|
return getItemCount() > 1;
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns all items that will be drawn in a recycler view. */
|
/** Returns all items that will be drawn in a recycler view. */
|
||||||
public List<WidgetsListBaseEntry> getItems() {
|
public List<WidgetsListBaseEntry> getItems() {
|
||||||
return mVisibleEntries;
|
return mVisibleEntries;
|
||||||
@@ -218,8 +234,9 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
|||||||
/** Updates the widget list based on {@code tempEntries}. */
|
/** Updates the widget list based on {@code tempEntries}. */
|
||||||
public void setWidgets(List<WidgetsListBaseEntry> tempEntries) {
|
public void setWidgets(List<WidgetsListBaseEntry> tempEntries) {
|
||||||
mCachingPreviewLoader.clearAll();
|
mCachingPreviewLoader.clearAll();
|
||||||
mAllEntries = tempEntries.stream().sorted(mRowComparator)
|
mAllEntries.clear();
|
||||||
.collect(Collectors.toList());
|
mAllEntries.add(new WidgetListSpaceEntry());
|
||||||
|
tempEntries.stream().sorted(mRowComparator).forEach(mAllEntries::add);
|
||||||
if (shouldClearVisibleEntries()) {
|
if (shouldClearVisibleEntries()) {
|
||||||
mVisibleEntries.clear();
|
mVisibleEntries.clear();
|
||||||
}
|
}
|
||||||
@@ -247,8 +264,9 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
|||||||
getOffsetForPosition(previousPositionForPackageUserKey);
|
getOffsetForPosition(previousPositionForPackageUserKey);
|
||||||
|
|
||||||
List<WidgetsListBaseEntry> newVisibleEntries = mAllEntries.stream()
|
List<WidgetsListBaseEntry> newVisibleEntries = mAllEntries.stream()
|
||||||
.filter(entry -> (mFilter == null || mFilter.test(entry))
|
.filter(entry -> ((mFilter == null || mFilter.test(entry))
|
||||||
&& mHeaderAndSelectedContentFilter.test(entry))
|
&& mHeaderAndSelectedContentFilter.test(entry))
|
||||||
|
|| entry instanceof WidgetListSpaceEntry)
|
||||||
.map(entry -> {
|
.map(entry -> {
|
||||||
if (entry instanceof WidgetsListBaseEntry.Header<?>
|
if (entry instanceof WidgetsListBaseEntry.Header<?>
|
||||||
&& matchesKey(entry, mWidgetsContentVisiblePackageUserKey)) {
|
&& matchesKey(entry, mWidgetsContentVisiblePackageUserKey)) {
|
||||||
@@ -352,7 +370,13 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
|||||||
public void onBindViewHolder(ViewHolder holder, int pos) {
|
public void onBindViewHolder(ViewHolder holder, int pos) {
|
||||||
ViewHolderBinder viewHolderBinder = mViewHolderBinders.get(getItemViewType(pos));
|
ViewHolderBinder viewHolderBinder = mViewHolderBinders.get(getItemViewType(pos));
|
||||||
WidgetsListBaseEntry entry = mVisibleEntries.get(pos);
|
WidgetsListBaseEntry entry = mVisibleEntries.get(pos);
|
||||||
viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos), pos);
|
|
||||||
|
// The first entry has an empty space, count from second entries.
|
||||||
|
int listPos = (pos > 1) ? POSITION_DEFAULT : POSITION_FIRST;
|
||||||
|
if (pos == (getItemCount() - 1)) {
|
||||||
|
listPos |= POSITION_LAST;
|
||||||
|
}
|
||||||
|
viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos), listPos);
|
||||||
holder.itemView.setTag(R.id.tag_widget_entry, entry);
|
holder.itemView.setTag(R.id.tag_widget_entry, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,6 +419,8 @@ public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderC
|
|||||||
return VIEW_TYPE_WIDGETS_HEADER;
|
return VIEW_TYPE_WIDGETS_HEADER;
|
||||||
} else if (entry instanceof WidgetsListSearchHeaderEntry) {
|
} else if (entry instanceof WidgetsListSearchHeaderEntry) {
|
||||||
return VIEW_TYPE_WIDGETS_SEARCH_HEADER;
|
return VIEW_TYPE_WIDGETS_SEARCH_HEADER;
|
||||||
|
} else if (entry instanceof WidgetListSpaceEntry) {
|
||||||
|
return VIEW_TYPE_WIDGETS_SPACE;
|
||||||
}
|
}
|
||||||
throw new UnsupportedOperationException("ViewHolderBinder not found for " + entry);
|
throw new UnsupportedOperationException("ViewHolderBinder not found for " + entry);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,16 +31,13 @@ public final class WidgetsListHeaderViewHolderBinder implements
|
|||||||
private final LayoutInflater mLayoutInflater;
|
private final LayoutInflater mLayoutInflater;
|
||||||
private final OnHeaderClickListener mOnHeaderClickListener;
|
private final OnHeaderClickListener mOnHeaderClickListener;
|
||||||
private final WidgetsListDrawableFactory mListDrawableFactory;
|
private final WidgetsListDrawableFactory mListDrawableFactory;
|
||||||
private final WidgetsListAdapter mWidgetsListAdapter;
|
|
||||||
|
|
||||||
public WidgetsListHeaderViewHolderBinder(LayoutInflater layoutInflater,
|
public WidgetsListHeaderViewHolderBinder(LayoutInflater layoutInflater,
|
||||||
OnHeaderClickListener onHeaderClickListener,
|
OnHeaderClickListener onHeaderClickListener,
|
||||||
WidgetsListDrawableFactory listDrawableFactory,
|
WidgetsListDrawableFactory listDrawableFactory) {
|
||||||
WidgetsListAdapter listAdapter) {
|
|
||||||
mLayoutInflater = layoutInflater;
|
mLayoutInflater = layoutInflater;
|
||||||
mOnHeaderClickListener = onHeaderClickListener;
|
mOnHeaderClickListener = onHeaderClickListener;
|
||||||
mListDrawableFactory = listDrawableFactory;
|
mListDrawableFactory = listDrawableFactory;
|
||||||
mWidgetsListAdapter = listAdapter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -53,14 +50,14 @@ public final class WidgetsListHeaderViewHolderBinder implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindViewHolder(WidgetsListHeaderHolder viewHolder, WidgetsListHeaderEntry data,
|
public void bindViewHolder(WidgetsListHeaderHolder viewHolder, WidgetsListHeaderEntry data,
|
||||||
int position) {
|
@ListPosition int position) {
|
||||||
WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
|
WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
|
||||||
widgetsListHeader.applyFromItemInfoWithIcon(data);
|
widgetsListHeader.applyFromItemInfoWithIcon(data);
|
||||||
widgetsListHeader.setExpanded(data.isWidgetListShown());
|
widgetsListHeader.setExpanded(data.isWidgetListShown());
|
||||||
widgetsListHeader.setListDrawableState(
|
widgetsListHeader.setListDrawableState(
|
||||||
WidgetsListDrawableState.obtain(
|
WidgetsListDrawableState.obtain(
|
||||||
/* isFirst= */ position == 0,
|
(position & POSITION_FIRST) != 0,
|
||||||
/* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
|
(position & POSITION_LAST) != 0,
|
||||||
/* isExpanded= */ data.isWidgetListShown()));
|
/* isExpanded= */ data.isWidgetListShown()));
|
||||||
widgetsListHeader.setOnExpandChangeListener(isExpanded ->
|
widgetsListHeader.setOnExpandChangeListener(isExpanded ->
|
||||||
mOnHeaderClickListener.onHeaderClicked(
|
mOnHeaderClickListener.onHeaderClicked(
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2021 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.widget.picker;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import com.android.launcher3.widget.picker.SearchAndRecommendationsScrollController.OnContentChangeListener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A layout manager for the {@link WidgetsRecyclerView}.
|
|
||||||
*
|
|
||||||
* {@link #setOnContentChangeListener(OnContentChangeListener)} can be used to register a callback
|
|
||||||
* for when the content of the layout manager has changed, following measurement and animation.
|
|
||||||
*/
|
|
||||||
public final class WidgetsListLayoutManager extends LinearLayoutManager {
|
|
||||||
@Nullable
|
|
||||||
private OnContentChangeListener mOnContentChangeListener;
|
|
||||||
|
|
||||||
public WidgetsListLayoutManager(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLayoutCompleted(RecyclerView.State state) {
|
|
||||||
super.onLayoutCompleted(state);
|
|
||||||
if (mOnContentChangeListener != null) {
|
|
||||||
mOnContentChangeListener.onContentChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnContentChangeListener(@Nullable OnContentChangeListener listener) {
|
|
||||||
mOnContentChangeListener = listener;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+4
-7
@@ -32,16 +32,13 @@ public final class WidgetsListSearchHeaderViewHolderBinder implements
|
|||||||
private final LayoutInflater mLayoutInflater;
|
private final LayoutInflater mLayoutInflater;
|
||||||
private final OnHeaderClickListener mOnHeaderClickListener;
|
private final OnHeaderClickListener mOnHeaderClickListener;
|
||||||
private final WidgetsListDrawableFactory mListDrawableFactory;
|
private final WidgetsListDrawableFactory mListDrawableFactory;
|
||||||
private final WidgetsListAdapter mWidgetsListAdapter;
|
|
||||||
|
|
||||||
public WidgetsListSearchHeaderViewHolderBinder(LayoutInflater layoutInflater,
|
public WidgetsListSearchHeaderViewHolderBinder(LayoutInflater layoutInflater,
|
||||||
OnHeaderClickListener onHeaderClickListener,
|
OnHeaderClickListener onHeaderClickListener,
|
||||||
WidgetsListDrawableFactory listDrawableFactory,
|
WidgetsListDrawableFactory listDrawableFactory) {
|
||||||
WidgetsListAdapter listAdapter) {
|
|
||||||
mLayoutInflater = layoutInflater;
|
mLayoutInflater = layoutInflater;
|
||||||
mOnHeaderClickListener = onHeaderClickListener;
|
mOnHeaderClickListener = onHeaderClickListener;
|
||||||
mListDrawableFactory = listDrawableFactory;
|
mListDrawableFactory = listDrawableFactory;
|
||||||
mWidgetsListAdapter = listAdapter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -54,14 +51,14 @@ public final class WidgetsListSearchHeaderViewHolderBinder implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindViewHolder(WidgetsListSearchHeaderHolder viewHolder,
|
public void bindViewHolder(WidgetsListSearchHeaderHolder viewHolder,
|
||||||
WidgetsListSearchHeaderEntry data, int position) {
|
WidgetsListSearchHeaderEntry data, @ListPosition int position) {
|
||||||
WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
|
WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
|
||||||
widgetsListHeader.applyFromItemInfoWithIcon(data);
|
widgetsListHeader.applyFromItemInfoWithIcon(data);
|
||||||
widgetsListHeader.setExpanded(data.isWidgetListShown());
|
widgetsListHeader.setExpanded(data.isWidgetListShown());
|
||||||
widgetsListHeader.setListDrawableState(
|
widgetsListHeader.setListDrawableState(
|
||||||
WidgetsListDrawableState.obtain(
|
WidgetsListDrawableState.obtain(
|
||||||
/* isFirst= */ position == 0,
|
(position & POSITION_FIRST) != 0,
|
||||||
/* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1,
|
(position & POSITION_LAST) != 0,
|
||||||
/* isExpanded= */ data.isWidgetListShown()));
|
/* isExpanded= */ data.isWidgetListShown()));
|
||||||
widgetsListHeader.setOnExpandChangeListener(isExpanded ->
|
widgetsListHeader.setOnExpandChangeListener(isExpanded ->
|
||||||
mOnHeaderClickListener.onHeaderClicked(isExpanded,
|
mOnHeaderClickListener.onHeaderClicked(isExpanded,
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ public final class WidgetsListTableViewHolderBinder
|
|||||||
private final OnLongClickListener mIconLongClickListener;
|
private final OnLongClickListener mIconLongClickListener;
|
||||||
private final WidgetsListDrawableFactory mListDrawableFactory;
|
private final WidgetsListDrawableFactory mListDrawableFactory;
|
||||||
private final CachingWidgetPreviewLoader mWidgetPreviewLoader;
|
private final CachingWidgetPreviewLoader mWidgetPreviewLoader;
|
||||||
private final WidgetsListAdapter mWidgetsListAdapter;
|
|
||||||
private boolean mApplyBitmapDeferred = false;
|
private boolean mApplyBitmapDeferred = false;
|
||||||
|
|
||||||
public WidgetsListTableViewHolderBinder(
|
public WidgetsListTableViewHolderBinder(
|
||||||
@@ -62,14 +61,12 @@ public final class WidgetsListTableViewHolderBinder
|
|||||||
OnClickListener iconClickListener,
|
OnClickListener iconClickListener,
|
||||||
OnLongClickListener iconLongClickListener,
|
OnLongClickListener iconLongClickListener,
|
||||||
CachingWidgetPreviewLoader widgetPreviewLoader,
|
CachingWidgetPreviewLoader widgetPreviewLoader,
|
||||||
WidgetsListDrawableFactory listDrawableFactory,
|
WidgetsListDrawableFactory listDrawableFactory) {
|
||||||
WidgetsListAdapter listAdapter) {
|
|
||||||
mLayoutInflater = layoutInflater;
|
mLayoutInflater = layoutInflater;
|
||||||
mIconClickListener = iconClickListener;
|
mIconClickListener = iconClickListener;
|
||||||
mIconLongClickListener = iconLongClickListener;
|
mIconLongClickListener = iconLongClickListener;
|
||||||
mWidgetPreviewLoader = widgetPreviewLoader;
|
mWidgetPreviewLoader = widgetPreviewLoader;
|
||||||
mListDrawableFactory = listDrawableFactory;
|
mListDrawableFactory = listDrawableFactory;
|
||||||
mWidgetsListAdapter = listAdapter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -97,15 +94,13 @@ public final class WidgetsListTableViewHolderBinder
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindViewHolder(WidgetsRowViewHolder holder, WidgetsListContentEntry entry,
|
public void bindViewHolder(WidgetsRowViewHolder holder, WidgetsListContentEntry entry,
|
||||||
int position) {
|
@ListPosition int position) {
|
||||||
WidgetsListTableView table = holder.mTableContainer;
|
WidgetsListTableView table = holder.mTableContainer;
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, String.format("onBindViewHolder [widget#=%d, table.getChildCount=%d]",
|
Log.d(TAG, String.format("onBindViewHolder [widget#=%d, table.getChildCount=%d]",
|
||||||
entry.mWidgets.size(), table.getChildCount()));
|
entry.mWidgets.size(), table.getChildCount()));
|
||||||
}
|
}
|
||||||
|
table.setListDrawableState(((position & POSITION_LAST) != 0) ? LAST : MIDDLE);
|
||||||
table.setListDrawableState(
|
|
||||||
position == mWidgetsListAdapter.getItemCount() - 1 ? LAST : MIDDLE);
|
|
||||||
|
|
||||||
List<ArrayList<WidgetItem>> widgetItemsTable =
|
List<ArrayList<WidgetItem>> widgetItemsTable =
|
||||||
WidgetsTableUtils.groupWidgetItemsIntoTable(
|
WidgetsTableUtils.groupWidgetItemsIntoTable(
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ public final class WidgetsRecommendationTableLayout extends TableLayout {
|
|||||||
private float mRecommendationTableMaxHeight = Float.MAX_VALUE;
|
private float mRecommendationTableMaxHeight = Float.MAX_VALUE;
|
||||||
@Nullable private OnLongClickListener mWidgetCellOnLongClickListener;
|
@Nullable private OnLongClickListener mWidgetCellOnLongClickListener;
|
||||||
@Nullable private OnClickListener mWidgetCellOnClickListener;
|
@Nullable private OnClickListener mWidgetCellOnClickListener;
|
||||||
@Nullable private OnTouchListener mWidgetCellOnTouchListener;
|
|
||||||
|
|
||||||
public WidgetsRecommendationTableLayout(Context context) {
|
public WidgetsRecommendationTableLayout(Context context) {
|
||||||
this(context, /* attrs= */ null);
|
this(context, /* attrs= */ null);
|
||||||
@@ -79,11 +78,6 @@ public final class WidgetsRecommendationTableLayout extends TableLayout {
|
|||||||
mWidgetCellOnClickListener = widgetCellOnClickListener;
|
mWidgetCellOnClickListener = widgetCellOnClickListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets a {@link android.view.View.OnTouchListener} for all widget cells in this table. */
|
|
||||||
public void setWidgetCellOnTouchListener(OnTouchListener widgetCellOnTouchListener) {
|
|
||||||
mWidgetCellOnTouchListener = widgetCellOnTouchListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a list of recommended widgets that would like to be displayed in this table within the
|
* Sets a list of recommended widgets that would like to be displayed in this table within the
|
||||||
* desired {@code recommendationTableMaxHeight}.
|
* desired {@code recommendationTableMaxHeight}.
|
||||||
@@ -129,7 +123,6 @@ public final class WidgetsRecommendationTableLayout extends TableLayout {
|
|||||||
WidgetCell widget = (WidgetCell) LayoutInflater.from(
|
WidgetCell widget = (WidgetCell) LayoutInflater.from(
|
||||||
getContext()).inflate(R.layout.widget_cell, parent, false);
|
getContext()).inflate(R.layout.widget_cell, parent, false);
|
||||||
|
|
||||||
widget.setOnTouchListener(mWidgetCellOnTouchListener);
|
|
||||||
View previewContainer = widget.findViewById(R.id.widget_preview_container);
|
View previewContainer = widget.findViewById(R.id.widget_preview_container);
|
||||||
previewContainer.setOnClickListener(mWidgetCellOnClickListener);
|
previewContainer.setOnClickListener(mWidgetCellOnClickListener);
|
||||||
previewContainer.setOnLongClickListener(mWidgetCellOnLongClickListener);
|
previewContainer.setOnLongClickListener(mWidgetCellOnLongClickListener);
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import android.view.MotionEvent;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TableLayout;
|
import android.widget.TableLayout;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
|
import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
|
||||||
@@ -32,11 +31,12 @@ import com.android.launcher3.BaseRecyclerView;
|
|||||||
import com.android.launcher3.DeviceProfile;
|
import com.android.launcher3.DeviceProfile;
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.views.ActivityContext;
|
import com.android.launcher3.views.ActivityContext;
|
||||||
|
import com.android.launcher3.widget.model.WidgetListSpaceEntry;
|
||||||
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
|
||||||
import com.android.launcher3.widget.model.WidgetsListContentEntry;
|
import com.android.launcher3.widget.model.WidgetsListContentEntry;
|
||||||
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
|
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
|
||||||
import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
|
import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
|
||||||
import com.android.launcher3.widget.picker.SearchAndRecommendationsScrollController.OnContentChangeListener;
|
import com.android.launcher3.widget.picker.WidgetsSpaceViewHolderBinder.EmptySpaceView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The widgets recycler view.
|
* The widgets recycler view.
|
||||||
@@ -50,10 +50,13 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
|
|||||||
private final Point mFastScrollerOffset = new Point();
|
private final Point mFastScrollerOffset = new Point();
|
||||||
private boolean mTouchDownOnScroller;
|
private boolean mTouchDownOnScroller;
|
||||||
private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider;
|
private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider;
|
||||||
|
|
||||||
|
// Cached sizes
|
||||||
private int mLastVisibleWidgetContentTableHeight = 0;
|
private int mLastVisibleWidgetContentTableHeight = 0;
|
||||||
private int mWidgetHeaderHeight = 0;
|
private int mWidgetHeaderHeight = 0;
|
||||||
|
private int mWidgetEmptySpaceHeight = 0;
|
||||||
|
|
||||||
private final int mSpacingBetweenEntries;
|
private final int mSpacingBetweenEntries;
|
||||||
@Nullable private OnContentChangeListener mOnContentChangeListener;
|
|
||||||
|
|
||||||
public WidgetsRecyclerView(Context context) {
|
public WidgetsRecyclerView(Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
@@ -82,9 +85,7 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
|
|||||||
super.onFinishInflate();
|
super.onFinishInflate();
|
||||||
// create a layout manager with Launcher's context so that scroll position
|
// create a layout manager with Launcher's context so that scroll position
|
||||||
// can be preserved during screen rotation.
|
// can be preserved during screen rotation.
|
||||||
WidgetsListLayoutManager layoutManager = new WidgetsListLayoutManager(getContext());
|
setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
layoutManager.setOnContentChangeListener(mOnContentChangeListener);
|
|
||||||
setLayoutManager(layoutManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -169,10 +170,12 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
|
|||||||
// This assumes there is ever only one content shown in this recycler view.
|
// This assumes there is ever only one content shown in this recycler view.
|
||||||
mLastVisibleWidgetContentTableHeight = view.getMeasuredHeight();
|
mLastVisibleWidgetContentTableHeight = view.getMeasuredHeight();
|
||||||
} else if (view instanceof WidgetsListHeader
|
} else if (view instanceof WidgetsListHeader
|
||||||
&& mLastVisibleWidgetContentTableHeight == 0
|
&& mWidgetHeaderHeight == 0
|
||||||
&& view.getMeasuredHeight() > 0) {
|
&& view.getMeasuredHeight() > 0) {
|
||||||
// This assumes all header views are of the same height.
|
// This assumes all header views are of the same height.
|
||||||
mWidgetHeaderHeight = view.getMeasuredHeight();
|
mWidgetHeaderHeight = view.getMeasuredHeight();
|
||||||
|
} else if (view instanceof EmptySpaceView && view.getMeasuredHeight() > 0) {
|
||||||
|
mWidgetEmptySpaceHeight = view.getMeasuredHeight();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,14 +254,6 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
|
|||||||
scrollToPosition(0);
|
scrollToPosition(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOnContentChangeListener(@Nullable OnContentChangeListener listener) {
|
|
||||||
mOnContentChangeListener = listener;
|
|
||||||
WidgetsListLayoutManager layoutManager = (WidgetsListLayoutManager) getLayoutManager();
|
|
||||||
if (layoutManager != null) {
|
|
||||||
layoutManager.setOnContentChangeListener(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the sum of the height, in pixels, of this list adapter's items from index 0 until
|
* Returns the sum of the height, in pixels, of this list adapter's items from index 0 until
|
||||||
* {@code untilIndex}.
|
* {@code untilIndex}.
|
||||||
@@ -283,6 +278,8 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
|
|||||||
}
|
}
|
||||||
} else if (entry instanceof WidgetsListContentEntry) {
|
} else if (entry instanceof WidgetsListContentEntry) {
|
||||||
totalItemsHeight += mLastVisibleWidgetContentTableHeight;
|
totalItemsHeight += mLastVisibleWidgetContentTableHeight;
|
||||||
|
} else if (entry instanceof WidgetListSpaceEntry) {
|
||||||
|
totalItemsHeight += mWidgetEmptySpaceHeight;
|
||||||
} else {
|
} else {
|
||||||
throw new UnsupportedOperationException("Can't estimate height for " + entry);
|
throw new UnsupportedOperationException("Can't estimate height for " + entry);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.widget.picker;
|
||||||
|
|
||||||
|
import static android.view.View.MeasureSpec.EXACTLY;
|
||||||
|
import static android.view.View.MeasureSpec.makeMeasureSpec;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||||
|
|
||||||
|
import com.android.launcher3.recyclerview.ViewHolderBinder;
|
||||||
|
import com.android.launcher3.widget.model.WidgetListSpaceEntry;
|
||||||
|
|
||||||
|
import java.util.function.IntSupplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ViewHolderBinder} for binding the top empty space
|
||||||
|
*/
|
||||||
|
public class WidgetsSpaceViewHolderBinder
|
||||||
|
implements ViewHolderBinder<WidgetListSpaceEntry, ViewHolder> {
|
||||||
|
|
||||||
|
private final IntSupplier mEmptySpaceHeightProvider;
|
||||||
|
|
||||||
|
public WidgetsSpaceViewHolderBinder(IntSupplier emptySpaceHeightProvider) {
|
||||||
|
mEmptySpaceHeightProvider = emptySpaceHeightProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ViewHolder newViewHolder(ViewGroup parent) {
|
||||||
|
return new ViewHolder(new EmptySpaceView(parent.getContext())) { };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindViewHolder(ViewHolder holder, WidgetListSpaceEntry data, int position) {
|
||||||
|
((EmptySpaceView) holder.itemView).setFixedHeight(mEmptySpaceHeightProvider.getAsInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty view which allows listening for 'Y' changes
|
||||||
|
*/
|
||||||
|
public static class EmptySpaceView extends View {
|
||||||
|
|
||||||
|
private Runnable mOnYChangeCallback;
|
||||||
|
private int mHeight = 0;
|
||||||
|
|
||||||
|
private EmptySpaceView(Context context) {
|
||||||
|
super(context);
|
||||||
|
animate().setUpdateListener(v -> notifyYChanged());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the height for the empty view
|
||||||
|
* @return true if the height changed, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean setFixedHeight(int height) {
|
||||||
|
if (mHeight != height) {
|
||||||
|
mHeight = height;
|
||||||
|
requestLayout();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
super.onMeasure(widthMeasureSpec, makeMeasureSpec(mHeight, EXACTLY));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnYChangeCallback(Runnable callback) {
|
||||||
|
mOnYChangeCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||||
|
super.onLayout(changed, left, top, right, bottom);
|
||||||
|
notifyYChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void offsetTopAndBottom(int offset) {
|
||||||
|
super.offsetTopAndBottom(offset);
|
||||||
|
notifyYChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTranslationY(float translationY) {
|
||||||
|
super.setTranslationY(translationY);
|
||||||
|
notifyYChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyYChanged() {
|
||||||
|
if (mOnYChangeCallback != null) {
|
||||||
|
mOnYChangeCallback.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user