Snap for 8283941 from fec75c5a0e to tm-release

Change-Id: Ib40be74466fcfa0737331eacb8a314b78ea972d4
This commit is contained in:
Android Build Coastguard Worker
2022-03-10 02:08:33 +00:00
86 changed files with 2267 additions and 1282 deletions
@@ -14,7 +14,8 @@
limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<corners android:radius="28dp"/>
<solid android:color="?android:attr/colorBackgroundFloating"/>
<solid android:color="?androidprv:attr/colorSurface"/>
</shape>
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2022 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.
-->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingTop="26dp"
android:paddingBottom="26dp"
android:paddingStart="56dp"
android:paddingEnd="56dp">
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_icon_1"
android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
app:cardBackgroundColor="@color/mock_app_icon_1"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintVertical_chainStyle="spread_inside"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/hotseat_icon_2"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_icon_2"
android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
app:cardBackgroundColor="@color/mock_app_icon_2"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toBottomOf="@id/hotseat_icon_1"
app:layout_constraintBottom_toTopOf="@id/hotseat_icon_3"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_icon_3"
android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
app:cardBackgroundColor="@color/mock_app_icon_3"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toBottomOf="@id/hotseat_icon_2"
app:layout_constraintBottom_toTopOf="@id/hotseat_icon_4"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_icon_4"
android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
app:cardBackgroundColor="@color/mock_app_icon_4"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toBottomOf="@id/hotseat_icon_3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2022 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.
-->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="32dp"
android:paddingStart="@dimen/gesture_tutorial_hotseat_padding_start_end"
android:paddingEnd="@dimen/gesture_tutorial_hotseat_padding_start_end">
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_search_bar"
android:layout_width="200dp"
android:layout_height="@dimen/gesture_tutorial_hotseat_search_height"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_search_corner_radius"
app:cardBackgroundColor="@color/mock_search_bar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/hotseat_icon_1"/>
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_icon_1"
android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
app:cardBackgroundColor="@color/mock_app_icon_1"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/hotseat_search_bar"
app:layout_constraintEnd_toStartOf="@id/hotseat_icon_2"/>
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_icon_2"
android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
app:cardBackgroundColor="@color/mock_app_icon_2"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/hotseat_icon_1"
app:layout_constraintEnd_toStartOf="@id/hotseat_icon_3"/>
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_icon_3"
android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
app:cardBackgroundColor="@color/mock_app_icon_3"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/hotseat_icon_2"
app:layout_constraintEnd_toStartOf="@id/hotseat_icon_4"/>
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_icon_4"
android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
app:cardBackgroundColor="@color/mock_app_icon_1"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/hotseat_icon_3"
app:layout_constraintEnd_toStartOf="@id/hotseat_icon_5"/>
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_icon_5"
android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
app:cardBackgroundColor="@color/mock_app_icon_4"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/hotseat_icon_4"
app:layout_constraintEnd_toStartOf="@id/hotseat_icon_6"/>
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_icon_6"
android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
app:cardBackgroundColor="@color/mock_app_icon_2"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/hotseat_icon_5"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
+3 -4
View File
@@ -60,7 +60,7 @@
<TextView
android:id="@+id/title"
style="@style/TextAppearance.GestureTutorial.Feedback.Title"
style="@style/TextAppearance.GestureTutorial.Feedback.Title.AllSet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/allset_title_margin_top"
@@ -71,15 +71,14 @@
<TextView
android:id="@+id/subtitle"
style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle.AllSet"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/allset_subtitle_margin_top"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintWidth_max="@dimen/allset_subtitle_width_max"
android:gravity="start"
android:text="@string/allset_description"/>
android:gravity="start"/>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/navigation_settings_guideline_bottom"
@@ -1,12 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2022 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.
-->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="32dp"
android:paddingStart="170dp"
android:paddingEnd="170dp">
android:paddingStart="@dimen/gesture_tutorial_hotseat_padding_start_end"
android:paddingEnd="@dimen/gesture_tutorial_hotseat_padding_start_end">
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_search_bar"
@@ -27,10 +27,8 @@
<FrameLayout
android:id="@+id/gesture_tutorial_fake_hotseat_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"/>
android:layout_width="@dimen/gesture_tutorial_hotseat_width"
android:layout_height="@dimen/gesture_tutorial_hotseat_height"/>
</RelativeLayout>
@@ -102,7 +100,7 @@
android:background="@drawable/gesture_tutorial_ripple"/>
<include
layout="@layout/gesture_tutorial_foldable_mock_taskbar"
layout="@layout/gesture_tutorial_tablet_mock_taskbar"
android:id="@+id/gesture_tutorial_fake_taskbar_view"
android:layout_width="match_parent"
android:layout_height="@dimen/gesture_tutorial_mock_taskbar_height"
@@ -121,33 +119,23 @@
android:scaleType="fitXY"
android:visibility="gone"/>
<ImageView
android:id="@+id/gesture_tutorial_finger_dot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/gesture_tutorial_finger_dot"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:visibility="gone"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/gesture_tutorial_fragment_feedback_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="24dp"
android:paddingTop="24dp"
android:paddingBottom="16dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:background="@drawable/bg_sandbox_feedback">
<TextView
android:id="@+id/gesture_tutorial_fragment_feedback_title"
style="@style/TextAppearance.GestureTutorial.Feedback.Title"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
@@ -158,9 +146,7 @@
style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
@@ -182,7 +168,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:paddingStart="26dp"
@@ -201,11 +186,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:paddingStart="26dp"
android:paddingEnd="26dp"
android:text="@string/gesture_tutorial_action_button_label_skip"
android:background="?android:attr/selectableItemBackgroundBorderless"
@@ -214,4 +196,13 @@
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
android:id="@+id/gesture_tutorial_finger_dot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/gesture_tutorial_finger_dot"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:visibility="gone"/>
</com.android.quickstep.interaction.RootSandboxLayout>
@@ -34,8 +34,8 @@
android:layout_height="0dp"
android:layout_marginTop="43dp"
android:layout_marginBottom="22dp"
android:layout_marginStart="34dp"
android:layout_marginEnd="211dp"
android:layout_marginStart="@dimen/gesture_tutorial_top_bar_margin_start"
android:layout_marginEnd="@dimen/gesture_tutorial_top_bar_margin_end"
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
@@ -84,7 +84,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/mock_conversation_background"
android:paddingBottom="66dp"
android:paddingBottom="@dimen/gesture_tutorial_conversation_bottom_padding"
app:layout_constraintTop_toBottomOf="@id/top_bar"
app:layout_constraintBottom_toBottomOf="parent"
@@ -108,6 +108,7 @@
android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
android:layout_marginStart="124dp"
android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end"
android:visibility="@integer/gesture_tutorial_extra_messages_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="18dp"
@@ -122,6 +123,7 @@
android:layout_height="@dimen/gesture_tutorial_message_icon_size"
android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
android:layout_marginStart="@dimen/gesture_tutorial_message_padding_start"
android:visibility="@integer/gesture_tutorial_extra_messages_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_message_icon_corner_radius"
@@ -135,6 +137,7 @@
android:layout_height="36dp"
android:layout_marginStart="17dp"
android:layout_marginEnd="112dp"
android:visibility="@integer/gesture_tutorial_extra_messages_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="18dp"
@@ -151,6 +154,7 @@
android:layout_marginBottom="@dimen/gesture_tutorial_message_small_margin_bottom"
android:layout_marginStart="280dp"
android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end"
android:visibility="@integer/gesture_tutorial_extra_messages_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="18dp"
@@ -164,7 +168,7 @@
android:layout_width="0dp"
android:layout_height="74dp"
android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
android:layout_marginStart="124dp"
android:layout_marginStart="@dimen/gesture_tutorial_message_margin_start"
android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end"
app:cardElevation="0dp"
@@ -192,7 +196,7 @@
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_marginStart="17dp"
android:layout_marginEnd="144dp"
android:layout_marginEnd="@dimen/gesture_tutorial_reply_margin_end"
app:cardElevation="0dp"
app:cardCornerRadius="18dp"
@@ -206,7 +210,7 @@
android:id="@+id/message_4"
android:layout_width="0dp"
android:layout_height="74dp"
android:layout_marginStart="124dp"
android:layout_marginStart="@dimen/gesture_tutorial_message_margin_start"
android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end"
app:cardElevation="0dp"
@@ -35,7 +35,7 @@
android:layout_marginTop="43dp"
android:layout_marginBottom="22dp"
android:layout_marginStart="34dp"
android:layout_marginEnd="35dp"
android:layout_marginEnd="34dp"
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
@@ -51,337 +51,336 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/mock_list_background"
android:paddingBottom="66dp"
android:paddingTop="@dimen/gesture_tutorial_conversation_list_padding_top"
android:paddingStart="26dp"
app:layout_constraintTop_toBottomOf="@id/top_bar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:paddingTop="@dimen/gesture_tutorial_conversation_list_padding_top"
android:paddingStart="26dp"
android:paddingBottom="14dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_icon_1"
android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
app:cardBackgroundColor="@color/mock_list_profile_icon"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/mock_button">
app:layout_constraintStart_toStartOf="parent"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_icon_1"
android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_1"
android:layout_width="0dp"
android:layout_height="18dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="@dimen/gesture_tutorial_conversation_line_1_margin_end"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
app:cardBackgroundColor="@color/mock_list_profile_icon"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toTopOf="@id/conversation_icon_1"
app:layout_constraintStart_toEndOf="@id/conversation_icon_1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/conversation_line_2"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_1"
android:layout_width="0dp"
android:layout_height="18dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="217dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_2"
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="@dimen/gesture_tutorial_conversation_line_2_margin_end"
android:layout_marginTop="4dp"
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toTopOf="@id/conversation_icon_1"
app:layout_constraintStart_toEndOf="@id/conversation_icon_1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/conversation_line_2"/>
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintTop_toBottomOf="@id/conversation_line_1"
app:layout_constraintStart_toEndOf="@id/conversation_icon_1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/conversation_icon_1"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_2"
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="142dp"
android:layout_marginTop="4dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_icon_2"
android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_marginTop="32dp"
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintTop_toBottomOf="@id/conversation_line_1"
app:layout_constraintStart_toEndOf="@id/conversation_icon_1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/conversation_icon_1"/>
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
app:cardBackgroundColor="@color/mock_list_profile_icon"
app:layout_constraintTop_toBottomOf="@id/conversation_icon_1"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_icon_2"
android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_marginTop="32dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_3"
android:layout_width="0dp"
android:layout_height="18dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="@dimen/gesture_tutorial_conversation_line_3_margin_end"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
app:cardBackgroundColor="@color/mock_list_profile_icon"
app:layout_constraintTop_toBottomOf="@id/conversation_icon_1"
app:layout_constraintStart_toStartOf="parent"/>
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toTopOf="@id/conversation_icon_2"
app:layout_constraintStart_toEndOf="@id/conversation_icon_2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/conversation_line_4"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_3"
android:layout_width="0dp"
android:layout_height="18dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="190dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_4"
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="@dimen/gesture_tutorial_conversation_line_4_margin_end"
android:layout_marginTop="4dp"
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toTopOf="@id/conversation_icon_2"
app:layout_constraintStart_toEndOf="@id/conversation_icon_2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/conversation_line_4"/>
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintTop_toBottomOf="@id/conversation_line_3"
app:layout_constraintStart_toEndOf="@id/conversation_icon_2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/conversation_icon_2"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_4"
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="171dp"
android:layout_marginTop="4dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_icon_3"
android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_marginTop="32dp"
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintTop_toBottomOf="@id/conversation_line_3"
app:layout_constraintStart_toEndOf="@id/conversation_icon_2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/conversation_icon_2"/>
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
app:cardBackgroundColor="@color/mock_list_profile_icon"
app:layout_constraintTop_toBottomOf="@id/conversation_icon_2"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_icon_3"
android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_marginTop="32dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_5"
android:layout_width="0dp"
android:layout_height="18dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="@dimen/gesture_tutorial_conversation_line_5_margin_end"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
app:cardBackgroundColor="@color/mock_list_profile_icon"
app:layout_constraintTop_toBottomOf="@id/conversation_icon_2"
app:layout_constraintStart_toStartOf="parent"/>
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toTopOf="@id/conversation_icon_3"
app:layout_constraintStart_toEndOf="@id/conversation_icon_3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/conversation_line_6"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_5"
android:layout_width="0dp"
android:layout_height="18dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="198dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_6"
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="@dimen/gesture_tutorial_conversation_line_6_margin_end"
android:layout_marginTop="4dp"
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toTopOf="@id/conversation_icon_3"
app:layout_constraintStart_toEndOf="@id/conversation_icon_3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/conversation_line_6"/>
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintTop_toBottomOf="@id/conversation_line_5"
app:layout_constraintStart_toEndOf="@id/conversation_icon_3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/conversation_icon_3"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_6"
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="79dp"
android:layout_marginTop="4dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_icon_4"
android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_marginTop="32dp"
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintTop_toBottomOf="@id/conversation_line_5"
app:layout_constraintStart_toEndOf="@id/conversation_icon_3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/conversation_icon_3"/>
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
app:cardBackgroundColor="@color/mock_list_profile_icon"
app:layout_constraintTop_toBottomOf="@id/conversation_icon_3"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_icon_4"
android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_marginTop="32dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_7"
android:layout_width="0dp"
android:layout_height="18dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="@dimen/gesture_tutorial_conversation_line_7_margin_end"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
app:cardBackgroundColor="@color/mock_list_profile_icon"
app:layout_constraintTop_toBottomOf="@id/conversation_icon_3"
app:layout_constraintStart_toStartOf="parent"/>
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toTopOf="@id/conversation_icon_4"
app:layout_constraintStart_toEndOf="@id/conversation_icon_4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/conversation_line_8"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_7"
android:layout_width="0dp"
android:layout_height="18dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="174dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_8"
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="@dimen/gesture_tutorial_conversation_line_8_margin_end"
android:layout_marginTop="4dp"
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toTopOf="@id/conversation_icon_4"
app:layout_constraintStart_toEndOf="@id/conversation_icon_4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/conversation_line_8"/>
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintTop_toBottomOf="@id/conversation_line_7"
app:layout_constraintStart_toEndOf="@id/conversation_icon_4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/conversation_icon_4"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_8"
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="117dp"
android:layout_marginTop="4dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_icon_5"
android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_marginTop="32dp"
android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintTop_toBottomOf="@id/conversation_line_7"
app:layout_constraintStart_toEndOf="@id/conversation_icon_4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/conversation_icon_4"/>
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
app:cardBackgroundColor="@color/mock_list_profile_icon"
app:layout_constraintTop_toBottomOf="@id/conversation_icon_4"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_icon_5"
android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_marginTop="32dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_9"
android:layout_width="0dp"
android:layout_height="18dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="244dp"
android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
app:cardBackgroundColor="@color/mock_list_profile_icon"
app:layout_constraintTop_toBottomOf="@id/conversation_icon_4"
app:layout_constraintStart_toStartOf="parent"/>
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toTopOf="@id/conversation_icon_5"
app:layout_constraintStart_toEndOf="@id/conversation_icon_5"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/conversation_line_10"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_9"
android:layout_width="0dp"
android:layout_height="18dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="244dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_10"
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="143dp"
android:layout_marginTop="4dp"
android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toTopOf="@id/conversation_icon_5"
app:layout_constraintStart_toEndOf="@id/conversation_icon_5"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/conversation_line_10"/>
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintTop_toBottomOf="@id/conversation_line_9"
app:layout_constraintStart_toEndOf="@id/conversation_icon_5"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/conversation_icon_5"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_10"
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="143dp"
android:layout_marginTop="4dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_icon_6"
android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_marginTop="32dp"
android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintTop_toBottomOf="@id/conversation_line_9"
app:layout_constraintStart_toEndOf="@id/conversation_icon_5"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/conversation_icon_5"/>
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
app:cardBackgroundColor="@color/mock_list_profile_icon"
app:layout_constraintTop_toBottomOf="@id/conversation_icon_5"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_icon_6"
android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_marginTop="32dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_11"
android:layout_width="0dp"
android:layout_height="18dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="177dp"
android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
app:cardBackgroundColor="@color/mock_list_profile_icon"
app:layout_constraintTop_toBottomOf="@id/conversation_icon_5"
app:layout_constraintStart_toStartOf="parent"/>
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toTopOf="@id/conversation_icon_6"
app:layout_constraintStart_toEndOf="@id/conversation_icon_6"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/conversation_line_12"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_11"
android:layout_width="0dp"
android:layout_height="18dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="177dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_12"
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="117dp"
android:layout_marginTop="4dp"
android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toTopOf="@id/conversation_icon_6"
app:layout_constraintStart_toEndOf="@id/conversation_icon_6"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/conversation_line_12"/>
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintTop_toBottomOf="@id/conversation_line_11"
app:layout_constraintStart_toEndOf="@id/conversation_icon_6"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/conversation_icon_6"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_12"
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="117dp"
android:layout_marginTop="4dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_icon_7"
android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_marginTop="32dp"
android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintTop_toBottomOf="@id/conversation_line_11"
app:layout_constraintStart_toEndOf="@id/conversation_icon_6"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/conversation_icon_6"/>
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
app:cardBackgroundColor="@color/mock_list_profile_icon"
app:layout_constraintTop_toBottomOf="@id/conversation_icon_6"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_icon_7"
android:layout_width="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_height="@dimen/gesture_tutorial_conversation_icon_size"
android:layout_marginTop="32dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_13"
android:layout_width="0dp"
android:layout_height="18dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="189dp"
android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius"
app:cardBackgroundColor="@color/mock_list_profile_icon"
app:layout_constraintTop_toBottomOf="@id/conversation_icon_6"
app:layout_constraintStart_toStartOf="parent"/>
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toTopOf="@id/conversation_icon_7"
app:layout_constraintStart_toEndOf="@id/conversation_icon_7"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/conversation_line_14"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_13"
android:layout_width="0dp"
android:layout_height="18dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="189dp"
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_14"
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="166dp"
android:layout_marginTop="4dp"
android:visibility="@integer/gesture_tutorial_extra_conversations_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toTopOf="@id/conversation_icon_7"
app:layout_constraintStart_toEndOf="@id/conversation_icon_7"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/conversation_line_14"/>
<androidx.cardview.widget.CardView
android:id="@+id/conversation_line_14"
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="166dp"
android:layout_marginTop="4dp"
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintTop_toBottomOf="@id/conversation_line_13"
app:layout_constraintStart_toEndOf="@id/conversation_icon_7"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/conversation_icon_7"/>
</androidx.constraintlayout.widget.ConstraintLayout>
app:cardElevation="0dp"
app:cardCornerRadius="4dp"
app:cardBackgroundColor="@color/mock_list_preview_message"
app:layout_constraintTop_toBottomOf="@id/conversation_line_13"
app:layout_constraintStart_toEndOf="@id/conversation_icon_7"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/conversation_icon_7"/>
<androidx.cardview.widget.CardView
android:id="@+id/mock_button"
android:layout_width="149dp"
android:layout_height="56dp"
android:layout_marginEnd="24dp"
android:layout_marginEnd="@dimen/gesture_tutorial_mock_button_margin_end"
android:layout_marginBottom="@dimen/gesture_tutorial_mock_button_margin_bottom"
app:cardElevation="0dp"
app:cardCornerRadius="164dp"
@@ -34,8 +34,8 @@
android:layout_height="0dp"
android:layout_marginTop="48dp"
android:layout_marginBottom="16dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_webpage_url_margin_start_end"
android:layout_marginEnd="@dimen/gesture_tutorial_webpage_url_margin_start_end"
app:cardElevation="0dp"
app:cardCornerRadius="100dp"
@@ -63,7 +63,7 @@
android:layout_height="0dp"
android:layout_marginTop="22dp"
android:layout_marginBottom="22dp"
android:layout_marginStart="24dp"
android:layout_marginStart="@dimen/gesture_tutorial_webpage_top_bar_button_margin_start"
app:cardElevation="0dp"
app:cardCornerRadius="8dp"
@@ -78,8 +78,8 @@
android:layout_height="0dp"
android:layout_marginTop="28dp"
android:layout_marginBottom="28dp"
android:layout_marginStart="97dp"
android:layout_marginEnd="97dp"
android:layout_marginStart="@dimen/gesture_tutorial_webpage_top_bar_margin_start"
android:layout_marginEnd="@dimen/gesture_tutorial_webpage_top_bar_margin_end"
app:cardElevation="0dp"
app:cardCornerRadius="2dp"
@@ -107,7 +107,7 @@
android:id="@+id/mock_line_1"
android:layout_width="0dp"
android:layout_height="@dimen/gesture_tutorial_webpage_large_line_height"
android:layout_marginEnd="126dp"
android:layout_marginEnd="@dimen/gesture_tutorial_webpage_line_1_margin_end"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
@@ -121,7 +121,7 @@
android:layout_width="0dp"
android:layout_height="@dimen/gesture_tutorial_webpage_large_line_height"
android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
android:layout_marginEnd="64dp"
android:layout_marginEnd="@dimen/gesture_tutorial_webpage_line_2_margin_end"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
@@ -135,7 +135,7 @@
android:layout_width="0dp"
android:layout_height="@dimen/gesture_tutorial_webpage_large_line_height"
android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
android:layout_marginEnd="151dp"
android:layout_marginEnd="@dimen/gesture_tutorial_webpage_line_3_margin_end"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_webpage_small_corner_radius"
@@ -174,7 +174,7 @@
android:layout_width="0dp"
android:layout_height="240dp"
android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
android:layout_marginEnd="24dp"
android:layout_marginEnd="@dimen/gesture_tutorial_webpage_block_margin_end"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_webpage_large_corner_radius"
@@ -189,6 +189,7 @@
android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
android:layout_marginEnd="52dp"
android:visibility="@integer/gesture_tutorial_webpage_extra_lines_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
@@ -203,6 +204,7 @@
android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
android:layout_marginEnd="41dp"
android:visibility="@integer/gesture_tutorial_webpage_extra_lines_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
@@ -217,6 +219,7 @@
android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
android:layout_marginEnd="71dp"
android:visibility="@integer/gesture_tutorial_webpage_extra_lines_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
@@ -231,6 +234,7 @@
android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
android:layout_marginEnd="198dp"
android:visibility="@integer/gesture_tutorial_webpage_extra_lines_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
@@ -245,6 +249,7 @@
android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
android:layout_marginTop="@dimen/gesture_tutorial_webpage_large_margin_top"
android:layout_marginEnd="64dp"
android:visibility="@integer/gesture_tutorial_webpage_extra_lines_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
@@ -258,6 +263,7 @@
android:layout_height="@dimen/gesture_tutorial_webpage_small_line_height"
android:layout_marginTop="@dimen/gesture_tutorial_webpage_small_margin_top"
android:layout_marginEnd="71dp"
android:visibility="@integer/gesture_tutorial_webpage_extra_lines_visibility"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_webpage_medium_corner_radius"
@@ -106,8 +106,8 @@
android:layout_width="0dp"
android:layout_height="112dp"
android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
android:layout_marginStart="445dp"
android:layout_marginEnd="@dimen/gesture_tutorial_foldable_message_padding_start_end"
android:layout_marginStart="@dimen/gesture_tutorial_tablet_message_1_margin"
android:layout_marginEnd="@dimen/gesture_tutorial_tablet_message_padding_start_end"
app:cardElevation="0dp"
app:cardCornerRadius="18dp"
@@ -121,7 +121,7 @@
android:layout_width="@dimen/gesture_tutorial_message_icon_size"
android:layout_height="@dimen/gesture_tutorial_message_icon_size"
android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
android:layout_marginStart="@dimen/gesture_tutorial_foldable_message_padding_start_end"
android:layout_marginStart="@dimen/gesture_tutorial_tablet_message_padding_start_end"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_message_icon_corner_radius"
@@ -134,7 +134,7 @@
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_marginStart="17dp"
android:layout_marginEnd="441dp"
android:layout_marginEnd="@dimen/gesture_tutorial_tablet_reply_1_margin"
app:cardElevation="0dp"
app:cardCornerRadius="18dp"
@@ -149,8 +149,8 @@
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_marginBottom="@dimen/gesture_tutorial_message_small_margin_bottom"
android:layout_marginStart="601dp"
android:layout_marginEnd="@dimen/gesture_tutorial_foldable_message_padding_start_end"
android:layout_marginStart="@dimen/gesture_tutorial_tablet_message_2_margin"
android:layout_marginEnd="@dimen/gesture_tutorial_tablet_message_padding_start_end"
app:cardElevation="0dp"
app:cardCornerRadius="18dp"
@@ -164,8 +164,8 @@
android:layout_width="0dp"
android:layout_height="74dp"
android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom"
android:layout_marginStart="445dp"
android:layout_marginEnd="@dimen/gesture_tutorial_foldable_message_padding_start_end"
android:layout_marginStart="@dimen/gesture_tutorial_tablet_message_3_margin"
android:layout_marginEnd="@dimen/gesture_tutorial_tablet_message_padding_start_end"
app:cardElevation="0dp"
app:cardCornerRadius="18dp"
@@ -179,7 +179,7 @@
android:layout_width="@dimen/gesture_tutorial_message_icon_size"
android:layout_height="@dimen/gesture_tutorial_message_icon_size"
android:layout_marginBottom="32dp"
android:layout_marginStart="@dimen/gesture_tutorial_foldable_message_padding_start_end"
android:layout_marginStart="@dimen/gesture_tutorial_tablet_message_padding_start_end"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_message_icon_corner_radius"
@@ -192,7 +192,7 @@
android:layout_width="0dp"
android:layout_height="36dp"
android:layout_marginStart="17dp"
android:layout_marginEnd="473dp"
android:layout_marginEnd="@dimen/gesture_tutorial_tablet_reply_2_margin"
app:cardElevation="0dp"
app:cardCornerRadius="18dp"
@@ -206,8 +206,8 @@
android:id="@+id/message_4"
android:layout_width="0dp"
android:layout_height="74dp"
android:layout_marginStart="445dp"
android:layout_marginEnd="@dimen/gesture_tutorial_foldable_message_padding_start_end"
android:layout_marginStart="345dp"
android:layout_marginEnd="@dimen/gesture_tutorial_tablet_message_padding_start_end"
app:cardElevation="0dp"
app:cardCornerRadius="18dp"
@@ -67,7 +67,7 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/mock_button">
app:layout_constraintEnd_toEndOf="parent">
<androidx.cardview.widget.CardView
android:id="@+id/conversation_icon_1"
@@ -189,7 +189,7 @@
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="15dp"
android:layout_marginEnd="@dimen/gesture_tutorial_tablet_conversation_line_6_margin_end"
android:layout_marginTop="4dp"
app:cardElevation="0dp"
@@ -233,7 +233,7 @@
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="72dp"
android:layout_marginEnd="@dimen/gesture_tutorial_tablet_conversation_line_8_margin_end"
android:layout_marginTop="4dp"
app:cardElevation="0dp"
@@ -277,7 +277,7 @@
android:layout_width="0dp"
android:layout_height="16dp"
android:layout_marginStart="@dimen/gesture_tutorial_conversation_line_padding_start"
android:layout_marginEnd="111dp"
android:layout_marginEnd="@dimen/gesture_tutorial_tablet_conversation_line_10_margin_end"
android:layout_marginTop="4dp"
app:cardElevation="0dp"
@@ -376,21 +376,21 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/conversation_icon_7"/>
<androidx.cardview.widget.CardView
android:id="@+id/mock_button"
android:layout_width="149dp"
android:layout_height="56dp"
android:layout_marginEnd="126dp"
android:layout_marginBottom="24dp"
app:cardElevation="0dp"
app:cardCornerRadius="164dp"
app:cardBackgroundColor="@color/mock_list_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.cardview.widget.CardView
android:id="@+id/mock_button"
android:layout_width="149dp"
android:layout_height="56dp"
android:layout_marginEnd="126dp"
android:layout_marginBottom="24dp"
app:cardElevation="0dp"
app:cardCornerRadius="164dp"
app:cardBackgroundColor="@color/mock_list_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2022 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.
-->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="32dp"
android:paddingStart="@dimen/gesture_tutorial_hotseat_padding_start_end"
android:paddingEnd="@dimen/gesture_tutorial_hotseat_padding_start_end">
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_search_bar"
android:layout_width="0dp"
android:layout_height="@dimen/gesture_tutorial_hotseat_search_height"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_search_corner_radius"
app:cardBackgroundColor="@color/mock_search_bar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_icon_1"
android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
app:cardBackgroundColor="@color/mock_app_icon_1"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/hotseat_icon_2"/>
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_icon_2"
android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
app:cardBackgroundColor="@color/mock_app_icon_2"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
app:layout_constraintStart_toEndOf="@id/hotseat_icon_1"
app:layout_constraintEnd_toStartOf="@id/hotseat_icon_3"/>
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_icon_3"
android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
app:cardBackgroundColor="@color/mock_app_icon_3"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
app:layout_constraintStart_toEndOf="@id/hotseat_icon_2"
app:layout_constraintEnd_toStartOf="@id/hotseat_icon_4"/>
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_icon_4"
android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
app:cardBackgroundColor="@color/mock_app_icon_1"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
app:layout_constraintStart_toEndOf="@id/hotseat_icon_3"
app:layout_constraintEnd_toStartOf="@id/hotseat_icon_5"/>
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_icon_5"
android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
app:cardBackgroundColor="@color/mock_app_icon_4"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
app:layout_constraintStart_toEndOf="@id/hotseat_icon_4"
app:layout_constraintEnd_toStartOf="@id/hotseat_icon_6"/>
<androidx.cardview.widget.CardView
android:id="@+id/hotseat_icon_6"
android:layout_width="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_height="@dimen/gesture_tutorial_hotseat_icon_size"
android:layout_marginTop="@dimen/gesture_tutorial_hotseat_icon_search_margin"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_hotseat_icon_corner_radius"
app:cardBackgroundColor="@color/mock_app_icon_2"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toBottomOf="@id/hotseat_search_bar"
app:layout_constraintStart_toEndOf="@id/hotseat_icon_5"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
+57
View File
@@ -16,4 +16,61 @@
-->
<resources>
<dimen name="overview_task_margin">8dp</dimen>
<!-- Tips Gesture Tutorial -->
<dimen name="gesture_tutorial_feedback_margin_start_end">126dp</dimen>
<dimen name="gesture_tutorial_feedback_margin_top">24dp</dimen>
<!-- Gesture Tutorial mock conversations -->
<dimen name="gesture_tutorial_message_padding_start">42dp</dimen>
<dimen name="gesture_tutorial_message_padding_end">60dp</dimen>
<dimen name="gesture_tutorial_top_bar_margin_start">42dp</dimen>
<dimen name="gesture_tutorial_top_bar_margin_end">683dp</dimen>
<dimen name="gesture_tutorial_top_bar_button_margin_end">42dp</dimen>
<dimen name="gesture_tutorial_conversation_bottom_padding">35dp</dimen>
<integer name="gesture_tutorial_extra_messages_visibility">2</integer> <!-- GONE -->
<dimen name="gesture_tutorial_message_margin_start">505dp</dimen>
<dimen name="gesture_tutorial_reply_margin_end">462dp</dimen>
<dimen name="gesture_tutorial_input_margin_start">103dp</dimen>
<dimen name="gesture_tutorial_input_margin_end">103dp</dimen>
<dimen name="gesture_tutorial_tablet_message_1_margin">345dp</dimen>
<dimen name="gesture_tutorial_tablet_reply_1_margin">341dp</dimen>
<dimen name="gesture_tutorial_tablet_message_2_margin">501dp</dimen>
<dimen name="gesture_tutorial_tablet_message_3_margin">345dp</dimen>
<dimen name="gesture_tutorial_tablet_reply_2_margin">373dp</dimen>
<!-- Gesture Tutorial mock conversation lists -->
<dimen name="gesture_tutorial_conversation_line_1_margin_end">607dp</dimen>
<dimen name="gesture_tutorial_conversation_line_2_margin_end">460dp</dimen>
<dimen name="gesture_tutorial_conversation_line_3_margin_end">554dp</dimen>
<dimen name="gesture_tutorial_conversation_line_4_margin_end">517dp</dimen>
<dimen name="gesture_tutorial_conversation_line_5_margin_end">570dp</dimen>
<dimen name="gesture_tutorial_conversation_line_6_margin_end">336dp</dimen>
<dimen name="gesture_tutorial_conversation_line_7_margin_end">523dp</dimen>
<dimen name="gesture_tutorial_conversation_line_8_margin_end">500dp</dimen>
<dimen name="gesture_tutorial_tablet_conversation_line_6_margin_end">15dp</dimen>
<dimen name="gesture_tutorial_tablet_conversation_line_8_margin_end">72dp</dimen>
<dimen name="gesture_tutorial_tablet_conversation_line_10_margin_end">111dp</dimen>
<integer name="gesture_tutorial_extra_conversations_visibility">2</integer> <!-- GONE -->
<dimen name="gesture_tutorial_mock_button_margin_end">34dp</dimen>
<dimen name="gesture_tutorial_mock_button_margin_bottom">42dp</dimen>
<!-- Gesture Tutorial mock hotseats -->
<dimen name="gesture_tutorial_hotseat_width">-2px</dimen> <!-- wrap_content -->
<dimen name="gesture_tutorial_hotseat_height">-1px</dimen> <!-- match_parent -->
<dimen name="gesture_tutorial_hotseat_padding_start_end">170dp</dimen>
<!-- Gesture Tutorial mock webpages -->
<dimen name="gesture_tutorial_webpage_url_margin_start_end">24dp</dimen>
<dimen name="gesture_tutorial_webpage_top_bar_button_margin_start">48dp</dimen>
<dimen name="gesture_tutorial_webpage_top_bar_margin_start">121dp</dimen>
<dimen name="gesture_tutorial_webpage_top_bar_margin_end">355dp</dimen>
<dimen name="gesture_tutorial_webpage_line_1_margin_end">355dp</dimen>
<dimen name="gesture_tutorial_webpage_line_2_margin_end">208dp</dimen>
<dimen name="gesture_tutorial_webpage_line_3_margin_end">439dp</dimen>
<dimen name="gesture_tutorial_webpage_block_margin_end">311dp</dimen>
<integer name="gesture_tutorial_webpage_extra_lines_visibility">2</integer> <!-- GONE -->
<!-- Gesture Tutorial mock taskbar -->
<dimen name="gesture_tutorial_taskbar_padding_start_end">218dp</dimen>
</resources>
+1 -1
View File
@@ -23,8 +23,8 @@
</string-array>
<string name="stats_log_manager_class" translatable="false">com.android.quickstep.logging.StatsLogCompatManager</string>
<string name="test_information_handler_class" translatable="false">com.android.quickstep.QuickstepTestInformationHandler</string>
<string name="window_manager_proxy_class" translatable="false">com.android.quickstep.util.SystemWindowManagerProxy</string>
<!-- The number of thumbnails and icons to keep in the cache. The thumbnail cache size also
determines how many thumbnails will be fetched in the background. -->
+47 -4
View File
@@ -110,8 +110,10 @@
<dimen name="gestures_overscroll_finish_threshold">136dp</dimen>
<!-- Tips Gesture Tutorial -->
<dimen name="gesture_tutorial_feedback_margin_start_end">24dp</dimen>
<dimen name="gesture_tutorial_foldable_feedback_margin_start_end">140dp</dimen>
<dimen name="gesture_tutorial_feedback_margin_start_end">8dp</dimen>
<dimen name="gesture_tutorial_tablet_feedback_margin_start_end">140dp</dimen>
<dimen name="gesture_tutorial_feedback_margin_top">16dp</dimen>
<dimen name="gesture_tutorial_tablet_feedback_margin_top">24dp</dimen>
<dimen name="gesture_tutorial_multi_row_task_view_spacing">72dp</dimen>
<dimen name="gesture_tutorial_small_task_view_corner_radius">18dp</dimen>
<dimen name="gesture_tutorial_mock_taskbar_height">80dp</dimen>
@@ -124,15 +126,46 @@
<dimen name="gesture_tutorial_message_small_margin_bottom">4dp</dimen>
<dimen name="gesture_tutorial_message_padding_start">26dp</dimen>
<dimen name="gesture_tutorial_message_padding_end">18dp</dimen>
<dimen name="gesture_tutorial_foldable_message_padding_start_end">126dp</dimen>
<dimen name="gesture_tutorial_top_bar_margin_start">34dp</dimen>
<dimen name="gesture_tutorial_top_bar_margin_end">211dp</dimen>
<dimen name="gesture_tutorial_top_bar_button_margin_end">24dp</dimen>
<dimen name="gesture_tutorial_conversation_bottom_padding">66dp</dimen>
<integer name="gesture_tutorial_extra_messages_visibility">0</integer> <!-- VISIBLE -->
<dimen name="gesture_tutorial_message_margin_start">124dp</dimen>
<dimen name="gesture_tutorial_reply_margin_end">144dp</dimen>
<dimen name="gesture_tutorial_input_margin_start">34dp</dimen>
<dimen name="gesture_tutorial_input_margin_end">24dp</dimen>
<dimen name="gesture_tutorial_tablet_message_padding_start_end">126dp</dimen>
<dimen name="gesture_tutorial_tablet_message_1_margin">245dp</dimen>
<dimen name="gesture_tutorial_tablet_reply_1_margin">241dp</dimen>
<dimen name="gesture_tutorial_tablet_message_2_margin">401dp</dimen>
<dimen name="gesture_tutorial_tablet_message_3_margin">245dp</dimen>
<dimen name="gesture_tutorial_tablet_reply_2_margin">273dp</dimen>
<!-- Gesture Tutorial mock conversation lists -->
<dimen name="gesture_tutorial_conversation_icon_size">56dp</dimen>
<dimen name="gesture_tutorial_conversation_icon_corner_radius">100dp</dimen>
<dimen name="gesture_tutorial_conversation_list_padding_top">28dp</dimen>
<dimen name="gesture_tutorial_conversation_line_padding_start">20dp</dimen>
<dimen name="gesture_tutorial_conversation_line_1_margin_end">217dp</dimen>
<dimen name="gesture_tutorial_conversation_line_2_margin_end">142dp</dimen>
<dimen name="gesture_tutorial_conversation_line_3_margin_end">190dp</dimen>
<dimen name="gesture_tutorial_conversation_line_4_margin_end">171dp</dimen>
<dimen name="gesture_tutorial_conversation_line_5_margin_end">198dp</dimen>
<dimen name="gesture_tutorial_conversation_line_6_margin_end">79dp</dimen>
<dimen name="gesture_tutorial_conversation_line_7_margin_end">174dp</dimen>
<dimen name="gesture_tutorial_conversation_line_8_margin_end">117dp</dimen>
<dimen name="gesture_tutorial_tablet_conversation_line_6_margin_end">65dp</dimen>
<dimen name="gesture_tutorial_tablet_conversation_line_8_margin_end">132dp</dimen>
<dimen name="gesture_tutorial_tablet_conversation_line_10_margin_end">161dp</dimen>
<integer name="gesture_tutorial_extra_conversations_visibility">0</integer> <!-- VISIBLE -->
<dimen name="gesture_tutorial_mock_button_margin_end">24dp</dimen>
<dimen name="gesture_tutorial_mock_button_margin_bottom">66dp</dimen>
<!-- Gesture Tutorial mock hotseats -->
<dimen name="gesture_tutorial_hotseat_width">-1px</dimen> <!-- match_parent -->
<dimen name="gesture_tutorial_hotseat_height">-2px</dimen> <!-- wrap_content -->
<dimen name="gesture_tutorial_hotseat_padding_start_end">26dp</dimen>
<dimen name="gesture_tutorial_hotseat_icon_size">60dp</dimen>
<dimen name="gesture_tutorial_hotseat_icon_corner_radius">100dp</dimen>
<dimen name="gesture_tutorial_hotseat_search_height">50dp</dimen>
@@ -148,11 +181,20 @@
<dimen name="gesture_tutorial_webpage_small_corner_radius">4dp</dimen>
<dimen name="gesture_tutorial_webpage_large_line_height">36dp</dimen>
<dimen name="gesture_tutorial_webpage_small_line_height">22dp</dimen>
<dimen name="gesture_tutorial_webpage_url_margin_start_end">16dp</dimen>
<dimen name="gesture_tutorial_webpage_top_bar_button_margin_start">24dp</dimen>
<dimen name="gesture_tutorial_webpage_top_bar_margin_start">97dp</dimen>
<dimen name="gesture_tutorial_webpage_top_bar_margin_end">97dp</dimen>
<dimen name="gesture_tutorial_webpage_line_1_margin_end">126dp</dimen>
<dimen name="gesture_tutorial_webpage_line_2_margin_end">64dp</dimen>
<dimen name="gesture_tutorial_webpage_line_3_margin_end">151dp</dimen>
<dimen name="gesture_tutorial_webpage_block_margin_end">24dp</dimen>
<integer name="gesture_tutorial_webpage_extra_lines_visibility">0</integer> <!-- VISIBLE -->
<!-- Gesture Tutorial mock taskbar -->
<dimen name="gesture_tutorial_taskbar_icon_size">44dp</dimen>
<dimen name="gesture_tutorial_taskbar_icon_corner_radius">100dp</dimen>
<dimen name="gesture_tutorial_taskbar_padding_start_end">218dp</dimen>
<dimen name="gesture_tutorial_taskbar_padding_start_end">52dp</dimen>
<!-- All Set page -->
<dimen name="allset_page_margin_horizontal">40dp</dimen>
@@ -217,6 +259,7 @@
<dimen name="taskbar_contextual_buttons_size">35dp</dimen>
<dimen name="taskbar_stashed_size">24dp</dimen>
<dimen name="taskbar_stashed_handle_width">220dp</dimen>
<dimen name="taskbar_unstash_input_area">316dp</dimen>
<dimen name="taskbar_stashed_handle_height">4dp</dimen>
<dimen name="taskbar_edu_wave_anim_trans_y">25dp</dimen>
<dimen name="taskbar_edu_wave_anim_trans_y_return_overshoot">4dp</dimen>
+3 -1
View File
@@ -182,8 +182,10 @@
<string name="allset_title">All set!</string>
<!-- Hint string at the bottom of "All Set" page [CHAR LIMIT=NONE] -->
<string name="allset_hint">Swipe up to go Home</string>
<!-- Description of "All Set" page [CHAR LIMIT=NONE] -->
<!-- Description of "All Set" page on phones [CHAR LIMIT=NONE] -->
<string name="allset_description">You\u2019re ready to start using your phone</string>
<!-- Description of "All Set" page on tablets [CHAR LIMIT=NONE] -->
<string name="allset_description_tablet">You\u2019re ready to start using your tablet</string>
<!-- String linking to navigation settings on "All Set" page [CHAR LIMIT=NONE] -->
<string name="allset_navigation_settings"><annotation id="link">System navigation settings</annotation></string>
+13 -2
View File
@@ -47,6 +47,12 @@
<item name="android:lineHeight">44sp</item>
</style>
<style name="TextAppearance.GestureTutorial.Feedback.Title.AllSet"
parent="TextAppearance.GestureTutorial.Feedback.Title">
<item name="android:letterSpacing">0.03</item>
<item name="android:lineHeight">44sp</item>
</style>
<style name="TextAppearance.GestureTutorial.Dialog.Title"
parent="TextAppearance.GestureTutorial.Feedback.Title">
<item name="android:gravity">center_horizontal</item>
@@ -61,6 +67,12 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:fontFamily">google-sans-text</item>
<item name="android:letterSpacing">0.03</item>
<item name="android:textSize">14sp</item>
<item name="android:lineHeight">20sp</item>
</style>
<style name="TextAppearance.GestureTutorial.Feedback.Subtitle.AllSet"
parent="TextAppearance.GestureTutorial.Feedback.Subtitle">
<item name="android:textSize">18sp</item>
<item name="android:lineHeight">24sp</item>
</style>
@@ -76,9 +88,8 @@
<style name="TextAppearance.GestureTutorial.Feedback.Subtext"
parent="TextAppearance.GestureTutorial.Feedback.Subtitle">
<item name="android:textSize">16sp</item>
<item name="android:textColor">@color/gesture_tutorial_primary_color</item>
<item name="android:gravity">center</item>
<item name="android:gravity">start</item>
</style>
<style name="TextAppearance.GestureTutorial.Feedback.Subtext.Dark"
@@ -26,7 +26,6 @@ import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.DisplayController.NavigationMode.NO_BUTTON;
import static com.android.launcher3.util.DisplayController.NavigationMode.TWO_BUTTONS;
import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
@@ -38,7 +37,6 @@ import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.graphics.Insets;
import android.hardware.SensorManager;
import android.hardware.devicestate.DeviceStateManager;
import android.os.Bundle;
@@ -46,7 +44,6 @@ import android.os.CancellationSignal;
import android.os.IBinder;
import android.view.Display;
import android.view.View;
import android.view.WindowInsets;
import android.window.SplashScreen;
import androidx.annotation.Nullable;
@@ -614,17 +611,4 @@ public abstract class BaseQuickstepLauncher extends Launcher {
mDepthController.dump(prefix, writer);
}
}
@Override
public void updateWindowInsets(WindowInsets.Builder updatedInsetsBuilder,
WindowInsets oldInsets) {
// Override the tappable insets to be 0 on the bottom for gesture nav (otherwise taskbar
// would count towards it). This is used for the bottom protection in All Apps for example.
if (DisplayController.getNavigationMode(this) == NO_BUTTON) {
Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
oldTappableInsets.right, 0);
updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
}
}
}
@@ -579,8 +579,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
}
}
// Pause page indicator animations as they lead to layer trashing.
mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
// Pause expensive view updates as they can lead to layer thrashing and skipped frames.
mLauncher.pauseExpensiveViewUpdates();
endListener = () -> {
viewsToAnimate.forEach(view -> {
@@ -590,7 +590,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
if (scrimEnabled) {
mLauncher.getScrimView().setBackgroundColor(Color.TRANSPARENT);
}
mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
mLauncher.resumeExpensiveViewUpdates();
};
}
@@ -1186,6 +1186,19 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
return false;
}
private boolean hasMultipleTargetsWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
int numTargets = 0;
for (RemoteAnimationTargetCompat target : targets) {
if (target.mode == mode) {
numTargets++;
}
if (numTargets > 1) {
return true;
}
}
return false;
}
/**
* @return Runner that plays when user goes to Launcher
* ie. pressing home, swiping up from nav bar.
@@ -1580,7 +1593,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
View launcherView = findLauncherView(appTargets);
boolean playFallBackAnimation = (launcherView == null
&& launcherIsForceInvisibleOrOpening)
|| mLauncher.getWorkspace().isOverlayShown();
|| mLauncher.getWorkspace().isOverlayShown()
|| hasMultipleTargetsWithMode(appTargets, MODE_CLOSING);
boolean playWorkspaceReveal = true;
boolean skipAllAppsScale = false;
@@ -49,6 +49,7 @@ import android.view.Gravity;
import android.view.RoundedCorner;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.widget.FrameLayout;
import android.widget.Toast;
@@ -185,8 +186,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
new TaskbarDragLayerController(this, mDragLayer),
new TaskbarViewController(this, taskbarView),
new TaskbarScrimViewController(this, taskbarScrimView),
new TaskbarUnfoldAnimationController(unfoldTransitionProgressProvider,
mWindowManager),
new TaskbarUnfoldAnimationController(this, unfoldTransitionProgressProvider,
mWindowManager, WindowManagerGlobal.getWindowManagerService()),
new TaskbarKeyguardController(this),
new StashedHandleViewController(this, stashedHandleView),
new TaskbarStashController(this),
@@ -1,5 +1,6 @@
package com.android.launcher3.taskbar;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING;
@@ -14,6 +15,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import com.android.launcher3.AbstractFloatingView;
import com.android.systemui.shared.system.QuickStepContract;
import java.io.PrintWriter;
@@ -39,6 +41,7 @@ public class TaskbarKeyguardController implements TaskbarControllers.LoggableTas
@Override
public void onReceive(Context context, Intent intent) {
mIsScreenOff = true;
AbstractFloatingView.closeOpenViews(mContext, false, TYPE_ALL);
}
};
@@ -71,6 +74,10 @@ public class TaskbarKeyguardController implements TaskbarControllers.LoggableTas
mNavbarButtonsViewController.setKeyguardVisible(keyguardShowing || dozing,
keyguardOccluded);
updateIconsForBouncer();
if (keyguardShowing) {
AbstractFloatingView.closeOpenViews(mContext, true, TYPE_ALL);
}
}
public boolean isScreenOff() {
@@ -42,6 +42,7 @@ import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.SettingsCache;
@@ -74,8 +75,11 @@ public class TaskbarManager implements DisplayController.DisplayInfoChangeListen
private final SimpleBroadcastReceiver mShutdownReceiver;
// The source for this provider is set when Launcher is available
// We use 'non-destroyable' version here so the original provider won't be destroyed
// as it is tied to the activity lifecycle, not the taskbar lifecycle.
// It's destruction/creation will be managed by the activity.
private final ScopedUnfoldTransitionProgressProvider mUnfoldProgressProvider =
new ScopedUnfoldTransitionProgressProvider();
new NonDestroyableScopedUnfoldTransitionProgressProvider();
private TaskbarActivityContext mTaskbarActivityContext;
private StatefulActivity mActivity;
@@ -250,7 +250,11 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
* Returns whether the taskbar is currently visible and in an app.
*/
public boolean isInAppAndNotStashed() {
return !mIsStashed && (mState & FLAG_IN_APP) != 0;
return !mIsStashed && isInApp();
}
private boolean isInApp() {
return hasAnyFlag(FLAG_IN_APP);
}
/**
@@ -266,8 +270,15 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
return mUnstashedHeight;
}
boolean isAnimating = mAnimator != null && mAnimator.isStarted();
return mControllers.stashedHandleViewController.isStashedHandleVisible() || isAnimating
? mStashedHeight : 0;
if (!mControllers.stashedHandleViewController.isStashedHandleVisible()
&& isInApp()
&& !isAnimating) {
// We are in a settled state where we're not showing the handle even though taskbar
// is stashed. This can happen for example when home button is disabled (see
// StashedHandleViewController#setIsHomeButtonDisabled()).
return 0;
}
return mStashedHeight;
}
return mUnstashedHeight;
}
@@ -15,12 +15,14 @@
*/
package com.android.launcher3.taskbar;
import android.view.IWindowManager;
import android.view.View;
import android.view.WindowManager;
import com.android.quickstep.util.LauncherViewsMoveFromCenterTranslationApplier;
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
import java.io.PrintWriter;
@@ -31,14 +33,18 @@ import java.io.PrintWriter;
public class TaskbarUnfoldAnimationController implements
TaskbarControllers.LoggableTaskbarController {
private final ScopedUnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider;
private final ScopedUnfoldTransitionProgressProvider mScopedUnfoldTransitionProgressProvider;
private final NaturalRotationUnfoldProgressProvider mNaturalUnfoldTransitionProgressProvider;
private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimator;
private final TransitionListener mTransitionListener = new TransitionListener();
private TaskbarViewController mTaskbarViewController;
public TaskbarUnfoldAnimationController(ScopedUnfoldTransitionProgressProvider
unfoldTransitionProgressProvider, WindowManager windowManager) {
mUnfoldTransitionProgressProvider = unfoldTransitionProgressProvider;
public TaskbarUnfoldAnimationController(BaseTaskbarContext context,
ScopedUnfoldTransitionProgressProvider source,
WindowManager windowManager, IWindowManager iWindowManager) {
mScopedUnfoldTransitionProgressProvider = source;
mNaturalUnfoldTransitionProgressProvider =
new NaturalRotationUnfoldProgressProvider(context, iWindowManager, source);
mMoveFromCenterAnimator = new UnfoldMoveFromCenterAnimator(windowManager,
new LauncherViewsMoveFromCenterTranslationApplier());
}
@@ -48,18 +54,21 @@ public class TaskbarUnfoldAnimationController implements
* @param taskbarControllers references to all other taskbar controllers
*/
public void init(TaskbarControllers taskbarControllers) {
mNaturalUnfoldTransitionProgressProvider.init();
mTaskbarViewController = taskbarControllers.taskbarViewController;
mTaskbarViewController.addOneTimePreDrawListener(() ->
mUnfoldTransitionProgressProvider.setReadyToHandleTransition(true));
mUnfoldTransitionProgressProvider.addCallback(mTransitionListener);
mScopedUnfoldTransitionProgressProvider.setReadyToHandleTransition(true));
mNaturalUnfoldTransitionProgressProvider.addCallback(mTransitionListener);
}
/**
* Destroys the controller
*/
public void onDestroy() {
mUnfoldTransitionProgressProvider.setReadyToHandleTransition(false);
mUnfoldTransitionProgressProvider.removeCallback(mTransitionListener);
mScopedUnfoldTransitionProgressProvider.setReadyToHandleTransition(false);
mNaturalUnfoldTransitionProgressProvider.removeCallback(mTransitionListener);
mNaturalUnfoldTransitionProgressProvider.destroy();
mTaskbarViewController = null;
}
@Override
@@ -20,13 +20,14 @@ import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.AnimatedFloat.VALUE;
import android.annotation.NonNull;
import android.graphics.Rect;
import android.util.FloatProperty;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnPreDrawListener;
import androidx.core.view.OneShotPreDrawListener;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
@@ -143,18 +144,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
* drawing a frame and invoked only once
* @param listener callback that will be invoked before drawing the next frame
*/
public void addOneTimePreDrawListener(Runnable listener) {
mTaskbarView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
@Override
public boolean onPreDraw() {
final ViewTreeObserver viewTreeObserver = mTaskbarView.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
listener.run();
viewTreeObserver.removeOnPreDrawListener(this);
}
return true;
}
});
public void addOneTimePreDrawListener(@NonNull Runnable listener) {
OneShotPreDrawListener.add(mTaskbarView, listener);
}
public Rect getIconLayoutBounds() {
@@ -25,6 +25,9 @@ import android.view.WindowInsets;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.allapps.AllAppsGridAdapter;
import com.android.launcher3.allapps.AlphabeticalAppsList;
import com.android.launcher3.allapps.BaseAdapterProvider;
import com.android.launcher3.allapps.BaseAllAppsAdapter;
import com.android.launcher3.allapps.BaseAllAppsContainerView;
import com.android.launcher3.allapps.search.SearchAdapterProvider;
@@ -79,4 +82,11 @@ public class TaskbarAllAppsContainerView extends BaseAllAppsContainerView<Taskba
setInsets(insets.getInsets(WindowInsets.Type.systemBars()).toRect());
return super.onApplyWindowInsets(insets);
}
@Override
protected BaseAllAppsAdapter getAdapter(AlphabeticalAppsList<TaskbarAllAppsContext> mAppsList,
BaseAdapterProvider[] adapterProviders) {
return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), mAppsList,
adapterProviders);
}
}
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2022 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.taskbar.unfold;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
/**
* ScopedUnfoldTransitionProgressProvider that doesn't propagate destroy method
*/
public class NonDestroyableScopedUnfoldTransitionProgressProvider extends
ScopedUnfoldTransitionProgressProvider {
@Override
public void destroy() {
// no-op
}
}
@@ -20,7 +20,6 @@ import android.app.Person;
import android.content.Context;
import android.content.pm.ShortcutInfo;
import android.content.res.Resources;
import android.view.Display;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -36,20 +35,6 @@ public class ApiWrapper {
return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
}
/**
* Returns true if the display is an internal displays
*/
public static boolean isInternalDisplay(Display display) {
return display.getType() == Display.TYPE_INTERNAL;
}
/**
* Returns a unique ID representing the display
*/
public static String getUniqueId(Display display) {
return display.getUniqueId();
}
/**
* Returns the minimum space that should be left empty at the end of hotseat
*/
@@ -118,8 +118,8 @@ public final class RecentsViewStateController extends
if (isSplitSelectionState(currentState, toState)) {
// Animation to "dismiss" selected taskView
PendingAnimation splitSelectInitAnimation =
mRecentsView.createSplitSelectInitAnimation();
PendingAnimation splitSelectInitAnimation = mRecentsView.createSplitSelectInitAnimation(
toState.getTransitionDuration(mLauncher));
// Add properties to shift remaining taskViews to get out of placeholder view
splitSelectInitAnimation.setFloat(mRecentsView, taskViewsFloat.first,
toState.getSplitSelectTranslation(mLauncher), LINEAR);
@@ -35,11 +35,11 @@ import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.DisplayController.NavigationMode;
import com.android.launcher3.util.window.CachedDisplayInfo;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* Maintains state for supporting nav bars and tracking their gestures in multiple orientations.
@@ -51,55 +51,17 @@ import java.util.Objects;
*/
class OrientationTouchTransformer {
private static class CurrentDisplay {
public Point size;
public int rotation;
CurrentDisplay() {
this.size = new Point(0, 0);
this.rotation = 0;
}
CurrentDisplay(Point size, int rotation) {
this.size = size;
this.rotation = rotation;
}
@Override
public String toString() {
return "CurrentDisplay:"
+ " rotation: " + rotation
+ " size: " + size;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CurrentDisplay display = (CurrentDisplay) o;
if (rotation != display.rotation) return false;
return Objects.equals(size, display.size);
}
@Override
public int hashCode() {
return Objects.hash(size, rotation);
}
};
private static final String TAG = "OrientationTouchTransformer";
private static final boolean DEBUG = false;
private static final int QUICKSTEP_ROTATION_UNINITIALIZED = -1;
private final Map<CurrentDisplay, OrientationRectF> mSwipeTouchRegions =
new HashMap<CurrentDisplay, OrientationRectF>();
private final Map<CachedDisplayInfo, OrientationRectF> mSwipeTouchRegions =
new HashMap<CachedDisplayInfo, OrientationRectF>();
private final RectF mAssistantLeftRegion = new RectF();
private final RectF mAssistantRightRegion = new RectF();
private final RectF mOneHandedModeRegion = new RectF();
private CurrentDisplay mCurrentDisplay = new CurrentDisplay();
private CachedDisplayInfo mCachedDisplayInfo = new CachedDisplayInfo();
private int mNavBarGesturalHeight;
private final int mNavBarLargerGesturalHeight;
private boolean mEnableMultipleRegions;
@@ -184,22 +146,22 @@ class OrientationTouchTransformer {
* @see #enableMultipleRegions(boolean, Info)
*/
void createOrAddTouchRegion(Info info) {
mCurrentDisplay = new CurrentDisplay(info.currentSize, info.rotation);
mCachedDisplayInfo = new CachedDisplayInfo(info.currentSize, info.rotation);
if (mQuickStepStartingRotation > QUICKSTEP_ROTATION_UNINITIALIZED
&& mCurrentDisplay.rotation == mQuickStepStartingRotation) {
&& mCachedDisplayInfo.rotation == mQuickStepStartingRotation) {
// User already was swiping and the current screen is same rotation as the starting one
// Remove active nav bars in other rotations except for the one we started out in
resetSwipeRegions(info);
return;
}
OrientationRectF region = mSwipeTouchRegions.get(mCurrentDisplay);
OrientationRectF region = mSwipeTouchRegions.get(mCachedDisplayInfo);
if (region != null) {
return;
}
if (mEnableMultipleRegions) {
mSwipeTouchRegions.put(mCurrentDisplay, createRegionForDisplay(info));
mSwipeTouchRegions.put(mCachedDisplayInfo, createRegionForDisplay(info));
} else {
resetSwipeRegions(info);
}
@@ -245,31 +207,31 @@ class OrientationTouchTransformer {
*/
private void resetSwipeRegions(Info region) {
if (DEBUG) {
Log.d(TAG, "clearing all regions except rotation: " + mCurrentDisplay.rotation);
Log.d(TAG, "clearing all regions except rotation: " + mCachedDisplayInfo.rotation);
}
mCurrentDisplay = new CurrentDisplay(region.currentSize, region.rotation);
OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplay);
mCachedDisplayInfo = new CachedDisplayInfo(region.currentSize, region.rotation);
OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCachedDisplayInfo);
if (regionToKeep == null) {
regionToKeep = createRegionForDisplay(region);
}
mSwipeTouchRegions.clear();
mSwipeTouchRegions.put(mCurrentDisplay, regionToKeep);
mSwipeTouchRegions.put(mCachedDisplayInfo, regionToKeep);
updateAssistantRegions(regionToKeep);
}
private void resetSwipeRegions() {
OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplay);
OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCachedDisplayInfo);
mSwipeTouchRegions.clear();
if (regionToKeep != null) {
mSwipeTouchRegions.put(mCurrentDisplay, regionToKeep);
mSwipeTouchRegions.put(mCachedDisplayInfo, regionToKeep);
updateAssistantRegions(regionToKeep);
}
}
private OrientationRectF createRegionForDisplay(Info display) {
if (DEBUG) {
Log.d(TAG, "creating rotation region for: " + mCurrentDisplay.rotation
Log.d(TAG, "creating rotation region for: " + mCachedDisplayInfo.rotation
+ " with mode: " + mMode + " displayRotation: " + display.rotation);
}
@@ -368,7 +330,7 @@ class OrientationTouchTransformer {
true);
}
} else {
mLastRectTouched.applyTransformFromRotation(event, mCurrentDisplay.rotation,
mLastRectTouched.applyTransformFromRotation(event, mCachedDisplayInfo.rotation,
true);
}
break;
@@ -387,7 +349,7 @@ class OrientationTouchTransformer {
true);
}
} else {
mLastRectTouched.applyTransformFromRotation(event, mCurrentDisplay.rotation,
mLastRectTouched.applyTransformFromRotation(event, mCachedDisplayInfo.rotation,
true);
}
mLastRectTouched = null;
@@ -403,11 +365,12 @@ class OrientationTouchTransformer {
if (rect == null) {
continue;
}
if (rect.applyTransformFromRotation(event, mCurrentDisplay.rotation, false)) {
if (rect.applyTransformFromRotation(
event, mCachedDisplayInfo.rotation, false)) {
mLastRectTouched = rect;
mActiveTouchRotation = rect.getRotation();
if (mEnableMultipleRegions
&& mCurrentDisplay.rotation == mActiveTouchRotation) {
&& mCachedDisplayInfo.rotation == mActiveTouchRotation) {
// TODO(b/154580671) might make this block unnecessary
// Start a touch session for the default nav region for the display
mQuickStepStartingRotation = mLastRectTouched.getRotation();
@@ -430,7 +393,7 @@ class OrientationTouchTransformer {
pw.println(" lastTouchedRegion=" + mLastRectTouched);
pw.println(" multipleRegionsEnabled=" + mEnableMultipleRegions);
StringBuilder regions = new StringBuilder(" currentTouchableRotations=");
for (CurrentDisplay key: mSwipeTouchRegions.keySet()) {
for (CachedDisplayInfo key: mSwipeTouchRegions.keySet()) {
OrientationRectF rectF = mSwipeTouchRegions.get(key);
regions.append(rectF).append(" ");
}
@@ -49,9 +49,9 @@ import java.util.HashMap;
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
public static final boolean ENABLE_SHELL_TRANSITIONS =
SystemProperties.getBoolean("persist.debug.shell_transit", false);
SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS
&& SystemProperties.getBoolean("persist.debug.shell_transit_rotate", false);
&& SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
private RecentsAnimationController mController;
private RecentsAnimationCallbacks mCallbacks;
@@ -111,7 +111,8 @@ public class FallbackRecentsStateController implements StateHandler<RecentsState
RecentsState currentState = mActivity.getStateManager().getState();
if (isSplitSelectionState(state) && !isSplitSelectionState(currentState)) {
setter.add(mRecentsView.createSplitSelectInitAnimation().buildAnim());
setter.add(mRecentsView.createSplitSelectInitAnimation(
state.getTransitionDuration(mActivity)).buildAnim());
}
Pair<FloatProperty, FloatProperty> taskViewsFloat =
@@ -22,6 +22,7 @@ import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.quickstep.InputConsumer;
@@ -36,14 +37,20 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer {
private final GestureDetector mLongPressDetector;
private final float mSquaredTouchSlop;
private float mDownX, mDownY;
private boolean mCanceledUnstashHint;
private final float mUnstashArea;
private final float mScreenWidth;
public TaskbarStashInputConsumer(Context context, InputConsumer delegate,
InputMonitorCompat inputMonitor, TaskbarActivityContext taskbarActivityContext) {
super(delegate, inputMonitor);
mTaskbarActivityContext = taskbarActivityContext;
mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
mScreenWidth = context.getResources().getDisplayMetrics().widthPixels;
mUnstashArea = context.getResources()
.getDimensionPixelSize(R.dimen.taskbar_unstash_input_area);
mLongPressDetector = new GestureDetector(context, new SimpleOnGestureListener() {
@Override
@@ -69,11 +76,13 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer {
final float y = ev.getRawY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownX = x;
mDownY = y;
mTaskbarActivityContext.startTaskbarUnstashHint(
/* animateForward = */ true);
mCanceledUnstashHint = false;
if (isInArea(x)) {
mDownX = x;
mDownY = y;
mTaskbarActivityContext.startTaskbarUnstashHint(
/* animateForward = */ true);
mCanceledUnstashHint = false;
}
break;
case MotionEvent.ACTION_MOVE:
if (!mCanceledUnstashHint
@@ -95,10 +104,18 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer {
}
}
private boolean isInArea(float x) {
float areaFromMiddle = mUnstashArea / 2.0f;
float distFromMiddle = Math.abs(mScreenWidth / 2.0f - x);
return distFromMiddle < areaFromMiddle;
}
private void onLongPressDetected(MotionEvent motionEvent) {
if (mTaskbarActivityContext != null
&& mTaskbarActivityContext.onLongPressToUnstashTaskbar()) {
setActive(motionEvent);
if (mTaskbarActivityContext != null && isInArea(motionEvent.getRawX())) {
boolean taskBarPressed = mTaskbarActivityContext.onLongPressToUnstashTaskbar();
if (taskBarPressed) {
setActive(motionEvent);
}
}
}
}
@@ -52,6 +52,7 @@ import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.quickstep.AnimatedFloat;
@@ -110,6 +111,12 @@ public class AllSetActivity extends Activity {
mContentView = findViewById(R.id.content_view);
mSwipeUpShift = getResources().getDimension(R.dimen.allset_swipe_up_shift);
boolean isTablet = InvariantDeviceProfile.INSTANCE.get(getApplicationContext())
.getDeviceProfile(this).isTablet;
TextView subtitle = findViewById(R.id.subtitle);
subtitle.setText(isTablet
? R.string.allset_description_tablet : R.string.allset_description);
TextView tv = findViewById(R.id.navigation_settings);
tv.setTextColor(accentColor);
tv.setOnClickListener(v -> {
@@ -57,14 +57,14 @@ final class BackGestureTutorialController extends TutorialController {
@LayoutRes
int getMockAppTaskCurrentPageLayoutResId() {
return mTutorialFragment.isLargeScreen()
? R.layout.gesture_tutorial_foldable_mock_conversation
? R.layout.gesture_tutorial_tablet_mock_conversation
: R.layout.gesture_tutorial_mock_conversation;
}
@LayoutRes
int getMockAppTaskPreviousPageLayoutResId() {
return mTutorialFragment.isLargeScreen()
? R.layout.gesture_tutorial_foldable_mock_conversation_list
? R.layout.gesture_tutorial_tablet_mock_conversation_list
: R.layout.gesture_tutorial_mock_conversation_list;
}
@@ -51,7 +51,7 @@ final class HomeGestureTutorialController extends SwipeUpGestureTutorialControll
@Override
protected int getMockAppTaskLayoutResId() {
return mTutorialFragment.isLargeScreen()
? R.layout.gesture_tutorial_foldable_mock_webpage
? R.layout.gesture_tutorial_tablet_mock_webpage
: R.layout.gesture_tutorial_mock_webpage;
}
@@ -61,7 +61,7 @@ final class OverviewGestureTutorialController extends SwipeUpGestureTutorialCont
@Override
protected int getMockAppTaskLayoutResId() {
return mTutorialFragment.isLargeScreen()
? R.layout.gesture_tutorial_foldable_mock_conversation_list
? R.layout.gesture_tutorial_tablet_mock_conversation_list
: R.layout.gesture_tutorial_mock_conversation_list;
}
@@ -280,6 +280,15 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
super(context, deviceState, gestureState);
mRemoteTargetHandles[0] = new RemoteTargetGluer.RemoteTargetHandle(
mRemoteTargetHandles[0].getTaskViewSimulator(), new FakeTransformParams());
for (RemoteTargetGluer.RemoteTargetHandle handle
: mTargetGluer.getRemoteTargetHandles()) {
// Override home screen rotation preference so that home and overview animations
// work properly
handle.getTaskViewSimulator()
.getOrientationState()
.ignoreAllowHomeRotationPreference();
}
}
void initDp(DeviceProfile dp) {
@@ -336,8 +345,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
1f - SHAPE_PROGRESS_DURATION /* shapeProgressStart */,
radius, 255,
false, /* isOpening */
mFakeIconView, mDp,
false /* isVerticalBarLayout */);
mFakeIconView, mDp);
mFakeIconView.setAlpha(1);
mFakeTaskView.setAlpha(getWindowAlpha(progress));
mFakePreviousTaskView.setAlpha(getWindowAlpha(progress));
@@ -49,6 +49,7 @@ import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.content.res.AppCompatResources;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
@@ -73,7 +74,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
private static final int FEEDBACK_ANIMATION_MS = 133;
private static final int RIPPLE_VISIBLE_MS = 300;
private static final int GESTURE_ANIMATION_DELAY_MS = 1500;
private static final int ADVANCE_TUTORIAL_TIMEOUT_MS = 4000;
private static final int ADVANCE_TUTORIAL_TIMEOUT_MS = 2000;
private static final long GESTURE_ANIMATION_PAUSE_DURATION_MILLIS = 1000;
final TutorialFragment mTutorialFragment;
@@ -185,7 +186,9 @@ abstract class TutorialController implements BackGestureAttemptCallback,
@LayoutRes
protected int getMockHotseatResId() {
return mTutorialFragment.isLargeScreen()
? R.layout.gesture_tutorial_foldable_mock_hotseat
? (mTutorialFragment.isFoldable()
? R.layout.gesture_tutorial_foldable_mock_hotseat
: R.layout.gesture_tutorial_tablet_mock_hotseat)
: R.layout.gesture_tutorial_mock_hotseat;
}
@@ -319,6 +322,9 @@ abstract class TutorialController implements BackGestureAttemptCallback,
}
void hideFeedback() {
if (mFeedbackView.getVisibility() != View.VISIBLE) {
return;
}
cancelQueuedGestureAnimation();
mFeedbackView.clearAnimation();
mFeedbackView.setVisibility(View.INVISIBLE);
@@ -515,20 +521,45 @@ abstract class TutorialController implements BackGestureAttemptCallback,
}
private void updateLayout() {
if (mContext != null) {
RelativeLayout.LayoutParams feedbackLayoutParams =
(RelativeLayout.LayoutParams) mFeedbackView.getLayoutParams();
feedbackLayoutParams.setMarginStart(mContext.getResources().getDimensionPixelSize(
mTutorialFragment.isLargeScreen()
? R.dimen.gesture_tutorial_foldable_feedback_margin_start_end
: R.dimen.gesture_tutorial_feedback_margin_start_end));
feedbackLayoutParams.setMarginEnd(mContext.getResources().getDimensionPixelSize(
mTutorialFragment.isLargeScreen()
? R.dimen.gesture_tutorial_foldable_feedback_margin_start_end
: R.dimen.gesture_tutorial_feedback_margin_start_end));
mFakeTaskbarView.setVisibility(mTutorialFragment.isLargeScreen() ? View.VISIBLE : GONE);
if (mContext == null) {
return;
}
RelativeLayout.LayoutParams feedbackLayoutParams =
(RelativeLayout.LayoutParams) mFeedbackView.getLayoutParams();
feedbackLayoutParams.setMarginStart(mContext.getResources().getDimensionPixelSize(
mTutorialFragment.isLargeScreen()
? R.dimen.gesture_tutorial_tablet_feedback_margin_start_end
: R.dimen.gesture_tutorial_feedback_margin_start_end));
feedbackLayoutParams.setMarginEnd(mContext.getResources().getDimensionPixelSize(
mTutorialFragment.isLargeScreen()
? R.dimen.gesture_tutorial_tablet_feedback_margin_start_end
: R.dimen.gesture_tutorial_feedback_margin_start_end));
feedbackLayoutParams.topMargin = mContext.getResources().getDimensionPixelSize(
mTutorialFragment.isLargeScreen()
? R.dimen.gesture_tutorial_tablet_feedback_margin_top
: R.dimen.gesture_tutorial_feedback_margin_top);
mFakeTaskbarView.setVisibility(mTutorialFragment.isLargeScreen() ? View.VISIBLE : GONE);
RelativeLayout.LayoutParams hotseatLayoutParams =
(RelativeLayout.LayoutParams) mFakeHotseatView.getLayoutParams();
if (!mTutorialFragment.isLargeScreen()) {
DeviceProfile dp = mTutorialFragment.getDeviceProfile();
dp.updateIsSeascape(mContext);
hotseatLayoutParams.addRule(dp.isLandscape
? (dp.isSeascape()
? RelativeLayout.ALIGN_PARENT_START
: RelativeLayout.ALIGN_PARENT_END)
: RelativeLayout.ALIGN_PARENT_BOTTOM);
} else {
hotseatLayoutParams.width = RelativeLayout.LayoutParams.MATCH_PARENT;
hotseatLayoutParams.height = RelativeLayout.LayoutParams.WRAP_CONTENT;
hotseatLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
hotseatLayoutParams.removeRule(RelativeLayout.ALIGN_PARENT_START);
hotseatLayoutParams.removeRule(RelativeLayout.ALIGN_PARENT_END);
}
mFakeHotseatView.setLayoutParams(hotseatLayoutParams);
}
private AlertDialog createSkipTutorialDialog() {
@@ -20,7 +20,6 @@ import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Insets;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
@@ -41,6 +40,7 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.quickstep.interaction.TutorialController.TutorialType;
@@ -67,7 +67,9 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
private boolean mFragmentStopped = false;
private DeviceProfile mDeviceProfile;
private boolean mIsLargeScreen;
private boolean mIsFoldable;
public static TutorialFragment newInstance(TutorialType tutorialType, boolean gestureComplete) {
TutorialFragment fragment = getFragmentForTutorialType(tutorialType);
@@ -139,22 +141,24 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
mEdgeBackGestureHandler = new EdgeBackGestureHandler(getContext());
mNavBarGestureHandler = new NavBarGestureHandler(getContext());
mIsLargeScreen = InvariantDeviceProfile.INSTANCE.get(getContext())
.getDeviceProfile(getContext()).isTablet;
if (mIsLargeScreen) {
((Activity) getContext()).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
} else {
// Temporary until UI mocks for landscape mode for phones are created.
((Activity) getContext()).setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(getContext())
.getDeviceProfile(getContext());
mIsLargeScreen = mDeviceProfile.isTablet;
mIsFoldable = mDeviceProfile.isTwoPanels;
}
public boolean isLargeScreen() {
return mIsLargeScreen;
}
public boolean isFoldable() {
return mIsFoldable;
}
DeviceProfile getDeviceProfile() {
return mDeviceProfile;
}
@Override
public void onDestroy() {
super.onDestroy();
@@ -296,6 +300,9 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (mTutorialController != null) {
mTutorialController.hideFeedback();
}
// Note: Using logical-or to ensure both functions get called.
return mEdgeBackGestureHandler.onTouch(view, motionEvent)
| mNavBarGestureHandler.onTouch(view, motionEvent);
@@ -23,7 +23,6 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.graphics.ColorUtils;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -87,8 +86,10 @@ public class TutorialStepIndicator extends LinearLayout {
for (int i = mTotalSteps; i < getChildCount(); i++) {
removeViewAt(i);
}
int stepIndicatorColor = GraphicsUtils.getAttrColor(
int activeStepIndicatorColor = GraphicsUtils.getAttrColor(
getContext(), android.R.attr.textColorPrimary);
int inactiveStepIndicatorColor = GraphicsUtils.getAttrColor(
getContext(), android.R.attr.textColorSecondaryInverse);
for (int i = 0; i < mTotalSteps; i++) {
Drawable pageIndicatorPillDrawable = AppCompatResources.getDrawable(
getContext(), R.drawable.tutorial_step_indicator_pill);
@@ -107,10 +108,9 @@ public class TutorialStepIndicator extends LinearLayout {
}
if (pageIndicatorPillDrawable != null) {
if (i < mCurrentStep) {
pageIndicatorPillDrawable.setTint(stepIndicatorColor);
pageIndicatorPillDrawable.setTint(activeStepIndicatorColor);
} else {
pageIndicatorPillDrawable.setTint(
ColorUtils.setAlphaComponent(stepIndicatorColor, 0x22));
pageIndicatorPillDrawable.setTint(inactiveStepIndicatorColor);
}
}
}
@@ -19,6 +19,7 @@ package com.android.quickstep.logging;
import static androidx.core.util.Preconditions.checkNotNull;
import static androidx.core.util.Preconditions.checkState;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.EXTENDED_CONTAINERS;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.SEARCH_RESULT_CONTAINER;
@@ -82,6 +83,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
public class StatsLogCompatManager extends StatsLogManager {
private static final String TAG = "StatsLog";
private static final String LATENCY_TAG = "StatsLatencyLog";
private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG);
private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
// LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates
@@ -196,7 +198,9 @@ public class StatsLogCompatManager extends StatsLogManager {
private static class StatsCompatLogger implements StatsLogger {
private static final ItemInfo DEFAULT_ITEM_INFO = new ItemInfo();
static {
DEFAULT_ITEM_INFO.itemType = ITEM_TYPE_NON_ACTIONABLE;
}
private final Context mContext;
private final Optional<ActivityContext> mActivityContext;
private ItemInfo mItemInfo = DEFAULT_ITEM_INFO;
@@ -388,13 +392,21 @@ public class StatsLogCompatManager extends StatsLogManager {
if (IS_VERBOSE) {
String name = (event instanceof Enum) ? ((Enum) event).name() :
event.getId() + "";
Log.d(TAG, instanceId == DEFAULT_INSTANCE_ID
? String.format("\n%s (State:%s->%s)\n%s", name, getStateString(srcState),
getStateString(dstState), atomInfo)
: String.format("\n%s (State:%s->%s) (InstanceId:%s)\n%s", name,
getStateString(srcState), getStateString(dstState), instanceId,
atomInfo));
StringBuilder logStringBuilder = new StringBuilder("\n");
if (instanceId != DEFAULT_INSTANCE_ID) {
logStringBuilder.append(String.format("InstanceId:%s ", instanceId));
}
logStringBuilder.append(name);
if (srcState != LAUNCHER_STATE_UNSPECIFIED
|| dstState != LAUNCHER_STATE_UNSPECIFIED) {
logStringBuilder.append(
String.format("(State:%s->%s)", getStateString(srcState),
getStateString(dstState)));
}
if (mItemInfo != DEFAULT_ITEM_INFO) {
logStringBuilder.append("\n").append(atomInfo);
}
Log.d(TAG, logStringBuilder.toString());
}
for (StatsLogConsumer consumer : LOGS_CONSUMER) {
@@ -479,11 +491,12 @@ public class StatsLogCompatManager extends StatsLogManager {
if (IS_VERBOSE) {
String name = (event instanceof Enum) ? ((Enum) event).name() :
event.getId() + "";
Log.d(TAG, mInstanceId == DEFAULT_INSTANCE_ID
? String.format("\n%s = %dms\n", name, mLatencyInMillis)
: String.format("\n%s = %dms (InstanceId:%s)\n", name,
mLatencyInMillis, mInstanceId));
StringBuilder logStringBuilder = new StringBuilder("\n");
if (mInstanceId != DEFAULT_INSTANCE_ID) {
logStringBuilder.append(String.format("InstanceId:%s ", mInstanceId));
}
logStringBuilder.append(String.format("%s=%sms", name, mLatencyInMillis));
Log.d(LATENCY_TAG, logStringBuilder.toString());
}
SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_LATENCY,
@@ -48,10 +48,10 @@ import androidx.annotation.WorkerThread;
import androidx.core.content.FileProvider;
import com.android.internal.app.ChooserActivity;
import com.android.internal.util.ScreenshotHelper;
import com.android.launcher3.BuildConfig;
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.utilities.BitmapUtil;
import java.io.File;
import java.io.FileOutputStream;
@@ -77,7 +77,8 @@ public class ImageActionUtils {
public static void saveScreenshot(SystemUiProxy systemUiProxy, Bitmap screenshot,
Rect screenshotBounds,
Insets visibleInsets, Task.TaskKey task) {
systemUiProxy.handleImageBundleAsScreenshot(BitmapUtil.hardwareBitmapToBundle(screenshot),
systemUiProxy.handleImageBundleAsScreenshot(
ScreenshotHelper.HardwareBitmapBundler.hardwareBitmapToBundle(screenshot),
screenshotBounds, visibleInsets, task);
}
@@ -102,6 +102,8 @@ public class RecentsOrientedState implements
// Whether the swipe gesture is running, so the recents would stay locked in the
// current orientation
private static final int FLAG_SWIPE_UP_NOT_RUNNING = 1 << 8;
// Ignore shared prefs for home rotation rotation, allowing it in if the activity supports it
private static final int FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF = 1 << 9;
private static final int MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE =
FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY
@@ -371,12 +373,17 @@ public class RecentsOrientedState implements
== MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE;
}
public void ignoreAllowHomeRotationPreference() {
setFlag(FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF, true);
}
public boolean isRecentsActivityRotationAllowed() {
// Activity rotation is allowed if the multi-simulated-rotation is not supported
// (fallback recents or tablets) or activity rotation is enabled by various settings.
return ((mFlags & MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
!= MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
|| (mFlags & (FLAG_HOME_ROTATION_ALLOWED_IN_PREFS
|| (mFlags & (FLAG_IGNORE_ALLOW_HOME_ROTATION_PREF
| FLAG_HOME_ROTATION_ALLOWED_IN_PREFS
| FLAG_MULTIWINDOW_ROTATION_ALLOWED
| FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING)) != 0;
}
@@ -599,7 +606,7 @@ public class RecentsOrientedState implements
width = Math.min(currentSize.x, currentSize.y);
height = Math.max(currentSize.x, currentSize.y);
}
return idp.getBestMatch(width, height);
return idp.getBestMatch(width, height, mRecentsActivityRotation);
}
private static String nameAndAddress(Object obj) {
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2022 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.quickstep.util;
import android.content.Context;
import android.view.Display;
import com.android.launcher3.util.window.WindowManagerProxy;
/**
* Extension of {@link WindowManagerProxy} with some assumption for the default system Launcher
*/
public class SystemWindowManagerProxy extends WindowManagerProxy {
public SystemWindowManagerProxy(Context context) {
super(true);
}
@Override
protected String getDisplayId(Display display) {
return display.getUniqueId();
}
@Override
public boolean isInternalDisplay(Display display) {
return display.getType() == Display.TYPE_INTERNAL;
}
@Override
public int getRotation(Context context) {
return context.getResources().getConfiguration().windowConfiguration.getRotation();
}
}
@@ -28,7 +28,6 @@ import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRES
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.QuickstepTransitionManager.SPLIT_LAUNCH_DURATION;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.Utilities.squaredHypot;
@@ -45,7 +44,6 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
import static com.android.launcher3.testing.TestProtocol.TASK_VIEW_ID_CRASH;
import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
@@ -2282,8 +2280,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
* Sets the running task id, cleaning up the old running task if necessary.
*/
public void setCurrentTask(int runningTaskViewId) {
Log.d(TASK_VIEW_ID_CRASH, "currentRunningTaskViewId: " + mRunningTaskViewId
+ " requestedTaskViewId: " + runningTaskViewId);
if (mRunningTaskViewId == runningTaskViewId) {
return;
}
@@ -3987,14 +3983,12 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
splitSelectSource.position.stagePosition);
}
public PendingAnimation createSplitSelectInitAnimation() {
public PendingAnimation createSplitSelectInitAnimation(int duration) {
if (mSplitHiddenTaskView != null) {
int duration = mActivity.getStateManager().getState().getTransitionDuration(
getContext());
return createTaskDismissAnimation(mSplitHiddenTaskView, true, false, duration,
true /* dismissingForSplitSelection*/);
} else {
PendingAnimation anim = new PendingAnimation(SPLIT_LAUNCH_DURATION);
PendingAnimation anim = new PendingAnimation(duration);
createInitialSplitSelectAnimation(anim);
return anim;
}
@@ -36,7 +36,7 @@ import org.junit.rules.TestRule;
*/
public abstract class AbstractQuickStepTest extends AbstractLauncherUiTest {
static final boolean ENABLE_SHELL_TRANSITIONS =
SystemProperties.getBoolean("persist.debug.shell_transit", false);
SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
@Override
protected TestRule getRulesInsideActivityMonitor() {
return RuleChain.
@@ -15,16 +15,15 @@
*/
package com.android.quickstep.util;
import static android.view.Display.DEFAULT_DISPLAY;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.ArrayMap;
import android.util.Pair;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -38,6 +37,10 @@ import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.ReflectionHelpers;
import com.android.launcher3.util.RotationUtils;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.util.window.CachedDisplayInfo;
import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.quickstep.FallbackActivityInterface;
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -142,29 +145,34 @@ public class TaskViewSimulatorTest {
LauncherModelHelper helper = new LauncherModelHelper();
try {
helper.sandboxContext.allow(SystemUiProxy.INSTANCE);
int rotation = mDisplaySize.x > mDisplaySize.y
? Surface.ROTATION_90 : Surface.ROTATION_0;
CachedDisplayInfo cdi =
new CachedDisplayInfo("test-display", mDisplaySize, rotation , new Rect());
WindowBounds wm = new WindowBounds(
new Rect(0, 0, mDisplaySize.x, mDisplaySize.y),
mDisplayInsets);
WindowBounds[] allBounds = new WindowBounds[4];
for (int i = 0; i < 4; i++) {
Rect boundsR = new Rect(wm.bounds);
Rect insetsR = new Rect(wm.insets);
Display display = mock(Display.class);
doReturn(DEFAULT_DISPLAY).when(display).getDisplayId();
doReturn(mDisplaySize.x > mDisplaySize.y ? Surface.ROTATION_90 : Surface.ROTATION_0)
.when(display).getRotation();
doAnswer(i -> {
((Point) i.getArgument(0)).set(mDisplaySize.x, mDisplaySize.y);
return null;
}).when(display).getRealSize(any());
doAnswer(i -> {
Point smallestSize = i.getArgument(0);
Point largestSize = i.getArgument(1);
smallestSize.x = smallestSize.y = Math.min(mDisplaySize.x, mDisplaySize.y);
largestSize.x = largestSize.y = Math.max(mDisplaySize.x, mDisplaySize.y);
RotationUtils.rotateRect(insetsR, RotationUtils.deltaRotation(rotation, i));
RotationUtils.rotateRect(boundsR, RotationUtils.deltaRotation(rotation, i));
boundsR.set(0, 0, Math.abs(boundsR.width()), Math.abs(boundsR.height()));
allBounds[i] = new WindowBounds(boundsR, insetsR);
}
smallestSize.x -= mDisplayInsets.left + mDisplayInsets.right;
largestSize.x -= mDisplayInsets.left + mDisplayInsets.right;
WindowManagerProxy wmProxy = mock(WindowManagerProxy.class);
doReturn(cdi).when(wmProxy).getDisplayInfo(any());
doReturn(wm).when(wmProxy).getRealBounds(any(), any(), any());
smallestSize.y -= mDisplayInsets.top + mDisplayInsets.bottom;
largestSize.y -= mDisplayInsets.top + mDisplayInsets.bottom;
return null;
}).when(display).getCurrentSizeRange(any(), any());
DisplayController.Info mockInfo = new Info(helper.sandboxContext, display);
ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> perDisplayBoundsCache =
new ArrayMap<>();
perDisplayBoundsCache.put(cdi.id, Pair.create(cdi.normalize(), allBounds));
DisplayController.Info mockInfo = new Info(
helper.sandboxContext, mock(Display.class), wmProxy, perDisplayBoundsCache);
DisplayController controller =
DisplayController.INSTANCE.get(helper.sandboxContext);
@@ -172,7 +180,7 @@ public class TaskViewSimulatorTest {
ReflectionHelpers.setField(controller, "mInfo", mockInfo);
mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(helper.sandboxContext)
.getBestMatch(mAppBounds.width(), mAppBounds.height());
.getBestMatch(mAppBounds.width(), mAppBounds.height(), rotation);
mDeviceProfile.updateInsets(mLauncherInsets);
TaskViewSimulator tvs = new TaskViewSimulator(helper.sandboxContext,
+1
View File
@@ -69,6 +69,7 @@
<string name="test_information_handler_class" translatable="false"></string>
<string name="launcher_activity_logic_class" translatable="false"></string>
<string name="model_delegate_class" translatable="false"></string>
<string name="window_manager_proxy_class" translatable="false"></string>
<!-- View ID to use for QSB widget -->
<item type="id" name="qsb_widget" />
@@ -29,7 +29,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -41,8 +40,6 @@ import android.util.Log;
import android.view.ActionMode;
import android.view.Display;
import android.view.View;
import android.view.WindowInsets.Type;
import android.view.WindowMetrics;
import android.widget.Toast;
import androidx.annotation.NonNull;
@@ -322,11 +319,7 @@ public abstract class BaseDraggingActivity extends BaseActivity
protected WindowBounds getMultiWindowDisplaySize() {
if (Utilities.ATLEAST_R) {
WindowMetrics wm = getWindowManager().getCurrentWindowMetrics();
Insets insets = wm.getWindowInsets().getInsets(Type.systemBars());
return new WindowBounds(wm.getBounds(),
new Rect(insets.left, insets.top, insets.right, insets.bottom));
return WindowBounds.fromWindowMetrics(getWindowManager().getCurrentWindowMetrics());
}
// Note: Calls to getSize() can't rely on our cached DefaultDisplay since it can return
// the app window size
+11 -4
View File
@@ -75,6 +75,7 @@ public class DeviceProfile {
public final int heightPx;
public final int availableWidthPx;
public final int availableHeightPx;
public final int rotationHint;
public final float aspectRatio;
@@ -239,6 +240,7 @@ public class DeviceProfile {
this.isGestureMode = isGestureMode;
windowX = windowBounds.bounds.left;
windowY = windowBounds.bounds.top;
this.rotationHint = windowBounds.rotationHint;
isScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode;
@@ -346,9 +348,14 @@ public class DeviceProfile {
workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
hotseatQsbHeight = res.getDimensionPixelSize(R.dimen.qsb_widget_height);
isQsbInline = isLargeTablet && isLandscape && hotseatQsbHeight > 0;
// Whether QSB might be inline in appropriate orientation (landscape).
boolean canQsbInline = isLargeTablet && hotseatQsbHeight > 0;
isQsbInline = canQsbInline && isLandscape;
if (isTaskbarPresent && !isGestureMode && isQsbInline) {
// We shrink hotseat sizes regardless of orientation, if nav buttons are inline and QSB
// might be inline in either orientations, to keep hotseat size consistent across rotation.
boolean areNavButtonsInline = isTaskbarPresent && !isGestureMode;
if (areNavButtonsInline && canQsbInline) {
numShownHotseatIcons = inv.numShrunkenHotseatIcons;
} else {
numShownHotseatIcons =
@@ -543,8 +550,8 @@ public class DeviceProfile {
}
public Builder toBuilder(Context context) {
WindowBounds bounds =
new WindowBounds(widthPx, heightPx, availableWidthPx, availableHeightPx);
WindowBounds bounds = new WindowBounds(
widthPx, heightPx, availableWidthPx, availableHeightPx, rotationHint);
bounds.bounds.offsetTo(windowX, windowY);
return new Builder(context, inv, mInfo)
.setWindowBounds(bounds)
@@ -55,6 +55,7 @@ import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.WindowBounds;
import com.android.launcher3.util.window.WindowManagerProxy;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -180,8 +181,7 @@ public class InvariantDeviceProfile {
private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
@VisibleForTesting
public InvariantDeviceProfile() {
}
public InvariantDeviceProfile() { }
@TargetApi(23)
private InvariantDeviceProfile(Context context) {
@@ -278,11 +278,16 @@ public class InvariantDeviceProfile {
}
private static @DeviceType int getDeviceType(Info displayInfo) {
// Each screen has two profiles (portrait/landscape), so devices with four or more
// supported profiles implies two or more internal displays.
if (displayInfo.supportedBounds.size() >= 4 && ENABLE_TWO_PANEL_HOME.get()) {
int flagPhone = 1 << 0;
int flagTablet = 1 << 1;
int type = displayInfo.supportedBounds.stream()
.mapToInt(bounds -> displayInfo.isTablet(bounds) ? flagTablet : flagPhone)
.reduce(0, (a, b) -> a | b);
if ((type == (flagPhone | flagTablet)) && ENABLE_TWO_PANEL_HOME.get()) {
// device has profiles supporting both phone and table modes
return TYPE_MULTI_DISPLAY;
} else if (displayInfo.supportedBounds.stream().allMatch(displayInfo::isTablet)) {
} else if (type == flagTablet) {
return TYPE_TABLET;
} else {
return TYPE_PHONE;
@@ -613,10 +618,14 @@ public class InvariantDeviceProfile {
float screenWidth = config.screenWidthDp * res.getDisplayMetrics().density;
float screenHeight = config.screenHeightDp * res.getDisplayMetrics().density;
return getBestMatch(screenWidth, screenHeight);
return getBestMatch(screenWidth, screenHeight,
WindowManagerProxy.INSTANCE.get(context).getRotation(context));
}
public DeviceProfile getBestMatch(float screenWidth, float screenHeight) {
/**
* Returns the device profile matching the provided screen configuration
*/
public DeviceProfile getBestMatch(float screenWidth, float screenHeight, int rotation) {
DeviceProfile bestMatch = supportedProfiles.get(0);
float minDiff = Float.MAX_VALUE;
@@ -626,6 +635,8 @@ public class InvariantDeviceProfile {
if (diff < minDiff) {
minDiff = diff;
bestMatch = profile;
} else if (diff == minDiff && profile.rotationHint == rotation) {
bestMatch = profile;
}
}
return bestMatch;
+30 -15
View File
@@ -228,6 +228,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
@@ -1108,21 +1109,23 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
&& mAllAppsSessionLogId == null) {
// creates new instance ID since new all apps session is started.
mAllAppsSessionLogId = new InstanceIdSequence().newInstanceId();
getStatsLogManager().logger().withContainerInfo(
ContainerInfo.newBuilder().setWorkspace(
WorkspaceContainer.newBuilder().setPageIndex(
getWorkspace().getCurrentPage())).build())
.log(getAllAppsEntryEvent());
if (getAllAppsEntryEvent().isPresent()) {
getStatsLogManager().logger()
.withContainerInfo(ContainerInfo.newBuilder()
.setWorkspace(WorkspaceContainer.newBuilder()
.setPageIndex(getWorkspace().getCurrentPage())).build())
.log(getAllAppsEntryEvent().get());
}
}
}
/**
* Returns {@link EventEnum} that should be logged when Launcher enters into AllApps state.
*/
protected EventEnum getAllAppsEntryEvent() {
return FeatureFlags.ENABLE_DEVICE_SEARCH.get()
protected Optional<EventEnum> getAllAppsEntryEvent() {
return Optional.of(FeatureFlags.ENABLE_DEVICE_SEARCH.get()
? LAUNCHER_ALLAPPS_ENTRY_WITH_DEVICE_SEARCH
: LAUNCHER_ALLAPPS_ENTRY;
: LAUNCHER_ALLAPPS_ENTRY);
}
@Override
@@ -1151,17 +1154,18 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
// Making sure mAllAppsSessionLogId is not null to avoid double logging.
&& mAllAppsSessionLogId != null) {
getAppsView().reset(false);
getStatsLogManager().logger()
.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
.setWorkspace(
LauncherAtom.WorkspaceContainer.newBuilder()
.setPageIndex(getWorkspace().getCurrentPage()))
.build())
.log(LAUNCHER_ALLAPPS_EXIT);
getAllAppsExitEvent().ifPresent(getStatsLogManager().logger()::log);
mAllAppsSessionLogId = null;
}
}
/**
* Returns {@link EventEnum} that should be logged when Launcher exists from AllApps state.
*/
protected Optional<EventEnum> getAllAppsExitEvent() {
return Optional.of(LAUNCHER_ALLAPPS_EXIT);
}
@Override
protected void onResume() {
Object traceToken = TraceHelper.INSTANCE.beginSection(ON_RESUME_EVT,
@@ -3192,4 +3196,15 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
public ArrowPopup<?> getOptionsPopup() {
return findViewById(R.id.popup_container);
}
/** Pauses view updates that should not be run during the app launch animation. */
public void pauseExpensiveViewUpdates() {
// Pause page indicator animations as they lead to layer trashing.
getWorkspace().getPageIndicator().pauseAnimations();
}
/** Resumes view updates at the end of the app launch animation. */
public void resumeExpensiveViewUpdates() {
getWorkspace().getPageIndicator().skipAnimationsToEnd();
}
}
@@ -1,24 +1,19 @@
package com.android.launcher3;
import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.ViewDebug;
import android.view.WindowInsets;
import androidx.annotation.RequiresApi;
import com.android.launcher3.graphics.SysUiScrim;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.util.window.WindowManagerProxy;
import java.util.Collections;
import java.util.List;
@@ -60,76 +55,12 @@ public class LauncherRootView extends InsettableFrameLayout {
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
if (Utilities.ATLEAST_R) {
insets = updateInsetsDueToTaskbar(insets);
Insets systemWindowInsets = insets.getInsetsIgnoringVisibility(
WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
mTempRect.set(systemWindowInsets.left, systemWindowInsets.top, systemWindowInsets.right,
systemWindowInsets.bottom);
} else {
mTempRect.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
}
insets = WindowManagerProxy.INSTANCE.get(getContext())
.normalizeWindowInsets(getContext(), insets, mTempRect);
handleSystemWindowInsets(mTempRect);
return insets;
}
/**
* Taskbar provides nav bar and tappable insets. However, taskbar is not attached immediately,
* and can be destroyed and recreated. Thus, instead of relying on taskbar being present to
* get its insets, we calculate them ourselves so they are stable regardless of whether taskbar
* is currently attached.
*
* @param oldInsets The system-provided insets, which we are modifying.
* @return The updated insets.
*/
@RequiresApi(api = Build.VERSION_CODES.R)
private WindowInsets updateInsetsDueToTaskbar(WindowInsets oldInsets) {
if (!ApiWrapper.TASKBAR_DRAWN_IN_PROCESS) {
// 3P launchers based on Launcher3 should still be inset like normal.
return oldInsets;
}
WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
DeviceProfile dp = mActivity.getDeviceProfile();
Resources resources = getResources();
Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
Rect newNavInsets = new Rect(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
oldNavInsets.bottom);
if (dp.isLandscape) {
boolean isGesturalMode = ResourceUtils.getIntegerByName(
"config_navBarInteractionMode",
resources,
INVALID_RESOURCE_HANDLE) == 2;
if (dp.isTablet || isGesturalMode) {
newNavInsets.bottom = dp.isTaskbarPresent
? 0
: ResourceUtils.getNavbarSize("navigation_bar_height_landscape", resources);
} else {
int navWidth = ResourceUtils.getNavbarSize("navigation_bar_width", resources);
if (dp.isSeascape()) {
newNavInsets.left = navWidth;
} else {
newNavInsets.right = navWidth;
}
}
} else {
newNavInsets.bottom = dp.isTaskbarPresent
? 0
: ResourceUtils.getNavbarSize("navigation_bar_height", resources);
}
updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), Insets.of(newNavInsets));
updatedInsetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(),
Insets.of(newNavInsets));
mActivity.updateWindowInsets(updatedInsetsBuilder, oldInsets);
return updatedInsetsBuilder.build();
}
@Override
public void setInsets(Rect insets) {
// If the insets haven't changed, this is a no-op. Avoid unnecessary layout caused by
@@ -28,6 +28,9 @@ public class ResourceUtils {
public static final String NAVBAR_BOTTOM_GESTURE_LARGER_SIZE =
"navigation_bar_gesture_larger_height";
public static final String NAVBAR_HEIGHT = "navigation_bar_height";
public static final String NAVBAR_HEIGHT_LANDSCAPE = "navigation_bar_height_landscape";
public static int getNavbarSize(String resName, Resources res) {
return getDimenByName(resName, res, DEFAULT_NAVBAR_VALUE);
}
@@ -256,4 +256,11 @@ public class ActivityAllAppsContainerView<T extends BaseDraggingActivity> extend
layoutParams.removeRule(RelativeLayout.BELOW);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
}
@Override
protected BaseAllAppsAdapter getAdapter(AlphabeticalAppsList<T> mAppsList,
BaseAdapterProvider[] adapterProviders) {
return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), mAppsList,
adapterProviders);
}
}
@@ -15,36 +15,20 @@
*/
package com.android.launcher3.allapps;
import static com.android.launcher3.touch.ItemLongClickListener.INSTANCE_ALL_APPS;
import android.content.Context;
import android.content.res.Resources;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.accessibility.AccessibilityEventCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityRecordCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.views.ActivityContext;
import java.util.Arrays;
import java.util.List;
/**
@@ -53,111 +37,26 @@ import java.util.List;
* @param <T> Type of context inflating all apps.
*/
public class AllAppsGridAdapter<T extends Context & ActivityContext> extends
RecyclerView.Adapter<AllAppsGridAdapter.ViewHolder> {
BaseAllAppsAdapter<T> {
public static final String TAG = "AppsGridAdapter";
private final GridLayoutManager mGridLayoutMgr;
private final GridSpanSizer mGridSizer;
// A normal icon
public static final int VIEW_TYPE_ICON = 1 << 1;
// The message shown when there are no filtered results
public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 2;
// The message to continue to a market search when there are no filtered results
public static final int VIEW_TYPE_SEARCH_MARKET = 1 << 3;
// We use various dividers for various purposes. They share enough attributes to reuse layouts,
// but differ in enough attributes to require different view types
// A divider that separates the apps list and the search market button
public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 4;
// Common view type masks
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
private final BaseAdapterProvider[] mAdapterProviders;
/**
* ViewHolder for each icon.
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View v) {
super(v);
}
public AllAppsGridAdapter(T activityContext, LayoutInflater inflater,
AlphabeticalAppsList apps, BaseAdapterProvider[] adapterProviders) {
super(activityContext, inflater, apps, adapterProviders);
mGridSizer = new GridSpanSizer();
mGridLayoutMgr = new AppsGridLayoutManager(mActivityContext);
mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
setAppsPerRow(activityContext.getDeviceProfile().numShownAllAppsColumns);
}
/**
* Info about a particular adapter item (can be either section or app)
* Returns the grid layout manager.
*/
public static class AdapterItem {
/** Common properties */
// The index of this adapter item in the list
public int position;
// The type of this item
public int viewType;
// The section name of this item. Note that there can be multiple items with different
// sectionNames in the same section
public String sectionName = null;
// The row that this item shows up on
public int rowIndex;
// The index of this app in the row
public int rowAppIndex;
// The associated ItemInfoWithIcon for the item
public ItemInfoWithIcon itemInfo = null;
// The index of this app not including sections
public int appIndex = -1;
// Search section associated to result
public DecorationInfo decorationInfo = null;
/**
* Factory method for AppIcon AdapterItem
*/
public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo,
int appIndex) {
AdapterItem item = new AdapterItem();
item.viewType = VIEW_TYPE_ICON;
item.position = pos;
item.sectionName = sectionName;
item.itemInfo = appInfo;
item.appIndex = appIndex;
return item;
}
/**
* Factory method for empty search results view
*/
public static AdapterItem asEmptySearch(int pos) {
AdapterItem item = new AdapterItem();
item.viewType = VIEW_TYPE_EMPTY_SEARCH;
item.position = pos;
return item;
}
/**
* Factory method for a dividerView in AllAppsSearch
*/
public static AdapterItem asAllAppsDivider(int pos) {
AdapterItem item = new AdapterItem();
item.viewType = VIEW_TYPE_ALL_APPS_DIVIDER;
item.position = pos;
return item;
}
/**
* Factory method for a market search button
*/
public static AdapterItem asMarketSearch(int pos) {
AdapterItem item = new AdapterItem();
item.viewType = VIEW_TYPE_SEARCH_MARKET;
item.position = pos;
return item;
}
protected boolean isCountedForAccessibility() {
return viewType == VIEW_TYPE_ICON || viewType == VIEW_TYPE_SEARCH_MARKET;
}
public RecyclerView.LayoutManager getLayoutManager() {
return mGridLayoutMgr;
}
/**
@@ -217,7 +116,7 @@ public class AllAppsGridAdapter<T extends Context & ActivityContext> extends
*/
private int getRowsNotForAccessibility(int adapterPosition) {
List<AdapterItem> items = mApps.getAdapterItems();
adapterPosition = Math.max(adapterPosition, mApps.getAdapterItems().size() - 1);
adapterPosition = Math.max(adapterPosition, items.size() - 1);
int extraRows = 0;
for (int i = 0; i <= adapterPosition; i++) {
if (!isViewType(items.get(i).viewType, VIEW_TYPE_MASK_ICON)) {
@@ -228,6 +127,20 @@ public class AllAppsGridAdapter<T extends Context & ActivityContext> extends
}
}
@Override
public void setAppsPerRow(int appsPerRow) {
mAppsPerRow = appsPerRow;
int totalSpans = mAppsPerRow;
for (BaseAdapterProvider adapterProvider : mAdapterProviders) {
for (int itemPerRow : adapterProvider.getSupportedItemsPerRowArray()) {
if (totalSpans % itemPerRow != 0) {
totalSpans *= itemPerRow;
}
}
}
mGridLayoutMgr.setSpanCount(totalSpans);
}
/**
* Helper class to size the grid items.
*/
@@ -255,202 +168,4 @@ public class AllAppsGridAdapter<T extends Context & ActivityContext> extends
}
}
}
private final T mActivityContext;
private final LayoutInflater mLayoutInflater;
private final AlphabeticalAppsList<T> mApps;
private final GridLayoutManager mGridLayoutMgr;
private final GridSpanSizer mGridSizer;
private final OnClickListener mOnIconClickListener;
private OnLongClickListener mOnIconLongClickListener = INSTANCE_ALL_APPS;
private int mAppsPerRow;
private OnFocusChangeListener mIconFocusListener;
// The text to show when there are no search results and no market search handler.
protected String mEmptySearchMessage;
// The click listener to send off to the market app, updated each time the search query changes.
private OnClickListener mMarketSearchClickListener;
private final int mExtraHeight;
public AllAppsGridAdapter(T activityContext, LayoutInflater inflater,
AlphabeticalAppsList<T> apps, BaseAdapterProvider[] adapterProviders) {
Resources res = activityContext.getResources();
mActivityContext = activityContext;
mApps = apps;
mEmptySearchMessage = res.getString(R.string.all_apps_loading_message);
mGridSizer = new GridSpanSizer();
mGridLayoutMgr = new AppsGridLayoutManager(mActivityContext);
mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
mLayoutInflater = inflater;
mOnIconClickListener = mActivityContext.getItemOnClickListener();
mAdapterProviders = adapterProviders;
setAppsPerRow(mActivityContext.getDeviceProfile().numShownAllAppsColumns);
mExtraHeight = mActivityContext.getResources().getDimensionPixelSize(
R.dimen.all_apps_height_extra);
}
public void setAppsPerRow(int appsPerRow) {
mAppsPerRow = appsPerRow;
int totalSpans = mAppsPerRow;
for (BaseAdapterProvider adapterProvider : mAdapterProviders) {
for (int itemPerRow : adapterProvider.getSupportedItemsPerRowArray()) {
if (totalSpans % itemPerRow != 0) {
totalSpans *= itemPerRow;
}
}
}
mGridLayoutMgr.setSpanCount(totalSpans);
}
/**
* Sets the long click listener for icons
*/
public void setOnIconLongClickListener(@Nullable OnLongClickListener listener) {
mOnIconLongClickListener = listener;
}
public static boolean isDividerViewType(int viewType) {
return isViewType(viewType, VIEW_TYPE_MASK_DIVIDER);
}
public static boolean isIconViewType(int viewType) {
return isViewType(viewType, VIEW_TYPE_MASK_ICON);
}
public static boolean isViewType(int viewType, int viewTypeMask) {
return (viewType & viewTypeMask) != 0;
}
public void setIconFocusListener(OnFocusChangeListener focusListener) {
mIconFocusListener = focusListener;
}
/**
* Sets the last search query that was made, used to show when there are no results and to also
* seed the intent for searching the market.
*/
public void setLastSearchQuery(String query, OnClickListener marketSearchClickListener) {
Resources res = mActivityContext.getResources();
mEmptySearchMessage = res.getString(R.string.all_apps_no_search_results, query);
mMarketSearchClickListener = marketSearchClickListener;
}
/**
* Returns the grid layout manager.
*/
public GridLayoutManager getLayoutManager() {
return mGridLayoutMgr;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_ICON:
int layout = !FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get() ? R.layout.all_apps_icon
: R.layout.all_apps_icon_twoline;
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
layout, parent, false);
icon.setLongPressTimeoutFactor(1f);
icon.setOnFocusChangeListener(mIconFocusListener);
icon.setOnClickListener(mOnIconClickListener);
icon.setOnLongClickListener(mOnIconLongClickListener);
// Ensure the all apps icon height matches the workspace icons in portrait mode.
icon.getLayoutParams().height =
mActivityContext.getDeviceProfile().allAppsCellHeightPx;
if (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get()) {
icon.getLayoutParams().height += mExtraHeight;
}
return new ViewHolder(icon);
case VIEW_TYPE_EMPTY_SEARCH:
return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
parent, false));
case VIEW_TYPE_SEARCH_MARKET:
View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market,
parent, false);
searchMarketView.setOnClickListener(mMarketSearchClickListener);
return new ViewHolder(searchMarketView);
case VIEW_TYPE_ALL_APPS_DIVIDER:
return new ViewHolder(mLayoutInflater.inflate(
R.layout.all_apps_divider, parent, false));
default:
BaseAdapterProvider adapterProvider = getAdapterProvider(viewType);
if (adapterProvider != null) {
return adapterProvider.onCreateViewHolder(mLayoutInflater, parent, viewType);
}
throw new RuntimeException("Unexpected view type" + viewType);
}
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case VIEW_TYPE_ICON:
AdapterItem adapterItem = mApps.getAdapterItems().get(position);
BubbleTextView icon = (BubbleTextView) holder.itemView;
icon.reset();
if (adapterItem.itemInfo instanceof AppInfo) {
icon.applyFromApplicationInfo((AppInfo) adapterItem.itemInfo);
} else {
icon.applyFromItemInfoWithIcon(adapterItem.itemInfo);
}
break;
case VIEW_TYPE_EMPTY_SEARCH:
TextView emptyViewText = (TextView) holder.itemView;
emptyViewText.setText(mEmptySearchMessage);
emptyViewText.setGravity(mApps.hasNoFilteredResults() ? Gravity.CENTER :
Gravity.START | Gravity.CENTER_VERTICAL);
break;
case VIEW_TYPE_SEARCH_MARKET:
TextView searchView = (TextView) holder.itemView;
if (mMarketSearchClickListener != null) {
searchView.setVisibility(View.VISIBLE);
} else {
searchView.setVisibility(View.GONE);
}
break;
case VIEW_TYPE_ALL_APPS_DIVIDER:
// nothing to do
break;
default:
BaseAdapterProvider adapterProvider = getAdapterProvider(holder.getItemViewType());
if (adapterProvider != null) {
adapterProvider.onBindView(holder, position);
}
}
}
@Override
public void onViewRecycled(@NonNull ViewHolder holder) {
super.onViewRecycled(holder);
}
@Override
public boolean onFailedToRecycleView(ViewHolder holder) {
// Always recycle and we will reset the view when it is bound
return true;
}
@Override
public int getItemCount() {
return mApps.getAdapterItems().size();
}
@Override
public int getItemViewType(int position) {
AdapterItem item = mApps.getAdapterItems().get(position);
return item.viewType;
}
@Nullable
private BaseAdapterProvider getAdapterProvider(int viewType) {
return Arrays.stream(mAdapterProviders).filter(
adapterProvider -> adapterProvider.isViewSupported(viewType)).findFirst().orElse(
null);
}
}
@@ -19,6 +19,7 @@ import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.UNSPECIFIED;
import static android.view.View.MeasureSpec.makeMeasureSpec;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END;
import static com.android.launcher3.util.LogConfig.SEARCH_LOGGING;
@@ -203,6 +204,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView {
StatsLogManager mgr = ActivityContext.lookupContext(getContext()).getStatsLogManager();
switch (state) {
case SCROLL_STATE_DRAGGING:
mgr.logger().log(LAUNCHER_ALLAPPS_SCROLLED);
requestFocus();
mgr.logger().sendToInteractionJankMonitor(
LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN, this);
@@ -18,7 +18,7 @@ package com.android.launcher3.allapps;
import android.content.Context;
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -82,7 +82,7 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement
// The of ordered component names as a result of a search query
private ArrayList<AdapterItem> mSearchResults;
private AllAppsGridAdapter<T> mAdapter;
private BaseAllAppsAdapter<T> mAdapter;
private AppInfoComparator mAppNameComparator;
private final int mNumAppsPerRow;
private int mNumAppRowsInAdapter;
@@ -106,7 +106,7 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement
/**
* Sets the adapter to notify when this dataset changes.
*/
public void setAdapter(AllAppsGridAdapter<T> adapter) {
public void setAdapter(BaseAllAppsAdapter<T> adapter) {
mAdapter = adapter;
}
@@ -0,0 +1,333 @@
/*
* Copyright (C) 2022 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.allapps;
import static com.android.launcher3.touch.ItemLongClickListener.INSTANCE_ALL_APPS;
import android.content.Context;
import android.content.res.Resources;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.views.ActivityContext;
import java.util.Arrays;
/**
* Adapter for all the apps.
*
* @param <T> Type of context inflating all apps.
*/
public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> extends
RecyclerView.Adapter<BaseAllAppsAdapter.ViewHolder> {
public static final String TAG = "BaseAllAppsAdapter";
// A normal icon
public static final int VIEW_TYPE_ICON = 1 << 1;
// The message shown when there are no filtered results
public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 2;
// The message to continue to a market search when there are no filtered results
public static final int VIEW_TYPE_SEARCH_MARKET = 1 << 3;
// We use various dividers for various purposes. They share enough attributes to reuse layouts,
// but differ in enough attributes to require different view types
// A divider that separates the apps list and the search market button
public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 4;
// Common view type masks
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
protected final BaseAdapterProvider[] mAdapterProviders;
/**
* ViewHolder for each icon.
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View v) {
super(v);
}
}
/** Sets the number of apps to be displayed in one row of the all apps screen. */
public abstract void setAppsPerRow(int appsPerRow);
/**
* Info about a particular adapter item (can be either section or app)
*/
public static class AdapterItem {
/** Common properties */
// The index of this adapter item in the list
public int position;
// The type of this item
public int viewType;
// The section name of this item. Note that there can be multiple items with different
// sectionNames in the same section
public String sectionName = null;
// The row that this item shows up on
public int rowIndex;
// The index of this app in the row
public int rowAppIndex;
// The associated ItemInfoWithIcon for the item
public ItemInfoWithIcon itemInfo = null;
// The index of this app not including sections
public int appIndex = -1;
// Search section associated to result
public DecorationInfo decorationInfo = null;
/**
* Factory method for AppIcon AdapterItem
*/
public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo,
int appIndex) {
AdapterItem item = new AdapterItem();
item.viewType = VIEW_TYPE_ICON;
item.position = pos;
item.sectionName = sectionName;
item.itemInfo = appInfo;
item.appIndex = appIndex;
return item;
}
/**
* Factory method for empty search results view
*/
public static AdapterItem asEmptySearch(int pos) {
AdapterItem item = new AdapterItem();
item.viewType = VIEW_TYPE_EMPTY_SEARCH;
item.position = pos;
return item;
}
/**
* Factory method for a dividerView in AllAppsSearch
*/
public static AdapterItem asAllAppsDivider(int pos) {
AdapterItem item = new AdapterItem();
item.viewType = VIEW_TYPE_ALL_APPS_DIVIDER;
item.position = pos;
return item;
}
/**
* Factory method for a market search button
*/
public static AdapterItem asMarketSearch(int pos) {
AdapterItem item = new AdapterItem();
item.viewType = VIEW_TYPE_SEARCH_MARKET;
item.position = pos;
return item;
}
protected boolean isCountedForAccessibility() {
return viewType == VIEW_TYPE_ICON || viewType == VIEW_TYPE_SEARCH_MARKET;
}
}
protected final T mActivityContext;
protected final AlphabeticalAppsList<T> mApps;
// The text to show when there are no search results and no market search handler.
protected String mEmptySearchMessage;
protected int mAppsPerRow;
private final LayoutInflater mLayoutInflater;
private final OnClickListener mOnIconClickListener;
private OnLongClickListener mOnIconLongClickListener = INSTANCE_ALL_APPS;
private OnFocusChangeListener mIconFocusListener;
// The click listener to send off to the market app, updated each time the search query changes.
private OnClickListener mMarketSearchClickListener;
private final int mExtraHeight;
public BaseAllAppsAdapter(T activityContext, LayoutInflater inflater,
AlphabeticalAppsList<T> apps, BaseAdapterProvider[] adapterProviders) {
Resources res = activityContext.getResources();
mActivityContext = activityContext;
mApps = apps;
mEmptySearchMessage = res.getString(R.string.all_apps_loading_message);
mLayoutInflater = inflater;
mOnIconClickListener = mActivityContext.getItemOnClickListener();
mAdapterProviders = adapterProviders;
mExtraHeight = res.getDimensionPixelSize(R.dimen.all_apps_height_extra);
}
/**
* Sets the long click listener for icons
*/
public void setOnIconLongClickListener(@Nullable OnLongClickListener listener) {
mOnIconLongClickListener = listener;
}
/** Checks if the passed viewType represents all apps divider. */
public static boolean isDividerViewType(int viewType) {
return isViewType(viewType, VIEW_TYPE_MASK_DIVIDER);
}
/** Checks if the passed viewType represents all apps icon. */
public static boolean isIconViewType(int viewType) {
return isViewType(viewType, VIEW_TYPE_MASK_ICON);
}
public void setIconFocusListener(OnFocusChangeListener focusListener) {
mIconFocusListener = focusListener;
}
/**
* Sets the last search query that was made, used to show when there are no results and to also
* seed the intent for searching the market.
*/
public void setLastSearchQuery(String query, OnClickListener marketSearchClickListener) {
Resources res = mActivityContext.getResources();
mEmptySearchMessage = res.getString(R.string.all_apps_no_search_results, query);
mMarketSearchClickListener = marketSearchClickListener;
}
/**
* Returns the layout manager.
*/
public abstract RecyclerView.LayoutManager getLayoutManager();
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_ICON:
int layout = !FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get() ? R.layout.all_apps_icon
: R.layout.all_apps_icon_twoline;
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
layout, parent, false);
icon.setLongPressTimeoutFactor(1f);
icon.setOnFocusChangeListener(mIconFocusListener);
icon.setOnClickListener(mOnIconClickListener);
icon.setOnLongClickListener(mOnIconLongClickListener);
// Ensure the all apps icon height matches the workspace icons in portrait mode.
icon.getLayoutParams().height =
mActivityContext.getDeviceProfile().allAppsCellHeightPx;
if (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get()) {
icon.getLayoutParams().height += mExtraHeight;
}
return new ViewHolder(icon);
case VIEW_TYPE_EMPTY_SEARCH:
return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
parent, false));
case VIEW_TYPE_SEARCH_MARKET:
View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market,
parent, false);
searchMarketView.setOnClickListener(mMarketSearchClickListener);
return new ViewHolder(searchMarketView);
case VIEW_TYPE_ALL_APPS_DIVIDER:
return new ViewHolder(mLayoutInflater.inflate(
R.layout.all_apps_divider, parent, false));
default:
BaseAdapterProvider adapterProvider = getAdapterProvider(viewType);
if (adapterProvider != null) {
return adapterProvider.onCreateViewHolder(mLayoutInflater, parent, viewType);
}
throw new RuntimeException("Unexpected view type" + viewType);
}
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case VIEW_TYPE_ICON:
AdapterItem adapterItem = mApps.getAdapterItems().get(position);
BubbleTextView icon = (BubbleTextView) holder.itemView;
icon.reset();
if (adapterItem.itemInfo instanceof AppInfo) {
icon.applyFromApplicationInfo((AppInfo) adapterItem.itemInfo);
} else {
icon.applyFromItemInfoWithIcon(adapterItem.itemInfo);
}
break;
case VIEW_TYPE_EMPTY_SEARCH:
TextView emptyViewText = (TextView) holder.itemView;
emptyViewText.setText(mEmptySearchMessage);
emptyViewText.setGravity(mApps.hasNoFilteredResults() ? Gravity.CENTER :
Gravity.START | Gravity.CENTER_VERTICAL);
break;
case VIEW_TYPE_SEARCH_MARKET:
TextView searchView = (TextView) holder.itemView;
if (mMarketSearchClickListener != null) {
searchView.setVisibility(View.VISIBLE);
} else {
searchView.setVisibility(View.GONE);
}
break;
case VIEW_TYPE_ALL_APPS_DIVIDER:
// nothing to do
break;
default:
BaseAdapterProvider adapterProvider = getAdapterProvider(holder.getItemViewType());
if (adapterProvider != null) {
adapterProvider.onBindView(holder, position);
}
}
}
@Override
public void onViewRecycled(@NonNull ViewHolder holder) {
super.onViewRecycled(holder);
}
@Override
public boolean onFailedToRecycleView(ViewHolder holder) {
// Always recycle and we will reset the view when it is bound
return true;
}
@Override
public int getItemCount() {
return mApps.getAdapterItems().size();
}
@Override
public int getItemViewType(int position) {
AdapterItem item = mApps.getAdapterItems().get(position);
return item.viewType;
}
protected static boolean isViewType(int viewType, int viewTypeMask) {
return (viewType & viewTypeMask) != 0;
}
@Nullable
protected BaseAdapterProvider getAdapterProvider(int viewType) {
return Arrays.stream(mAdapterProviders).filter(
adapterProvider -> adapterProvider.isViewSupported(viewType)).findFirst().orElse(
null);
}
}
@@ -44,7 +44,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.DeviceProfile;
@@ -697,18 +696,21 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte
return ColorUtils.blendARGB(mScrimColor, mHeaderProtectionColor, blendRatio);
}
protected abstract BaseAllAppsAdapter getAdapter(AlphabeticalAppsList<T> mAppsList,
BaseAdapterProvider[] adapterProviders);
protected int getHeaderBottom() {
return (int) getTranslationY();
}
/** Holds a {@link AllAppsGridAdapter} and related fields. */
/** Holds a {@link BaseAllAppsAdapter} and related fields. */
public class AdapterHolder {
public static final int MAIN = 0;
public static final int WORK = 1;
private final boolean mIsWork;
public final AllAppsGridAdapter<T> adapter;
final LinearLayoutManager mLayoutManager;
public final BaseAllAppsAdapter<T> adapter;
final RecyclerView.LayoutManager mLayoutManager;
final AlphabeticalAppsList<T> mAppsList;
final Rect mPadding = new Rect();
AllAppsRecyclerView mRecyclerView;
@@ -724,8 +726,7 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte
mWorkManager.getAdapterProvider()}
: new BaseAdapterProvider[]{mMainAdapterProvider};
adapter = new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), mAppsList,
adapterProviders);
adapter = getAdapter(mAppsList, adapterProviders);
mAppsList.setAdapter(adapter);
mLayoutManager = adapter.getLayoutManager();
}
@@ -33,7 +33,7 @@ import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.search.SearchAlgorithm;
import com.android.launcher3.search.SearchCallback;
@@ -38,9 +38,9 @@ import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.AlphabeticalAppsList;
import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
import com.android.launcher3.allapps.SearchUiManager;
import com.android.launcher3.search.SearchCallback;
@@ -23,7 +23,7 @@ import android.os.Handler;
import androidx.annotation.AnyThread;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BaseModelUpdateTask;
import com.android.launcher3.model.BgDataModel;
@@ -87,6 +87,7 @@ import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.widget.BaseLauncherAppWidgetHostView;
@@ -128,7 +129,8 @@ public class LauncherPreviewRenderer extends ContextWrapper
public PreviewContext(Context base, InvariantDeviceProfile idp) {
super(base, UserCache.INSTANCE, InstallSessionHelper.INSTANCE,
LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
CustomWidgetManager.INSTANCE, PluginManagerWrapper.INSTANCE);
CustomWidgetManager.INSTANCE, PluginManagerWrapper.INSTANCE,
WindowManagerProxy.INSTANCE);
mIdp = idp;
mObjectMap.put(InvariantDeviceProfile.INSTANCE, idp);
mObjectMap.put(LauncherAppState.INSTANCE,
@@ -440,7 +440,7 @@ public class IconCache extends BaseIconCache {
cn,
sectionKey.first);
}
if (loadFallbackTitle && TextUtils.isEmpty(entry.title)) {
if (loadFallbackTitle && TextUtils.isEmpty(entry.title) && lai != null) {
loadFallbackTitle(
lai,
entry,
@@ -527,7 +527,11 @@ public class StatsLogManager implements ResourceBasedOverride {
LAUNCHER_TASKBAR_LONGPRESS_SHOW(897),
@UiEvent(doc = "User clicks on the search icon on header to launch search in app.")
LAUNCHER_ALLAPPS_SEARCHINAPP_LAUNCH(913);
LAUNCHER_ALLAPPS_SEARCHINAPP_LAUNCH(913),
@UiEvent(doc = "User scrolled on one of the all apps surfaces such as A-Z list, search "
+ "result page etc.")
LAUNCHER_ALLAPPS_SCROLLED(985);
// ADD MORE
@@ -17,15 +17,11 @@ package com.android.launcher3.statemanager;
import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
import android.graphics.Insets;
import android.os.Build;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowInsets;
import androidx.annotation.CallSuper;
import androidx.annotation.RequiresApi;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.LauncherRootView;
@@ -178,14 +174,6 @@ public abstract class StatefulActivity<STATE_TYPE extends BaseState<STATE_TYPE>>
Utilities.postAsyncCallback(mHandler, mHandleDeferredResume);
}
/**
* Gives subclasses a chance to override some window insets (via
* {@link android.view.WindowInsets.Builder#setInsets(int, Insets)}).
*/
@RequiresApi(api = Build.VERSION_CODES.R)
public void updateWindowInsets(WindowInsets.Builder updatedInsetsBuilder,
WindowInsets oldInsets) { }
/**
* Runs the given {@param r} runnable when this activity binds to the touch interaction service.
*/
@@ -21,7 +21,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
import static com.android.launcher3.Utilities.dpiFromPx;
import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
@@ -125,7 +125,6 @@ public final class TestProtocol {
public static final String REQUEST_MOCK_SENSOR_ROTATION = "mock-sensor-rotation";
public static final String PERMANENT_DIAG_TAG = "TaplTarget";
public static final String TASK_VIEW_ID_CRASH = "b/195430732";
public static final String NO_DROP_TARGET = "b/195031154";
public static final String NULL_INT_SET = "b/200572078";
public static final String MISSING_PROMISE_ICON = "b/202985412";
@@ -26,10 +26,8 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
import static com.android.launcher3.util.WindowManagerCompat.MIN_LARGE_TABLET_WIDTH;
import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
import static java.util.Collections.emptyMap;
import static com.android.launcher3.util.window.WindowManagerProxy.MIN_LARGE_TABLET_WIDTH;
import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
@@ -38,12 +36,13 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.Build;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.view.Display;
import androidx.annotation.AnyThread;
@@ -52,11 +51,12 @@ import androidx.annotation.UiThread;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.util.window.CachedDisplayInfo;
import com.android.launcher3.util.window.WindowManagerProxy;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Map;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
@@ -89,6 +89,7 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
// Null for SDK < S
private final Context mWindowContext;
// The callback in this listener updates DeviceProfile, which other listeners might depend on
private DisplayInfoChangeListener mPriorityListener;
private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
@@ -115,23 +116,9 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
mContext.registerReceiver(mReceiver,
getPackageFilter(TARGET_OVERLAY_PACKAGE, ACTION_OVERLAY_CHANGED));
WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(context);
mInfo = new Info(getDisplayInfoContext(display), display,
getInternalDisplays(mDM), emptyMap());
}
private static ArrayMap<String, PortraitSize> getInternalDisplays(
DisplayManager displayManager) {
Display[] displays = displayManager.getDisplays();
ArrayMap<String, PortraitSize> internalDisplays = new ArrayMap<>();
for (Display display : displays) {
if (ApiWrapper.isInternalDisplay(display)) {
Point size = new Point();
display.getRealSize(size);
internalDisplays.put(ApiWrapper.getUniqueId(display),
new PortraitSize(size.x, size.y));
}
}
return internalDisplays;
wmProxy, wmProxy.estimateInternalDisplayBounds(context));
}
/**
@@ -226,16 +213,17 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
@AnyThread
private void handleInfoChange(Display display) {
WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(mContext);
Info oldInfo = mInfo;
Context displayContext = getDisplayInfoContext(display);
Info newInfo = new Info(displayContext, display,
oldInfo.mInternalDisplays, oldInfo.mPerDisplayBounds);
Info newInfo = new Info(displayContext, display, wmProxy, oldInfo.mPerDisplayBounds);
if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale
|| newInfo.navigationMode != oldInfo.navigationMode) {
// Cache may not be valid anymore, recreate without cache
newInfo = new Info(displayContext, display, getInternalDisplays(mDM), emptyMap());
newInfo = new Info(displayContext, display, wmProxy,
wmProxy.estimateInternalDisplayBounds(displayContext));
}
int change = 0;
@@ -254,9 +242,8 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds)) {
change |= CHANGE_SUPPORTED_BOUNDS;
PortraitSize realSize = new PortraitSize(newInfo.currentSize.x, newInfo.currentSize.y);
PortraitSize expectedSize = oldInfo.mInternalDisplays.get(
ApiWrapper.getUniqueId(display));
Point currentS = newInfo.currentSize;
Point expectedS = oldInfo.mPerDisplayBounds.get(newInfo.displayId).first.size;
if (newInfo.supportedBounds.size() != oldInfo.supportedBounds.size()) {
Log.e("b/198965093",
"Inconsistent number of displays"
@@ -264,7 +251,9 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
+ "\noldInfo.supportedBounds: " + oldInfo.supportedBounds
+ "\nnewInfo.supportedBounds: " + newInfo.supportedBounds);
}
if (!realSize.equals(expectedSize) && display.getState() == Display.STATE_OFF) {
if ((Math.min(currentS.x, currentS.y) != Math.min(expectedS.x, expectedS.y)
|| Math.max(currentS.x, currentS.y) != Math.max(expectedS.x, expectedS.y))
&& display.getState() == Display.STATE_OFF) {
Log.e("b/198965093", "Display size changed while display is off, ignoring change");
return;
}
@@ -290,30 +279,38 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
public static class Info {
// Configuration properties
// Cached property
public final int rotation;
public final String displayId;
public final Point currentSize;
public final Rect cutout;
// Configuration property
public final float fontScale;
public final int densityDpi;
public final NavigationMode navigationMode;
private final PortraitSize mScreenSizeDp;
public final Point currentSize;
public String displayId;
public final Set<WindowBounds> supportedBounds = new ArraySet<>();
private final Map<String, Set<WindowBounds>> mPerDisplayBounds = new ArrayMap<>();
private final ArrayMap<String, PortraitSize> mInternalDisplays;
private final ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> mPerDisplayBounds =
new ArrayMap<>();
public Info(Context context, Display display) {
this(context, display, new ArrayMap<>(), emptyMap());
/* don't need system overrides for external displays */
this(context, display, new WindowManagerProxy(), new ArrayMap<>());
}
private Info(Context context, Display display,
ArrayMap<String, PortraitSize> internalDisplays,
Map<String, Set<WindowBounds>> perDisplayBoundsCache) {
mInternalDisplays = internalDisplays;
rotation = display.getRotation();
// Used for testing
public Info(Context context, Display display,
WindowManagerProxy wmProxy,
ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> perDisplayBoundsCache) {
CachedDisplayInfo displayInfo = wmProxy.getDisplayInfo(display);
rotation = displayInfo.rotation;
currentSize = displayInfo.size;
displayId = displayInfo.id;
cutout = displayInfo.cutout;
Configuration config = context.getResources().getConfiguration();
fontScale = config.fontScale;
@@ -321,54 +318,29 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
mScreenSizeDp = new PortraitSize(config.screenHeightDp, config.screenWidthDp);
navigationMode = parseNavigationMode(context);
currentSize = new Point();
display.getRealSize(currentSize);
mPerDisplayBounds.putAll(perDisplayBoundsCache);
Pair<CachedDisplayInfo, WindowBounds[]> cachedValue = mPerDisplayBounds.get(displayId);
displayId = ApiWrapper.getUniqueId(display);
Set<WindowBounds> currentSupportedBounds =
getSupportedBoundsForDisplay(display, currentSize);
mPerDisplayBounds.put(displayId, currentSupportedBounds);
supportedBounds.addAll(currentSupportedBounds);
if (ApiWrapper.isInternalDisplay(display) && internalDisplays.size() > 1) {
int displayCount = internalDisplays.size();
for (int i = 0; i < displayCount; i++) {
String displayKey = internalDisplays.keyAt(i);
if (TextUtils.equals(displayId, displayKey)) {
continue;
}
Set<WindowBounds> displayBounds = perDisplayBoundsCache.get(displayKey);
if (displayBounds == null) {
// We assume densityDpi is the same across all internal displays
displayBounds = WindowManagerCompat.estimateDisplayProfiles(
context, internalDisplays.valueAt(i), densityDpi,
ApiWrapper.TASKBAR_DRAWN_IN_PROCESS);
}
supportedBounds.addAll(displayBounds);
mPerDisplayBounds.put(displayKey, displayBounds);
WindowBounds realBounds = wmProxy.getRealBounds(context, display, displayInfo);
if (cachedValue == null) {
supportedBounds.add(realBounds);
} else {
// Verify that the real bounds are a match
WindowBounds expectedBounds = cachedValue.second[displayInfo.rotation];
if (!realBounds.equals(expectedBounds)) {
WindowBounds[] clone = new WindowBounds[4];
System.arraycopy(cachedValue.second, 0, clone, 0, 4);
clone[displayInfo.rotation] = realBounds;
cachedValue = Pair.create(displayInfo.normalize(), clone);
mPerDisplayBounds.put(displayId, cachedValue);
}
}
mPerDisplayBounds.values().forEach(
pair -> Collections.addAll(supportedBounds, pair.second));
Log.d("b/211775278", "displayId: " + displayId + ", currentSize: " + currentSize);
Log.d("b/211775278", "perDisplayBounds: " + mPerDisplayBounds);
}
private static Set<WindowBounds> getSupportedBoundsForDisplay(Display display, Point size) {
Point smallestSize = new Point();
Point largestSize = new Point();
display.getCurrentSizeRange(smallestSize, largestSize);
int portraitWidth = Math.min(size.x, size.y);
int portraitHeight = Math.max(size.x, size.y);
Set<WindowBounds> result = new ArraySet<>();
result.add(new WindowBounds(portraitWidth, portraitHeight,
smallestSize.x, largestSize.y));
result.add(new WindowBounds(portraitHeight, portraitWidth,
largestSize.x, smallestSize.y));
return result;
}
/**
* Returns {@code true} if the bounds represent a tablet.
*/
@@ -15,10 +15,17 @@
*/
package com.android.launcher3.util;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
@@ -34,6 +41,9 @@ public class Executors {
Math.max(Runtime.getRuntime().availableProcessors(), 2);
private static final int KEEP_ALIVE = 1;
/** Dedicated executor instances for work depending on other packages. */
private static final Map<String, ExecutorService> PACKAGE_EXECUTORS = new ConcurrentHashMap<>();
/**
* An {@link ThreadPoolExecutor} to be used with async task with no limit on the queue size.
*/
@@ -75,6 +85,18 @@ public class Executors {
public static final LooperExecutor MODEL_EXECUTOR =
new LooperExecutor(createAndStartNewLooper("launcher-loader"));
/**
* Returns and caches a single thread executor for a given package.
*
* @param packageName Package associated with the executor.
*/
public static ExecutorService getPackageExecutor(String packageName) {
return PACKAGE_EXECUTORS.computeIfAbsent(
packageName,
p -> newSingleThreadExecutor(
new SimpleThreadFactory(p, THREAD_PRIORITY_BACKGROUND)));
}
/**
* A simple ThreadFactory to set the thread name and priority when used with executors.
*/
@@ -0,0 +1,75 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.util;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import android.graphics.Point;
import android.graphics.Rect;
/**
* Utility methods based on {@code frameworks/base/core/java/android/util/RotationUtils.java}
*/
public class RotationUtils {
/**
* Rotates an Rect according to the given rotation.
*/
public static void rotateRect(Rect rect, int rotation) {
switch (rotation) {
case ROTATION_0:
return;
case ROTATION_90:
rect.set(rect.top, rect.right, rect.bottom, rect.left);
return;
case ROTATION_180:
rect.set(rect.right, rect.bottom, rect.left, rect.top);
return;
case ROTATION_270:
rect.set(rect.bottom, rect.left, rect.top, rect.right);
return;
default:
throw new IllegalArgumentException("unknown rotation: " + rotation);
}
}
/**
* Rotates an size according to the given rotation.
*/
public static void rotateSize(Point size, int rotation) {
switch (rotation) {
case ROTATION_0:
case ROTATION_180:
return;
case ROTATION_90:
case ROTATION_270:
size.set(size.y, size.x);
return;
default:
throw new IllegalArgumentException("unknown rotation: " + rotation);
}
}
/** @return the rotation needed to rotate from oldRotation to newRotation. */
public static int deltaRotation(int oldRotation, int newRotation) {
int delta = newRotation - oldRotation;
if (delta < 0) delta += 4;
return delta;
}
}
@@ -33,19 +33,27 @@ public class WindowBounds {
public final Rect bounds;
public final Rect insets;
public final Point availableSize;
public final int rotationHint;
public WindowBounds(Rect bounds, Rect insets) {
this(bounds, insets, -1);
}
public WindowBounds(Rect bounds, Rect insets, int rotationHint) {
this.bounds = bounds;
this.insets = insets;
this.rotationHint = rotationHint;
availableSize = new Point(bounds.width() - insets.left - insets.right,
bounds.height() - insets.top - insets.bottom);
}
public WindowBounds(int width, int height, int availableWidth, int availableHeight) {
public WindowBounds(int width, int height, int availableWidth, int availableHeight,
int rotationHint) {
this.bounds = new Rect(0, 0, width, height);
this.availableSize = new Point(availableWidth, availableHeight);
// We don't care about insets in this case
this.insets = new Rect(0, 0, width - availableWidth, height - availableHeight);
this.rotationHint = rotationHint;
}
@Override
@@ -1,109 +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.util;
import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
import static com.android.launcher3.Utilities.dpiFromPx;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Build;
import android.util.ArraySet;
import android.view.WindowInsets;
import android.view.WindowInsets.Type;
import android.view.WindowManager;
import android.view.WindowMetrics;
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.DisplayController.PortraitSize;
import java.util.Collections;
import java.util.Set;
/**
* Utility class to estimate window manager values
*/
@TargetApi(Build.VERSION_CODES.S)
public class WindowManagerCompat {
public static final int MIN_TABLET_WIDTH = 600;
public static final int MIN_LARGE_TABLET_WIDTH = 720;
/**
* Returns a set of supported render sizes for a internal display.
* This is a temporary workaround which assumes only nav-bar insets change across displays, and
* is only used until we eventually get the real values
* @param consumeTaskBar if true, it assumes that task bar is part of the app window
* and ignores any insets because of task bar.
*/
public static Set<WindowBounds> estimateDisplayProfiles(
Context windowContext, PortraitSize size, int densityDpi, boolean consumeTaskBar) {
if (!Utilities.ATLEAST_S) {
return Collections.emptySet();
}
WindowInsets defaultInsets = windowContext.getSystemService(WindowManager.class)
.getMaximumWindowMetrics().getWindowInsets();
boolean isGesturalMode = ResourceUtils.getIntegerByName(
"config_navBarInteractionMode",
windowContext.getResources(),
INVALID_RESOURCE_HANDLE) == 2;
WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(defaultInsets);
Set<WindowBounds> result = new ArraySet<>();
int swDP = (int) dpiFromPx(size.width, densityDpi);
boolean isTablet = swDP >= MIN_TABLET_WIDTH;
final Insets portraitNav, landscapeNav;
if (isTablet && !consumeTaskBar) {
portraitNav = landscapeNav = Insets.of(0, 0, 0, windowContext.getResources()
.getDimensionPixelSize(R.dimen.taskbar_size));
} else if (!isGesturalMode) {
portraitNav = Insets.of(0, 0, 0,
getSystemResource(windowContext, "navigation_bar_height", swDP));
landscapeNav = isTablet
? Insets.of(0, 0, 0, getSystemResource(windowContext,
"navigation_bar_height_landscape", swDP))
: Insets.of(0, 0, getSystemResource(windowContext,
"navigation_bar_width", swDP), 0);
} else {
portraitNav = landscapeNav = Insets.of(0, 0, 0, 0);
}
result.add(WindowBounds.fromWindowMetrics(new WindowMetrics(
new Rect(0, 0, size.width, size.height),
insetsBuilder.setInsets(Type.navigationBars(), portraitNav).build())));
result.add(WindowBounds.fromWindowMetrics(new WindowMetrics(
new Rect(0, 0, size.height, size.width),
insetsBuilder.setInsets(Type.navigationBars(), landscapeNav).build())));
return result;
}
private static int getSystemResource(Context context, String key, int swDp) {
int resourceId = context.getResources().getIdentifier(key, "dimen", "android");
if (resourceId > 0) {
Configuration conf = new Configuration();
conf.smallestScreenWidthDp = swDp;
return context.createConfigurationContext(conf)
.getResources().getDimensionPixelSize(resourceId);
}
return 0;
}
}
@@ -0,0 +1,92 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.util.window;
import static com.android.launcher3.util.RotationUtils.deltaRotation;
import static com.android.launcher3.util.RotationUtils.rotateRect;
import static com.android.launcher3.util.RotationUtils.rotateSize;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.Surface;
import java.util.Objects;
/**
* Properties on a display
*/
public class CachedDisplayInfo {
public final String id;
public final Point size;
public final int rotation;
public final Rect cutout;
public CachedDisplayInfo() {
this(new Point(0, 0), 0);
}
public CachedDisplayInfo(Point size, int rotation) {
this("", size, rotation, new Rect());
}
public CachedDisplayInfo(String id, Point size, int rotation, Rect cutout) {
this.id = id;
this.size = size;
this.rotation = rotation;
this.cutout = cutout;
}
/**
* Returns a CachedDisplayInfo where the properties are normalized to {@link Surface#ROTATION_0}
*/
public CachedDisplayInfo normalize() {
if (rotation == Surface.ROTATION_0) {
return this;
}
Point newSize = new Point(size);
rotateSize(newSize, deltaRotation(rotation, Surface.ROTATION_0));
Rect newCutout = new Rect(cutout);
rotateRect(newCutout, deltaRotation(rotation, Surface.ROTATION_0));
return new CachedDisplayInfo(id, newSize, Surface.ROTATION_0, newCutout);
}
@Override
public String toString() {
return "CachedDisplayInfo{"
+ "id='" + id + '\''
+ ", size=" + size
+ ", rotation=" + rotation
+ ", cutout=" + cutout
+ '}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof CachedDisplayInfo)) return false;
CachedDisplayInfo that = (CachedDisplayInfo) o;
return rotation == that.rotation && Objects.equals(id, that.id)
&& Objects.equals(size, that.size) && Objects.equals(cutout,
that.cutout);
}
@Override
public int hashCode() {
return Objects.hash(id, size, rotation, cutout);
}
}
@@ -0,0 +1,311 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.util.window;
import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT;
import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE;
import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE;
import static com.android.launcher3.ResourceUtils.getDimenByName;
import static com.android.launcher3.Utilities.dpiFromPx;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import static com.android.launcher3.util.RotationUtils.deltaRotation;
import static com.android.launcher3.util.RotationUtils.rotateRect;
import static com.android.launcher3.util.RotationUtils.rotateSize;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.Build;
import android.util.ArrayMap;
import android.util.Pair;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.Surface;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.launcher3.util.WindowBounds;
/**
* Utility class for mocking some window manager behaviours
*/
public class WindowManagerProxy implements ResourceBasedOverride {
public static final int MIN_TABLET_WIDTH = 600;
public static final int MIN_LARGE_TABLET_WIDTH = 720;
public static final MainThreadInitializedObject<WindowManagerProxy> INSTANCE =
forOverride(WindowManagerProxy.class, R.string.window_manager_proxy_class);
protected final boolean mTaskbarDrawnInProcess;
/**
* Creates a new instance of proxy, applying any overrides
*/
public static WindowManagerProxy newInstance(Context context) {
return Overrides.getObject(WindowManagerProxy.class, context,
R.string.window_manager_proxy_class);
}
public WindowManagerProxy() {
this(false);
}
protected WindowManagerProxy(boolean taskbarDrawnInProcess) {
mTaskbarDrawnInProcess = taskbarDrawnInProcess;
}
/**
* Returns a map of normalized info of internal displays to estimated window bounds
* for that display
*/
public ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> estimateInternalDisplayBounds(
Context context) {
Display[] displays = context.getSystemService(DisplayManager.class).getDisplays();
ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> result = new ArrayMap<>();
for (Display display : displays) {
if (isInternalDisplay(display)) {
CachedDisplayInfo info = getDisplayInfo(display).normalize();
WindowBounds[] bounds = estimateWindowBounds(context, info);
result.put(info.id, Pair.create(info, bounds));
}
}
return result;
}
/**
* Returns the real bounds for the provided display after applying any insets normalization
*/
@TargetApi(Build.VERSION_CODES.R)
public WindowBounds getRealBounds(Context windowContext,
Display display, CachedDisplayInfo info) {
if (!Utilities.ATLEAST_R) {
Point smallestSize = new Point();
Point largestSize = new Point();
display.getCurrentSizeRange(smallestSize, largestSize);
if (info.size.y > info.size.x) {
// Portrait
return new WindowBounds(info.size.x, info.size.y, smallestSize.x, largestSize.y,
info.rotation);
} else {
// Landscape
new WindowBounds(info.size.x, info.size.y, largestSize.x, smallestSize.y,
info.rotation);
}
}
WindowMetrics wm = windowContext.getSystemService(WindowManager.class)
.getCurrentWindowMetrics();
Rect insets = new Rect();
normalizeWindowInsets(windowContext, wm.getWindowInsets(), insets);
return new WindowBounds(wm.getBounds(), insets, info.rotation);
}
/**
* Returns an updated insets, accounting for various Launcher UI specific overrides like taskbar
*/
@TargetApi(Build.VERSION_CODES.R)
public WindowInsets normalizeWindowInsets(Context context, WindowInsets oldInsets,
Rect outInsets) {
if (!Utilities.ATLEAST_R || !mTaskbarDrawnInProcess) {
outInsets.set(oldInsets.getSystemWindowInsetLeft(), oldInsets.getSystemWindowInsetTop(),
oldInsets.getSystemWindowInsetRight(), oldInsets.getSystemWindowInsetBottom());
return oldInsets;
}
WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(oldInsets);
Insets navInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
Resources systemRes = context.getResources();
Configuration config = systemRes.getConfiguration();
boolean isTablet = config.smallestScreenWidthDp > MIN_TABLET_WIDTH;
boolean isGesture = isGestureNav(context);
int bottomNav = isTablet
? 0
: (config.screenHeightDp > config.screenWidthDp
? getDimenByName(NAVBAR_HEIGHT, systemRes, 0)
: (isGesture
? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes, 0)
: 0));
Insets newNavInsets = Insets.of(navInsets.left, navInsets.top, navInsets.right, bottomNav);
insetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
insetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(), newNavInsets);
// Override the tappable insets to be 0 on the bottom for gesture nav (otherwise taskbar
// would count towards it). This is used for the bottom protection in All Apps for example.
if (isGesture) {
Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
oldTappableInsets.right, 0);
insetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
}
WindowInsets result = insetsBuilder.build();
Insets systemWindowInsets = result.getInsetsIgnoringVisibility(
WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
outInsets.set(systemWindowInsets.left, systemWindowInsets.top, systemWindowInsets.right,
systemWindowInsets.bottom);
return result;
}
/**
* Returns true if the display is an internal displays
*/
protected boolean isInternalDisplay(Display display) {
return display.getDisplayId() == Display.DEFAULT_DISPLAY;
}
/**
* Returns a list of possible WindowBounds for the display keyed on the 4 surface rotations
*/
public WindowBounds[] estimateWindowBounds(Context context, CachedDisplayInfo display) {
int densityDpi = context.getResources().getConfiguration().densityDpi;
int rotation = display.rotation;
Rect safeCutout = display.cutout;
int minSize = Math.min(display.size.x, display.size.y);
int swDp = (int) dpiFromPx(minSize, densityDpi);
Resources systemRes;
{
Configuration conf = new Configuration();
conf.smallestScreenWidthDp = swDp;
systemRes = context.createConfigurationContext(conf).getResources();
}
boolean isTablet = swDp >= MIN_TABLET_WIDTH;
boolean isTabletOrGesture = isTablet
|| (Utilities.ATLEAST_R && isGestureNav(context));
int statusBarHeight = getDimenByName("status_bar_height", systemRes, 0);
int navBarHeightPortrait, navBarHeightLandscape, navbarWidthLandscape;
navBarHeightPortrait = isTablet
? (mTaskbarDrawnInProcess
? 0 : systemRes.getDimensionPixelSize(R.dimen.taskbar_size))
: getDimenByName(NAVBAR_HEIGHT, systemRes, 0);
navBarHeightLandscape = isTablet
? (mTaskbarDrawnInProcess
? 0 : systemRes.getDimensionPixelSize(R.dimen.taskbar_size))
: (isTabletOrGesture
? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes, 0) : 0);
navbarWidthLandscape = isTabletOrGesture
? 0
: getDimenByName(NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE, systemRes, 0);
WindowBounds[] result = new WindowBounds[4];
Point tempSize = new Point();
for (int i = 0; i < 4; i++) {
int rotationChange = deltaRotation(rotation, i);
tempSize.set(display.size.x, display.size.y);
rotateSize(tempSize, rotationChange);
Rect bounds = new Rect(0, 0, tempSize.x, tempSize.y);
int navBarHeight, navbarWidth;
if (tempSize.y > tempSize.x) {
navBarHeight = navBarHeightPortrait;
navbarWidth = 0;
} else {
navBarHeight = navBarHeightLandscape;
navbarWidth = navbarWidthLandscape;
}
Rect insets = new Rect(safeCutout);
rotateRect(insets, rotationChange);
insets.top = Math.max(insets.top, statusBarHeight);
insets.bottom = Math.max(insets.bottom, navBarHeight);
if (i == Surface.ROTATION_270 || i == Surface.ROTATION_180) {
// On reverse landscape (and in rare-case when the natural orientation of the
// device is landscape), navigation bar is on the right.
insets.left = Math.max(insets.left, navbarWidth);
} else {
insets.right = Math.max(insets.right, navbarWidth);
}
result[i] = new WindowBounds(bounds, insets, i);
}
return result;
}
protected boolean isGestureNav(Context context) {
return ResourceUtils.getIntegerByName("config_navBarInteractionMode",
context.getResources(), INVALID_RESOURCE_HANDLE) == 2;
}
/**
* Returns a CachedDisplayInfo initialized for the current display
*/
@TargetApi(Build.VERSION_CODES.S)
public CachedDisplayInfo getDisplayInfo(Display display) {
int rotation = display.getRotation();
Point size = new Point();
display.getRealSize(size);
Rect cutoutRect = new Rect();
if (Utilities.ATLEAST_S) {
DisplayCutout cutout = display.getCutout();
if (cutout != null) {
cutoutRect.set(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
cutout.getSafeInsetRight(), cutout.getSafeInsetBottom());
}
}
return new CachedDisplayInfo(getDisplayId(display), size, rotation, cutoutRect);
}
/**
* Returns a unique ID representing the display
*/
protected String getDisplayId(Display display) {
return Integer.toString(display.getDisplayId());
}
/**
* Returns rotation of the display associated with the context.
*/
public int getRotation(Context context) {
Display d = null;
if (Utilities.ATLEAST_R) {
try {
d = context.getDisplay();
} catch (UnsupportedOperationException e) {
// Ignore
}
}
return d == null ? DisplayController.INSTANCE.get(context).getInfo().rotation
: d.getRotation();
}
}
@@ -22,7 +22,7 @@ import com.android.launcher3.model.data.ItemInfoWithIcon;
/**
* Views that contain {@link BubbleTextView} should implement this interface.
*/
public interface BubbleTextHolder {
public interface BubbleTextHolder extends IconLabelDotView {
BubbleTextView getBubbleText();
/**
@@ -32,4 +32,14 @@ public interface BubbleTextHolder {
*/
default void onItemInfoUpdated(ItemInfoWithIcon itemInfo) {
}
@Override
default void setIconVisible(boolean visible) {
getBubbleText().setIconVisible(visible);
}
@Override
default void setForceHideDot(boolean hide) {
getBubbleText().setForceHideDot(hide);
}
}
@@ -147,8 +147,7 @@ public class ClipIconView extends View implements ClipPathView {
* Update the icon UI to match the provided parameters during an animation frame
*/
public void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius,
int fgIconAlpha, boolean isOpening, View container, DeviceProfile dp,
boolean isVerticalBarLayout) {
int fgIconAlpha, boolean isOpening, View container, DeviceProfile dp) {
MarginLayoutParams lp = (MarginLayoutParams) container.getLayoutParams();
float dX = mIsRtl
@@ -169,7 +168,7 @@ public class ClipIconView extends View implements ClipPathView {
}
update(rect, progress, shapeProgressStart, cornerRadius, fgIconAlpha, isOpening, scale,
minSize, lp, isVerticalBarLayout, dp);
minSize, lp, dp);
container.setPivotX(0);
container.setPivotY(0);
@@ -181,7 +180,7 @@ public class ClipIconView extends View implements ClipPathView {
private void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius,
int fgIconAlpha, boolean isOpening, float scale, float minSize,
MarginLayoutParams parentLp, boolean isVerticalBarLayout, DeviceProfile dp) {
MarginLayoutParams parentLp, DeviceProfile dp) {
float dX = mIsRtl
? rect.left - (dp.widthPx - parentLp.getMarginStart() - parentLp.width)
: rect.left - parentLp.getMarginStart();
@@ -193,7 +192,7 @@ public class ClipIconView extends View implements ClipPathView {
float shapeRevealProgress = boundToRange(mapToRange(max(shapeProgressStart, progress),
shapeProgressStart, 1f, 0, toMax, LINEAR), 0, 1);
if (isVerticalBarLayout) {
if (dp.isLandscape) {
mOutline.right = (int) (rect.width() / scale);
} else {
mOutline.bottom = (int) (rect.height() / scale);
@@ -218,16 +217,16 @@ public class ClipIconView extends View implements ClipPathView {
mRevealAnimator.setCurrentFraction(shapeRevealProgress);
}
float drawableScale = (isVerticalBarLayout ? mOutline.width() : mOutline.height())
float drawableScale = (dp.isLandscape ? mOutline.width() : mOutline.height())
/ minSize;
setBackgroundDrawableBounds(drawableScale, isVerticalBarLayout);
setBackgroundDrawableBounds(drawableScale, dp.isLandscape);
if (isOpening) {
// Center align foreground
int height = mFinalDrawableBounds.height();
int width = mFinalDrawableBounds.width();
int diffY = isVerticalBarLayout ? 0
int diffY = dp.isLandscape ? 0
: (int) (((height * drawableScale) - height) / 2);
int diffX = isVerticalBarLayout ? (int) (((width * drawableScale) - width) / 2)
int diffX = dp.isLandscape ? (int) (((width * drawableScale) - width) / 2)
: 0;
sTmpRect.set(mFinalDrawableBounds);
sTmpRect.offset(diffX, diffY);
@@ -247,11 +246,11 @@ public class ClipIconView extends View implements ClipPathView {
invalidateOutline();
}
private void setBackgroundDrawableBounds(float scale, boolean isVerticalBarLayout) {
private void setBackgroundDrawableBounds(float scale, boolean isLandscape) {
sTmpRect.set(mFinalDrawableBounds);
Utilities.scaleRectAboutCenter(sTmpRect, scale);
// Since the drawable is at the top of the view, we need to offset to keep it centered.
if (isVerticalBarLayout) {
if (isLandscape) {
sTmpRect.offsetTo((int) (mFinalDrawableBounds.left * scale), sTmpRect.top);
} else {
sTmpRect.offsetTo(sTmpRect.left, (int) (mFinalDrawableBounds.top * scale));
@@ -269,7 +268,7 @@ public class ClipIconView extends View implements ClipPathView {
* Sets the icon for this view as part of initial setup
*/
public void setIcon(@Nullable Drawable drawable, int iconOffset, MarginLayoutParams lp,
boolean isOpening, boolean isVerticalBarLayout, DeviceProfile dp) {
boolean isOpening, DeviceProfile dp) {
mIsAdaptiveIcon = drawable instanceof AdaptiveIconDrawable;
if (mIsAdaptiveIcon) {
boolean isFolderIcon = drawable instanceof FolderAdaptiveIcon;
@@ -304,7 +303,7 @@ public class ClipIconView extends View implements ClipPathView {
Utilities.scaleRectAboutCenter(mStartRevealRect, IconShape.getNormalizationScale());
}
if (isVerticalBarLayout) {
if (dp.isLandscape) {
lp.width = (int) Math.max(lp.width, lp.height * dp.aspectRatio);
} else {
lp.height = (int) Math.max(lp.height, lp.width * dp.aspectRatio);
@@ -325,7 +324,7 @@ public class ClipIconView extends View implements ClipPathView {
bgDrawableStartScale = scale;
mOutline.set(0, 0, lp.width, lp.height);
}
setBackgroundDrawableBounds(bgDrawableStartScale, isVerticalBarLayout);
setBackgroundDrawableBounds(bgDrawableStartScale, dp.isLandscape);
mEndRevealRect.set(0, 0, lp.width, lp.height);
setOutlineProvider(new ViewOutlineProvider() {
@Override
@@ -43,6 +43,7 @@ import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -82,7 +83,6 @@ public class FloatingIconView extends FrameLayout implements
private final Launcher mLauncher;
private final boolean mIsRtl;
private boolean mIsVerticalBarLayout = false;
private boolean mIsOpening;
private IconLoadResult mIconLoadResult;
@@ -150,7 +150,7 @@ public class FloatingIconView extends FrameLayout implements
float shapeProgressStart, float cornerRadius, boolean isOpening) {
setAlpha(alpha);
mClipIconView.update(rect, progress, shapeProgressStart, cornerRadius, fgIconAlpha,
isOpening, this, mLauncher.getDeviceProfile(), mIsVerticalBarLayout);
isOpening, this, mLauncher.getDeviceProfile());
}
@Override
@@ -320,11 +320,11 @@ public class FloatingIconView extends FrameLayout implements
@UiThread
private void setIcon(@Nullable Drawable drawable, @Nullable Drawable badge,
@Nullable Drawable btvIcon, int iconOffset) {
final DeviceProfile dp = mLauncher.getDeviceProfile();
final InsettableFrameLayout.LayoutParams lp =
(InsettableFrameLayout.LayoutParams) getLayoutParams();
mBadge = badge;
mClipIconView.setIcon(drawable, iconOffset, lp, mIsOpening, mIsVerticalBarLayout,
mLauncher.getDeviceProfile());
mClipIconView.setIcon(drawable, iconOffset, lp, mIsOpening, dp);
if (drawable instanceof AdaptiveIconDrawable) {
final int originalHeight = lp.height;
final int originalWidth = lp.width;
@@ -332,7 +332,7 @@ public class FloatingIconView extends FrameLayout implements
mFinalDrawableBounds.set(0, 0, originalWidth, originalHeight);
float aspectRatio = mLauncher.getDeviceProfile().aspectRatio;
if (mIsVerticalBarLayout) {
if (dp.isLandscape) {
lp.width = (int) Math.max(lp.width, lp.height * aspectRatio);
} else {
lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
@@ -565,7 +565,6 @@ public class FloatingIconView extends FrameLayout implements
view.recycle();
// Init properties before getting the drawable.
view.mIsVerticalBarLayout = launcher.getDeviceProfile().isVerticalBarLayout();
view.mIsOpening = isOpening;
view.mOriginalIcon = originalView;
view.mPositionOut = positionOut;
@@ -587,7 +586,7 @@ public class FloatingIconView extends FrameLayout implements
view.matchPositionOf(launcher, originalView, isOpening, positionOut);
// We need to add it to the overlay, but keep it invisible until animation starts..
view.setVisibility(INVISIBLE);
setIconAndDotVisible(view, false);
parent.addView(view);
dragLayer.addView(view.mListenerView);
view.mListenerView.setListener(view::fastFinish);
@@ -596,16 +595,8 @@ public class FloatingIconView extends FrameLayout implements
view.mEndRunnable = null;
if (hideOriginal) {
if (isOpening) {
setIconAndDotVisible(originalView, true);
view.finish(dragLayer);
} else {
originalView.setVisibility(VISIBLE);
if (originalView instanceof IconLabelDotView) {
setIconAndDotVisible(originalView, true);
}
view.finish(dragLayer);
}
setIconAndDotVisible(originalView, true);
view.finish(dragLayer);
} else {
view.finish(dragLayer);
}
@@ -31,6 +31,7 @@ import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.AttributeSet;
import android.util.Pair;
import android.util.SparseArray;
@@ -55,7 +56,9 @@ import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.model.UserManagerState;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.views.ArrowTipView;
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.SpringRelativeLayout;
@@ -94,13 +97,17 @@ public class WidgetsFullSheet extends BaseWidgetSheet
"launcher.widgets_education_dialog_seen";
private final Rect mInsets = new Rect();
private final UserManagerState mUserManagerState = new UserManagerState();
private final boolean mHasWorkProfile;
private final SparseArray<AdapterHolder> mAdapters = new SparseArray();
private final UserHandle mCurrentUser = Process.myUserHandle();
private final Predicate<WidgetsListBaseEntry> mPrimaryWidgetsFilter =
entry -> mCurrentUser.equals(entry.mPkgItem.user);
private final Predicate<WidgetsListBaseEntry> mWorkWidgetsFilter =
mPrimaryWidgetsFilter.negate();
entry -> !mCurrentUser.equals(entry.mPkgItem.user)
&& !mUserManagerState.isUserQuiet(entry.mPkgItem.user);
@Nullable private ArrowTipView mLatestEducationalTip;
private final OnLayoutChangeListener mLayoutChangeListenerToShowTips =
new OnLayoutChangeListener() {
@@ -171,6 +178,9 @@ public class WidgetsFullSheet extends BaseWidgetSheet
: 0;
mWidgetSheetContentHorizontalPadding = 2 * resources.getDimensionPixelSize(
R.dimen.widget_cell_horizontal_padding);
mUserManagerState.init(UserCache.INSTANCE.get(context),
context.getSystemService(UserManager.class));
}
public WidgetsFullSheet(Context context, AttributeSet attrs) {
@@ -259,10 +269,15 @@ public class WidgetsFullSheet extends BaseWidgetSheet
boolean isWidgetAvailable = adapterHolder.mWidgetsListAdapter.hasVisibleEntries();
adapterHolder.mWidgetsRecyclerView.setVisibility(isWidgetAvailable ? VISIBLE : GONE);
mNoWidgetsView.setText(
adapterHolder.mAdapterType == AdapterHolder.SEARCH
? R.string.no_search_results
: R.string.no_widgets_available);
if (adapterHolder.mAdapterType == AdapterHolder.SEARCH) {
mNoWidgetsView.setText(R.string.no_search_results);
} else if (adapterHolder.mAdapterType == AdapterHolder.WORK
&& mUserManagerState.isAnyProfileQuietModeEnabled()
&& mActivityContext.getStringCache() != null) {
mNoWidgetsView.setText(mActivityContext.getStringCache().workProfilePausedTitle);
} else {
mNoWidgetsView.setText(R.string.no_widgets_available);
}
mNoWidgetsView.setVisibility(isWidgetAvailable ? GONE : VISIBLE);
}
@@ -19,7 +19,6 @@ package com.android.launcher3.uioverrides;
import android.app.Person;
import android.content.Context;
import android.content.pm.ShortcutInfo;
import android.view.Display;
import com.android.launcher3.Utilities;
@@ -31,20 +30,6 @@ public class ApiWrapper {
return Utilities.EMPTY_PERSON_ARRAY;
}
/**
* Returns true if the display is an internal displays
*/
public static boolean isInternalDisplay(Display display) {
return display.getDisplayId() == Display.DEFAULT_DISPLAY;
}
/**
* Returns a unique ID representing the display
*/
public static String getUniqueId(Display display) {
return Integer.toString(display.getDisplayId());
}
/**
* Returns the minimum space that should be left empty at the end of hotseat
*/
@@ -139,7 +139,7 @@ class DeviceProfileTest {
else
Pair(1440, 3120)
windowBounds = WindowBounds(x, y, x, y - 100)
windowBounds = WindowBounds(x, y, x, y - 100, 0)
`when`(info.isTablet(any())).thenReturn(false)
`when`(info.isLargeTablet(any())).thenReturn(false)
@@ -153,7 +153,7 @@ class DeviceProfileTest {
else
Pair(1600, 2560)
windowBounds = WindowBounds(x, y, x, y - 100)
windowBounds = WindowBounds(x, y, x, y - 100, 0)
`when`(info.isTablet(any())).thenReturn(true)
`when`(info.isLargeTablet(any())).thenReturn(false)
@@ -167,7 +167,7 @@ class DeviceProfileTest {
else
Pair(1600, 2560)
windowBounds = WindowBounds(x, y, x, y - 100)
windowBounds = WindowBounds(x, y, x, y - 100, 0)
`when`(info.isTablet(any())).thenReturn(true)
`when`(info.isLargeTablet(any())).thenReturn(true)
@@ -67,6 +67,7 @@ import com.android.launcher3.pm.UserCache;
import com.android.launcher3.testing.TestInformationProvider;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.launcher3.widget.custom.CustomWidgetManager;
import org.mockito.ArgumentCaptor;
@@ -501,7 +502,7 @@ public class LauncherModelHelper {
LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
DisplayController.INSTANCE, CustomWidgetManager.INSTANCE,
SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE,
ItemInstallQueue.INSTANCE);
ItemInstallQueue.INSTANCE, WindowManagerProxy.INSTANCE);
mPm = spy(getBaseContext().getPackageManager());
mDbDir = new File(getCacheDir(), UUID.randomUUID().toString());
}