Snap for 10402374 from e2e1b79b06 to udc-qpr1-release
Change-Id: Ifd4e536ba2b1ff6d482eefbd88ed8558bf6f9b85
This commit is contained in:
@@ -20,7 +20,6 @@
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.launcher3">
|
||||
<uses-sdk android:targetSdkVersion="33" android:minSdkVersion="26"/>
|
||||
<!--
|
||||
Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
|
||||
Refer comments around specific entries on how to extend individual components.
|
||||
|
||||
@@ -13,155 +13,163 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:theme="@style/GestureTutorialActivity"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="@dimen/gesture_tutorial_menu_padding_top"
|
||||
android:paddingBottom="@dimen/gesture_tutorial_menu_padding_bottom"
|
||||
android:paddingHorizontal="@dimen/gesture_tutorial_menu_padding_horizontal"
|
||||
android:background="?androidprv:attr/materialColorSurfaceContainer">
|
||||
android:background="?androidprv:attr/materialColorSurfaceContainer"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/gesture_tutorial_menu_home_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
|
||||
android:layout_marginEnd="@dimen/gesture_tutorial_menu_button_spacing"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:background="@drawable/gesture_tutorial_menu_home_button_background"
|
||||
android:clipToOutline="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="@dimen/gesture_tutorial_menu_padding_top"
|
||||
android:paddingBottom="@dimen/gesture_tutorial_menu_padding_bottom"
|
||||
android:paddingHorizontal="@dimen/gesture_tutorial_menu_padding_horizontal"
|
||||
android:clipToPadding="false">
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/guideline"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/gesture_tutorial_menu_back_button">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/gesture_tutorial_home_step_shape"
|
||||
android:scaleType="fitXY"
|
||||
android:adjustViewBounds="true"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.GestureTutorial.MenuButton.Home"
|
||||
android:id="@+id/gesture_tutorial_menu_home_button_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/home_gesture_tutorial_title"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/gesture_tutorial_menu_home_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
|
||||
android:layout_marginEnd="@dimen/gesture_tutorial_menu_button_spacing"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:background="@drawable/gesture_tutorial_menu_home_button_background"
|
||||
android:clipToOutline="true"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/guideline"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/gesture_tutorial_menu_back_button">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/gesture_tutorial_home_step_shape"
|
||||
android:scaleType="fitXY"
|
||||
android:adjustViewBounds="true"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.GestureTutorial.MenuButton.Home"
|
||||
android:id="@+id/gesture_tutorial_menu_home_button_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/home_gesture_tutorial_title"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/gesture_tutorial_menu_back_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
|
||||
android:layout_marginEnd="@dimen/gesture_tutorial_menu_button_spacing"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:background="@drawable/gesture_tutorial_menu_back_button_background"
|
||||
android:clipToOutline="true"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/guideline"
|
||||
app:layout_constraintStart_toEndOf="@id/gesture_tutorial_menu_home_button"
|
||||
app:layout_constraintEnd_toStartOf="@id/gesture_tutorial_menu_overview_button">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/gesture_tutorial_back_step_shape"
|
||||
android:layout_marginBottom="@dimen/gesture_tutorial_menu_back_shape_bottom_margin"
|
||||
android:scaleType="fitXY"
|
||||
android:adjustViewBounds="true"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.GestureTutorial.MenuButton.Back"
|
||||
android:id="@+id/gesture_tutorial_menu_back_button_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/back_gesture_tutorial_title"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/gesture_tutorial_menu_overview_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:background="@drawable/gesture_tutorial_menu_overview_button_background"
|
||||
android:clipToOutline="true"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/guideline"
|
||||
app:layout_constraintStart_toEndOf="@id/gesture_tutorial_menu_back_button"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/gesture_tutorial_overview_step_shape"
|
||||
android:scaleType="fitXY"
|
||||
android:adjustViewBounds="true"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.GestureTutorial.MenuButton.Overview"
|
||||
android:id="@+id/gesture_tutorial_menu_overview_button_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/overview_gesture_tutorial_title"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
|
||||
app:layout_constraintGuide_end="@dimen/gesture_tutorial_menu_done_button_spacing"/>
|
||||
|
||||
<Button
|
||||
style="@style/TextAppearance.GestureTutorial.ButtonLabel"
|
||||
android:id="@+id/gesture_tutorial_menu_done_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:text="@string/gesture_tutorial_action_button_label"
|
||||
android:background="@drawable/gesture_tutorial_action_button_background"
|
||||
android:backgroundTint="?androidprv:attr/materialColorPrimary"
|
||||
android:stateListAnimator="@null"
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@id/guideline"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/gesture_tutorial_menu_back_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
|
||||
android:layout_marginEnd="@dimen/gesture_tutorial_menu_button_spacing"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:background="@drawable/gesture_tutorial_menu_back_button_background"
|
||||
android:clipToOutline="true"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/guideline"
|
||||
app:layout_constraintStart_toEndOf="@id/gesture_tutorial_menu_home_button"
|
||||
app:layout_constraintEnd_toStartOf="@id/gesture_tutorial_menu_overview_button">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/gesture_tutorial_back_step_shape"
|
||||
android:layout_marginBottom="@dimen/gesture_tutorial_menu_back_shape_bottom_margin"
|
||||
android:scaleType="fitXY"
|
||||
android:adjustViewBounds="true"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.GestureTutorial.MenuButton.Back"
|
||||
android:id="@+id/gesture_tutorial_menu_back_button_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/back_gesture_tutorial_title"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/gesture_tutorial_menu_overview_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:background="@drawable/gesture_tutorial_menu_overview_button_background"
|
||||
android:clipToOutline="true"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/guideline"
|
||||
app:layout_constraintStart_toEndOf="@id/gesture_tutorial_menu_back_button"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/gesture_tutorial_overview_step_shape"
|
||||
android:scaleType="fitXY"
|
||||
android:adjustViewBounds="true"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.GestureTutorial.MenuButton.Overview"
|
||||
android:id="@+id/gesture_tutorial_menu_overview_button_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/overview_gesture_tutorial_title"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
|
||||
app:layout_constraintGuide_end="@dimen/gesture_tutorial_menu_done_button_spacing"/>
|
||||
|
||||
<Button
|
||||
style="@style/TextAppearance.GestureTutorial.ButtonLabel"
|
||||
android:id="@+id/gesture_tutorial_menu_done_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:text="@string/gesture_tutorial_action_button_label"
|
||||
android:background="@drawable/gesture_tutorial_action_button_background"
|
||||
android:backgroundTint="?androidprv:attr/materialColorPrimary"
|
||||
android:stateListAnimator="@null"
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@id/guideline"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</FrameLayout>
|
||||
@@ -41,6 +41,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
android:fitsSystemWindows="true"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
@@ -95,7 +96,6 @@
|
||||
style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:text="@string/allset_hint"
|
||||
android:textSize="@dimen/allset_page_swipe_up_text_size"
|
||||
android:gravity="center_horizontal"
|
||||
|
||||
@@ -13,154 +13,161 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:theme="@style/GestureTutorialActivity"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="@dimen/gesture_tutorial_menu_padding_top"
|
||||
android:paddingBottom="@dimen/gesture_tutorial_menu_padding_bottom"
|
||||
android:paddingHorizontal="@dimen/gesture_tutorial_menu_padding_horizontal"
|
||||
android:background="?androidprv:attr/materialColorSurfaceContainer"
|
||||
android:clipToPadding="false">
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/gesture_tutorial_menu_home_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
|
||||
android:background="@drawable/gesture_tutorial_menu_home_button_background"
|
||||
android:clipToOutline="true"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="@dimen/gesture_tutorial_menu_padding_top"
|
||||
android:paddingBottom="@dimen/gesture_tutorial_menu_padding_bottom"
|
||||
android:paddingHorizontal="@dimen/gesture_tutorial_menu_padding_horizontal"
|
||||
android:clipToPadding="false">
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/gesture_tutorial_menu_back_button"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/gesture_tutorial_home_step_shape"
|
||||
android:scaleType="fitXY"
|
||||
android:adjustViewBounds="true"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.GestureTutorial.MenuButton.Home"
|
||||
android:id="@+id/gesture_tutorial_menu_home_button_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/home_gesture_tutorial_title"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/gesture_tutorial_menu_home_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
|
||||
android:background="@drawable/gesture_tutorial_menu_home_button_background"
|
||||
android:clipToOutline="true"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/gesture_tutorial_menu_back_button"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/gesture_tutorial_home_step_shape"
|
||||
android:scaleType="fitXY"
|
||||
android:adjustViewBounds="true"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.GestureTutorial.MenuButton.Home"
|
||||
android:id="@+id/gesture_tutorial_menu_home_button_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/home_gesture_tutorial_title"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/gesture_tutorial_menu_back_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
|
||||
android:layout_marginTop="@dimen/gesture_tutorial_menu_button_spacing"
|
||||
android:background="@drawable/gesture_tutorial_menu_back_button_background"
|
||||
android:clipToOutline="true"
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_menu_home_button"
|
||||
app:layout_constraintBottom_toTopOf="@id/gesture_tutorial_menu_overview_button"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/gesture_tutorial_back_step_shape"
|
||||
android:scaleType="fitXY"
|
||||
android:adjustViewBounds="true"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.GestureTutorial.MenuButton.Back"
|
||||
android:id="@+id/gesture_tutorial_menu_back_button_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/back_gesture_tutorial_title"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/gesture_tutorial_menu_overview_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
|
||||
android:layout_marginTop="@dimen/gesture_tutorial_menu_button_spacing"
|
||||
android:background="@drawable/gesture_tutorial_menu_overview_button_background"
|
||||
android:clipToOutline="true"
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_menu_back_button"
|
||||
app:layout_constraintBottom_toTopOf="@id/guideline"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/gesture_tutorial_overview_step_shape"
|
||||
android:scaleType="fitXY"
|
||||
android:adjustViewBounds="true"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.GestureTutorial.MenuButton.Overview"
|
||||
android:id="@+id/gesture_tutorial_menu_overview_button_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/overview_gesture_tutorial_title"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
|
||||
app:layout_constraintGuide_end="@dimen/gesture_tutorial_menu_done_button_spacing"/>
|
||||
|
||||
<Button
|
||||
style="@style/TextAppearance.GestureTutorial.ButtonLabel"
|
||||
android:id="@+id/gesture_tutorial_menu_done_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:text="@string/gesture_tutorial_action_button_label"
|
||||
android:background="@drawable/gesture_tutorial_action_button_background"
|
||||
android:backgroundTint="?androidprv:attr/materialColorPrimary"
|
||||
android:stateListAnimator="@null"
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@id/guideline"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/gesture_tutorial_menu_back_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
|
||||
android:layout_marginTop="@dimen/gesture_tutorial_menu_button_spacing"
|
||||
android:background="@drawable/gesture_tutorial_menu_back_button_background"
|
||||
android:clipToOutline="true"
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_menu_home_button"
|
||||
app:layout_constraintBottom_toTopOf="@id/gesture_tutorial_menu_overview_button"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/gesture_tutorial_back_step_shape"
|
||||
android:scaleType="fitXY"
|
||||
android:adjustViewBounds="true"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"/>
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.GestureTutorial.MenuButton.Back"
|
||||
android:id="@+id/gesture_tutorial_menu_back_button_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/back_gesture_tutorial_title"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/gesture_tutorial_menu_overview_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
|
||||
android:layout_marginTop="@dimen/gesture_tutorial_menu_button_spacing"
|
||||
android:background="@drawable/gesture_tutorial_menu_overview_button_background"
|
||||
android:clipToOutline="true"
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_menu_back_button"
|
||||
app:layout_constraintBottom_toTopOf="@id/guideline"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/gesture_tutorial_overview_step_shape"
|
||||
android:scaleType="fitXY"
|
||||
android:adjustViewBounds="true"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.GestureTutorial.MenuButton.Overview"
|
||||
android:id="@+id/gesture_tutorial_menu_overview_button_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/overview_gesture_tutorial_title"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
|
||||
app:layout_constraintGuide_end="@dimen/gesture_tutorial_menu_done_button_spacing"/>
|
||||
|
||||
<Button
|
||||
style="@style/TextAppearance.GestureTutorial.ButtonLabel"
|
||||
android:id="@+id/gesture_tutorial_menu_done_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:text="@string/gesture_tutorial_action_button_label"
|
||||
android:background="@drawable/gesture_tutorial_action_button_background"
|
||||
android:backgroundTint="?androidprv:attr/materialColorPrimary"
|
||||
android:stateListAnimator="@null"
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@id/guideline"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</FrameLayout>
|
||||
@@ -144,6 +144,7 @@ import com.android.quickstep.util.StaggeredWorkspaceAnim;
|
||||
import com.android.quickstep.util.SurfaceTransaction;
|
||||
import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
|
||||
import com.android.quickstep.util.SurfaceTransactionApplier;
|
||||
import com.android.quickstep.util.TaskRestartedDuringLaunchListener;
|
||||
import com.android.quickstep.util.WorkspaceRevealAnim;
|
||||
import com.android.quickstep.views.FloatingWidgetView;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
@@ -299,6 +300,12 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
||||
boolean fromRecents = isLaunchingFromRecents(v, null /* targets */);
|
||||
RunnableList onEndCallback = new RunnableList();
|
||||
|
||||
// Handle the case where an already visible task is launched which results in no transition
|
||||
TaskRestartedDuringLaunchListener restartedListener =
|
||||
new TaskRestartedDuringLaunchListener();
|
||||
restartedListener.register(onEndCallback::executeAllAndDestroy);
|
||||
onEndCallback.add(restartedListener::unregister);
|
||||
|
||||
mAppLaunchRunner = new AppLaunchAnimationRunner(v, onEndCallback);
|
||||
ItemInfo tag = (ItemInfo) v.getTag();
|
||||
if (tag != null && tag.shouldUseBackgroundAnimation()) {
|
||||
|
||||
@@ -20,7 +20,10 @@ import static android.app.PendingIntent.FLAG_MUTABLE;
|
||||
import static android.app.PendingIntent.FLAG_ONE_SHOT;
|
||||
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
|
||||
import static com.android.launcher3.Utilities.allowBGLaunch;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.PendingIntent.CanceledException;
|
||||
import android.content.Context;
|
||||
@@ -91,9 +94,10 @@ public class StartActivityParams implements Parcelable {
|
||||
}
|
||||
|
||||
public void deliverResult(Context context, int resultCode, Intent data) {
|
||||
ActivityOptions options = allowBGLaunch(ActivityOptions.makeBasic());
|
||||
try {
|
||||
if (mPICallback != null) {
|
||||
mPICallback.send(context, resultCode, data);
|
||||
mPICallback.send(context, resultCode, data, null, null, null, options.toBundle());
|
||||
}
|
||||
} catch (CanceledException e) {
|
||||
Log.e(TAG, "Unable to send back result", e);
|
||||
|
||||
@@ -579,6 +579,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
|
||||
Executors.MAIN_EXECUTOR.getHandler(), null,
|
||||
elapsedRealTime -> callbacks.executeAllAndDestroy());
|
||||
options.setSplashScreenStyle(splashScreenStyle);
|
||||
options.setPendingIntentBackgroundActivityStartMode(
|
||||
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
|
||||
return new ActivityOptionsWrapper(options, callbacks);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun
|
||||
private var showingArrow: Boolean = false
|
||||
private var arrowDrawable: ShapeDrawable
|
||||
|
||||
var width: Float = 0f
|
||||
|
||||
init {
|
||||
paint.color = context.getColor(R.color.taskbar_background)
|
||||
paint.flags = Paint.ANTI_ALIAS_FLAG
|
||||
@@ -59,8 +61,11 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun
|
||||
pointerSize = res.getDimension(R.dimen.bubblebar_pointer_size)
|
||||
|
||||
shadowAlpha =
|
||||
if (Utilities.isDarkTheme(context)) DARK_THEME_SHADOW_ALPHA
|
||||
else LIGHT_THEME_SHADOW_ALPHA
|
||||
if (Utilities.isDarkTheme(context)) {
|
||||
DARK_THEME_SHADOW_ALPHA
|
||||
} else {
|
||||
LIGHT_THEME_SHADOW_ALPHA
|
||||
}
|
||||
|
||||
arrowDrawable =
|
||||
ShapeDrawable(TriangleShape.create(pointerSize, pointerSize, /* pointUp= */ true))
|
||||
@@ -102,7 +107,7 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun
|
||||
// Draw background.
|
||||
val radius = backgroundHeight / 2f
|
||||
canvas.drawRoundRect(
|
||||
0f,
|
||||
canvas.width.toFloat() - width,
|
||||
0f,
|
||||
canvas.width.toFloat(),
|
||||
canvas.height.toFloat(),
|
||||
@@ -132,4 +137,8 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun
|
||||
override fun setColorFilter(colorFilter: ColorFilter?) {
|
||||
paint.colorFilter = colorFilter
|
||||
}
|
||||
|
||||
fun setArrowAlpha(alpha: Int) {
|
||||
arrowDrawable.paint.alpha = alpha
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.android.launcher3.taskbar.bubbles;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
@@ -66,8 +67,8 @@ public class BubbleBarView extends FrameLayout {
|
||||
// if it's smaller than 5.
|
||||
private static final int MAX_BUBBLES = 5;
|
||||
private static final int ARROW_POSITION_ANIMATION_DURATION_MS = 200;
|
||||
private static final int WIDTH_ANIMATION_DURATION_MS = 200;
|
||||
|
||||
private final TaskbarActivityContext mActivityContext;
|
||||
private final BubbleBarBackground mBubbleBarBackground;
|
||||
|
||||
// The current bounds of all the bubble bar.
|
||||
@@ -90,6 +91,10 @@ public class BubbleBarView extends FrameLayout {
|
||||
|
||||
private final Rect mTempRect = new Rect();
|
||||
|
||||
// An animator that represents the expansion state of the bubble bar, where 0 corresponds to the
|
||||
// collapsed state and 1 to the fully expanded state.
|
||||
private final ValueAnimator mWidthAnimator = ValueAnimator.ofFloat(0, 1);
|
||||
|
||||
// We don't reorder the bubbles when they are expanded as it could be jarring for the user
|
||||
// this runnable will be populated with any reordering of the bubbles that should be applied
|
||||
// once they are collapsed.
|
||||
@@ -110,7 +115,7 @@ public class BubbleBarView extends FrameLayout {
|
||||
|
||||
public BubbleBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
mActivityContext = ActivityContext.lookupContext(context);
|
||||
TaskbarActivityContext activityContext = ActivityContext.lookupContext(context);
|
||||
|
||||
mIconOverlapAmount = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_overlap);
|
||||
mIconSpacing = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_spacing);
|
||||
@@ -118,9 +123,39 @@ public class BubbleBarView extends FrameLayout {
|
||||
mBubbleElevation = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_elevation);
|
||||
setClipToPadding(false);
|
||||
|
||||
mBubbleBarBackground = new BubbleBarBackground(mActivityContext,
|
||||
mBubbleBarBackground = new BubbleBarBackground(activityContext,
|
||||
getResources().getDimensionPixelSize(R.dimen.bubblebar_size));
|
||||
setBackgroundDrawable(mBubbleBarBackground);
|
||||
|
||||
mWidthAnimator.setDuration(WIDTH_ANIMATION_DURATION_MS);
|
||||
mWidthAnimator.addUpdateListener(animation -> {
|
||||
updateChildrenRenderNodeProperties();
|
||||
invalidate();
|
||||
});
|
||||
mWidthAnimator.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mBubbleBarBackground.showArrow(mIsBarExpanded);
|
||||
if (!mIsBarExpanded && mReorderRunnable != null) {
|
||||
mReorderRunnable.run();
|
||||
mReorderRunnable = null;
|
||||
}
|
||||
updateWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mBubbleBarBackground.showArrow(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -146,7 +181,7 @@ public class BubbleBarView extends FrameLayout {
|
||||
return mBubbleBarBounds;
|
||||
}
|
||||
|
||||
// TODO: (b/273592694) animate it
|
||||
// TODO: (b/280605790) animate it
|
||||
@Override
|
||||
public void addView(View child, int index, ViewGroup.LayoutParams params) {
|
||||
if (getChildCount() + 1 > MAX_BUBBLES) {
|
||||
@@ -155,27 +190,55 @@ public class BubbleBarView extends FrameLayout {
|
||||
removeViewInLayout(getChildAt(getChildCount() - 2));
|
||||
}
|
||||
super.addView(child, index, params);
|
||||
updateWidth();
|
||||
}
|
||||
|
||||
// TODO: (b/283309949) animate it
|
||||
@Override
|
||||
public void removeView(View view) {
|
||||
super.removeView(view);
|
||||
updateWidth();
|
||||
}
|
||||
|
||||
private void updateWidth() {
|
||||
LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
|
||||
lp.width = (int) (mIsBarExpanded ? expandedWidth() : collapsedWidth());
|
||||
setLayoutParams(lp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the z order, positions, and badge visibility of the bubble views in the bar based
|
||||
* on the expanded state.
|
||||
*/
|
||||
// TODO: (b/273592694) animate it
|
||||
private void updateChildrenRenderNodeProperties() {
|
||||
final float widthState = (float) mWidthAnimator.getAnimatedValue();
|
||||
final float currentWidth = getWidth();
|
||||
final float expandedWidth = expandedWidth();
|
||||
final float collapsedWidth = collapsedWidth();
|
||||
int bubbleCount = getChildCount();
|
||||
final float ty = (mBubbleBarBounds.height() - mIconSize) / 2f;
|
||||
for (int i = 0; i < bubbleCount; i++) {
|
||||
BubbleView bv = (BubbleView) getChildAt(i);
|
||||
bv.setTranslationY(ty);
|
||||
|
||||
// the position of the bubble when the bar is fully expanded
|
||||
final float expandedX = i * (mIconSize + mIconSpacing);
|
||||
// the position of the bubble when the bar is fully collapsed
|
||||
final float collapsedX = i * mIconOverlapAmount;
|
||||
|
||||
if (mIsBarExpanded) {
|
||||
final float tx = i * (mIconSize + mIconSpacing);
|
||||
bv.setTranslationX(tx);
|
||||
bv.setZ(0);
|
||||
// where the bubble will end up when the animation ends
|
||||
final float targetX = currentWidth - expandedWidth + expandedX;
|
||||
bv.setTranslationX(widthState * (targetX - collapsedX) + collapsedX);
|
||||
// if we're fully expanded, set the z level to 0
|
||||
if (widthState == 1f) {
|
||||
bv.setZ(0);
|
||||
}
|
||||
bv.showBadge();
|
||||
} else {
|
||||
final float targetX = currentWidth - collapsedWidth + collapsedX;
|
||||
bv.setTranslationX(widthState * (expandedX - targetX) + targetX);
|
||||
bv.setZ((MAX_BUBBLES * mBubbleElevation) - i);
|
||||
bv.setTranslationX(i * mIconOverlapAmount);
|
||||
if (i > 0) {
|
||||
bv.hideBadge();
|
||||
} else {
|
||||
@@ -183,13 +246,33 @@ public class BubbleBarView extends FrameLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the arrow position
|
||||
final float collapsedArrowPosition = arrowPositionForSelectedWhenCollapsed();
|
||||
final float expandedArrowPosition = arrowPositionForSelectedWhenExpanded();
|
||||
final float interpolatedWidth =
|
||||
widthState * (expandedWidth - collapsedWidth) + collapsedWidth;
|
||||
if (mIsBarExpanded) {
|
||||
// when the bar is expanding, the selected bubble is always the first, so the arrow
|
||||
// always shifts with the interpolated width.
|
||||
final float arrowPosition = currentWidth - interpolatedWidth + collapsedArrowPosition;
|
||||
mBubbleBarBackground.setArrowPosition(arrowPosition);
|
||||
} else {
|
||||
final float targetPosition = currentWidth - collapsedWidth + collapsedArrowPosition;
|
||||
final float arrowPosition =
|
||||
targetPosition + widthState * (expandedArrowPosition - targetPosition);
|
||||
mBubbleBarBackground.setArrowPosition(arrowPosition);
|
||||
}
|
||||
|
||||
mBubbleBarBackground.setArrowAlpha((int) (255 * widthState));
|
||||
mBubbleBarBackground.setWidth(interpolatedWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorders the views to match the provided list.
|
||||
*/
|
||||
public void reorder(List<BubbleView> viewOrder) {
|
||||
if (isExpanded()) {
|
||||
if (isExpanded() || mWidthAnimator.isRunning()) {
|
||||
mReorderRunnable = () -> doReorder(viewOrder);
|
||||
} else {
|
||||
doReorder(viewOrder);
|
||||
@@ -249,6 +332,16 @@ public class BubbleBarView extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
private float arrowPositionForSelectedWhenExpanded() {
|
||||
final int index = indexOfChild(mSelectedBubbleView);
|
||||
return getPaddingStart() + index * (mIconSize + mIconSpacing) + mIconSize / 2f;
|
||||
}
|
||||
|
||||
private float arrowPositionForSelectedWhenCollapsed() {
|
||||
final int index = indexOfChild(mSelectedBubbleView);
|
||||
return getPaddingStart() + index * (mIconOverlapAmount) + mIconSize / 2f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnClickListener(View.OnClickListener listener) {
|
||||
mOnClickListener = listener;
|
||||
@@ -266,18 +359,16 @@ public class BubbleBarView extends FrameLayout {
|
||||
/**
|
||||
* Sets whether the bubble bar is expanded or collapsed.
|
||||
*/
|
||||
// TODO: (b/273592694) animate it
|
||||
public void setExpanded(boolean isBarExpanded) {
|
||||
if (mIsBarExpanded != isBarExpanded) {
|
||||
mIsBarExpanded = isBarExpanded;
|
||||
updateArrowForSelected(/* shouldAnimate= */ false);
|
||||
setOrUnsetClickListener();
|
||||
if (!isBarExpanded && mReorderRunnable != null) {
|
||||
mReorderRunnable.run();
|
||||
mReorderRunnable = null;
|
||||
if (isBarExpanded) {
|
||||
mWidthAnimator.start();
|
||||
} else {
|
||||
mWidthAnimator.reverse();
|
||||
}
|
||||
mBubbleBarBackground.showArrow(mIsBarExpanded);
|
||||
requestLayout(); // trigger layout to reposition views & update size for expansion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,19 +379,16 @@ public class BubbleBarView extends FrameLayout {
|
||||
return mIsBarExpanded;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
private float expandedWidth() {
|
||||
final int childCount = getChildCount();
|
||||
final float iconWidth = mIsBarExpanded
|
||||
? (childCount * (mIconSize + mIconSpacing))
|
||||
: mIconSize + ((childCount - 1) * mIconOverlapAmount);
|
||||
final int totalWidth = (int) iconWidth + getPaddingStart() + getPaddingEnd();
|
||||
setMeasuredDimension(totalWidth, MeasureSpec.getSize(heightMeasureSpec));
|
||||
final int horizontalPadding = getPaddingStart() + getPaddingEnd();
|
||||
return childCount * (mIconSize + mIconSpacing) + horizontalPadding;
|
||||
}
|
||||
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View child = getChildAt(i);
|
||||
measureChild(child, (int) mIconSize, (int) mIconSize);
|
||||
}
|
||||
private float collapsedWidth() {
|
||||
final int childCount = getChildCount();
|
||||
final int horizontalPadding = getPaddingStart() + getPaddingEnd();
|
||||
return mIconSize + ((childCount - 1) * mIconOverlapAmount) + horizontalPadding;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -344,11 +344,13 @@ public class QuickstepLauncher extends Launcher {
|
||||
|
||||
@Override
|
||||
public RunnableList startActivitySafely(View v, Intent intent, ItemInfo item) {
|
||||
// Only pause is taskbar controller is not present
|
||||
// Only pause is taskbar controller is not present until the transition (if it exists) ends
|
||||
mHotseatPredictionController.setPauseUIUpdate(getTaskbarUIController() == null);
|
||||
RunnableList result = super.startActivitySafely(v, intent, item);
|
||||
if (getTaskbarUIController() == null && result == null) {
|
||||
mHotseatPredictionController.setPauseUIUpdate(false);
|
||||
if (result == null) {
|
||||
if (getTaskbarUIController() == null) {
|
||||
mHotseatPredictionController.setPauseUIUpdate(false);
|
||||
}
|
||||
} else {
|
||||
result.add(() -> mHotseatPredictionController.setPauseUIUpdate(false));
|
||||
}
|
||||
@@ -1105,6 +1107,8 @@ public class QuickstepLauncher extends Launcher {
|
||||
activityOptions.options.setLaunchDisplayId(
|
||||
(v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId()
|
||||
: Display.DEFAULT_DISPLAY);
|
||||
activityOptions.options.setPendingIntentBackgroundActivityStartMode(
|
||||
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
|
||||
addLaunchCookie(item, activityOptions.options);
|
||||
return activityOptions;
|
||||
}
|
||||
@@ -1117,6 +1121,8 @@ public class QuickstepLauncher extends Launcher {
|
||||
Executors.MAIN_EXECUTOR.getHandler(), null,
|
||||
elapsedRealTime -> callbacks.executeAllAndDestroy());
|
||||
options.setSplashScreenStyle(splashScreenStyle);
|
||||
options.setPendingIntentBackgroundActivityStartMode(
|
||||
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
|
||||
return new ActivityOptionsWrapper(options, callbacks);
|
||||
}
|
||||
|
||||
|
||||
@@ -275,6 +275,8 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
|
||||
activityOptions.options.setLaunchDisplayId(
|
||||
(v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId()
|
||||
: Display.DEFAULT_DISPLAY);
|
||||
activityOptions.options.setPendingIntentBackgroundActivityStartMode(
|
||||
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
|
||||
mHandler.postDelayed(mAnimationStartTimeoutRunnable, RECENTS_ANIMATION_TIMEOUT);
|
||||
return activityOptions;
|
||||
}
|
||||
|
||||
@@ -473,16 +473,14 @@ public final class TaskViewUtils {
|
||||
throw new IllegalStateException(
|
||||
"Expected task to be showing, but it is " + mode);
|
||||
}
|
||||
if (change.getParent() == null) {
|
||||
throw new IllegalStateException("Initiating multi-split launch but the split"
|
||||
+ "root of " + taskId + " is already visible or has broken hierarchy.");
|
||||
}
|
||||
}
|
||||
if (taskId == initialTaskId) {
|
||||
splitRoot1 = transitionInfo.getChange(change.getParent());
|
||||
splitRoot1 = change.getParent() == null ? change :
|
||||
transitionInfo.getChange(change.getParent());
|
||||
}
|
||||
if (taskId == secondTaskId) {
|
||||
splitRoot2 = transitionInfo.getChange(change.getParent());
|
||||
splitRoot2 = change.getParent() == null ? change :
|
||||
transitionInfo.getChange(change.getParent());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -103,7 +103,8 @@ public class ProgressDelegateInputConsumer implements InputConsumer,
|
||||
mStateCallback = new MultiStateCallback(STATE_NAMES);
|
||||
mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED,
|
||||
this::endRemoteAnimation);
|
||||
mStateCallback.runOnceAtState(STATE_FLING_FINISHED, this::onFlingFinished);
|
||||
mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_FLING_FINISHED,
|
||||
this::onFlingFinished);
|
||||
|
||||
mSwipeDetector = new SingleAxisSwipeDetector(mContext, this, VERTICAL);
|
||||
mSwipeDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
|
||||
|
||||
@@ -252,6 +252,10 @@ public class AllSetActivity extends Activity {
|
||||
binder.setSwipeUpProxy(isResumed() ? this::createSwipeUpProxy : null);
|
||||
binder.setOverviewTargetChangeListener(binder::preloadOverviewForSUWAllSet);
|
||||
binder.preloadOverviewForSUWAllSet();
|
||||
TaskbarManager taskbarManager = binder.getTaskbarManager();
|
||||
if (taskbarManager != null) {
|
||||
mLauncherStartAnim = taskbarManager.createLauncherStartFromSuwAnim(MAX_SWIPE_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -327,13 +331,9 @@ public class AllSetActivity extends Activity {
|
||||
mRootView.setAlpha(alpha);
|
||||
mRootView.setTranslationY((alpha - 1) * mSwipeUpShift);
|
||||
|
||||
TaskbarManager taskbarManager = mTISBindHelper.getTaskbarManager();
|
||||
if (mLauncherStartAnim == null && taskbarManager != null) {
|
||||
mLauncherStartAnim = taskbarManager.createLauncherStartFromSuwAnim(MAX_SWIPE_DURATION);
|
||||
}
|
||||
if (mLauncherStartAnim != null) {
|
||||
mLauncherStartAnim.setPlayFraction(Utilities.mapBoundToRange(
|
||||
mSwipeProgress.value, 0, 1, 0, 1, FAST_OUT_SLOW_IN));
|
||||
mLauncherStartAnim.setPlayFraction(
|
||||
FAST_OUT_SLOW_IN.getInterpolation(mSwipeProgress.value));
|
||||
}
|
||||
maybeResumeOrPauseBackgroundAnimation();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_GESTU
|
||||
import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_TUTORIAL_TYPE;
|
||||
import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_USE_TUTORIAL_MENU;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -28,33 +27,17 @@ import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.R;
|
||||
|
||||
/** Displays the gesture nav tutorial menu. */
|
||||
public final class MenuFragment extends GestureSandboxFragment {
|
||||
|
||||
@NonNull private Rect mInsets = new Rect();
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mInsets = InvariantDeviceProfile.INSTANCE.get(getContext())
|
||||
.getDeviceProfile(getContext()).getInsets();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
View root = inflater.inflate(
|
||||
final View root = inflater.inflate(
|
||||
R.layout.gesture_tutorial_step_menu, container, false);
|
||||
|
||||
root.setPadding(
|
||||
root.getPaddingLeft() + mInsets.left,
|
||||
root.getPaddingTop() + mInsets.top,
|
||||
root.getPaddingRight() + mInsets.right,
|
||||
root.getPaddingBottom() + mInsets.bottom);
|
||||
|
||||
root.findViewById(R.id.gesture_tutorial_menu_home_button).setOnClickListener(
|
||||
v -> launchTutorialStep(TutorialController.TutorialType.HOME_NAVIGATION));
|
||||
root.findViewById(R.id.gesture_tutorial_menu_back_button).setOnClickListener(
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
|
||||
import com.android.quickstep.RecentsModel;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListeners;
|
||||
|
||||
/**
|
||||
* This class tracks the failure of a task launch through the Launcher.startActivitySafely() call,
|
||||
* in an edge case in which a task may already be visible on screen (ie. in PIP) and no transition
|
||||
* will be run in WM, which results in expected callbacks to not be processed.
|
||||
*
|
||||
* We transiently register a task stack listener during a task launch and if the restart signal is
|
||||
* received, then the registered callback will be notified.
|
||||
*/
|
||||
public class TaskRestartedDuringLaunchListener implements TaskStackChangeListener {
|
||||
|
||||
private static final String TAG = "TaskRestartedDuringLaunchListener";
|
||||
|
||||
private @NonNull Runnable mTaskRestartedCallback = null;
|
||||
|
||||
/**
|
||||
* Registers a failure listener callback if it detects a scenario in which an app launch
|
||||
* resulted in an already existing task to be "restarted".
|
||||
*/
|
||||
public void register(@NonNull Runnable taskRestartedCallback) {
|
||||
TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
|
||||
mTaskRestartedCallback = taskRestartedCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the failure listener.
|
||||
*/
|
||||
public void unregister() {
|
||||
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(this);
|
||||
mTaskRestartedCallback = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
|
||||
boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
|
||||
if (wasVisible) {
|
||||
Log.d(TAG, "Detected activity restart during launch for task=" + task.taskId);
|
||||
mTaskRestartedCallback.run();
|
||||
unregister();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,7 +164,7 @@ public class DeviceProfile {
|
||||
public int iconSizePx;
|
||||
public int iconTextSizePx;
|
||||
public int iconDrawablePaddingPx;
|
||||
public int iconDrawablePaddingOriginalPx;
|
||||
private final int mIconDrawablePaddingOriginalPx;
|
||||
public boolean iconCenterVertically;
|
||||
|
||||
public float cellScaleToFit;
|
||||
@@ -456,7 +456,7 @@ public class DeviceProfile {
|
||||
cellStyle = context.obtainStyledAttributes(R.style.CellStyleDefault,
|
||||
R.styleable.CellStyle);
|
||||
}
|
||||
iconDrawablePaddingOriginalPx = cellStyle.getDimensionPixelSize(
|
||||
mIconDrawablePaddingOriginalPx = cellStyle.getDimensionPixelSize(
|
||||
R.styleable.CellStyle_iconDrawablePadding, 0);
|
||||
cellStyle.recycle();
|
||||
|
||||
@@ -883,7 +883,6 @@ public class DeviceProfile {
|
||||
iconCenterVertically = mIsScalableGrid || mIsResponsiveGrid;
|
||||
|
||||
updateIconSize(1f, res);
|
||||
|
||||
updateWorkspacePadding();
|
||||
|
||||
// Check to see if the icons fit within the available height.
|
||||
@@ -925,6 +924,25 @@ public class DeviceProfile {
|
||||
+ cellLayoutPaddingPx.left + cellLayoutPaddingPx.right;
|
||||
}
|
||||
|
||||
private int getNormalizedIconDrawablePadding() {
|
||||
// TODO(b/235886078): workaround needed because of this bug
|
||||
// Icons are 10% larger on XML than their visual size,
|
||||
// so remove that extra space to get labels closer to the correct padding
|
||||
int iconVisibleSizePx = (int) Math.round(ICON_VISIBLE_AREA_FACTOR * iconSizePx);
|
||||
return Math.max(0, mIconDrawablePaddingOriginalPx - ((iconSizePx - iconVisibleSizePx) / 2));
|
||||
}
|
||||
|
||||
private int getNormalizedFolderChildDrawablePaddingPx(int textHeight) {
|
||||
// TODO(b/235886078): workaround needed because of this bug
|
||||
// Icons are 10% larger on XML than their visual size,
|
||||
// so remove that extra space to get labels closer to the correct padding
|
||||
int drawablePadding = (folderCellHeightPx - folderChildIconSizePx - textHeight) / 3;
|
||||
|
||||
int iconVisibleSizePx = Math.round(ICON_VISIBLE_AREA_FACTOR * folderChildIconSizePx);
|
||||
int iconSizeDiff = folderChildIconSizePx - iconVisibleSizePx;
|
||||
return Math.max(0, drawablePadding - iconSizeDiff / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updating the iconSize affects many aspects of the launcher layout, such as: iconSizePx,
|
||||
* iconTextSizePx, iconDrawablePaddingPx, cellWidth/Height, allApps* variants,
|
||||
@@ -937,43 +955,36 @@ public class DeviceProfile {
|
||||
|
||||
// Workspace
|
||||
final boolean isVerticalLayout = isVerticalBarLayout();
|
||||
iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * iconScale);
|
||||
cellLayoutBorderSpacePx = getCellLayoutBorderSpace(inv, scale);
|
||||
int cellTextAndPaddingHeight =
|
||||
iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx);
|
||||
|
||||
if (mIsResponsiveGrid) {
|
||||
int cellContentHeight = iconSizePx + cellTextAndPaddingHeight;
|
||||
|
||||
cellWidthPx = mResponsiveWidthSpec.getCellSizePx();
|
||||
cellHeightPx = mResponsiveHeightSpec.getCellSizePx();
|
||||
|
||||
if (cellWidthPx < iconSizePx) {
|
||||
// get a smaller icon size
|
||||
iconSizePx = mIconSizeSteps.getIconSmallerThan(cellWidthPx);
|
||||
// calculate new cellContentHeight
|
||||
cellContentHeight = iconSizePx + cellTextAndPaddingHeight;
|
||||
}
|
||||
|
||||
iconDrawablePaddingPx = getNormalizedIconDrawablePadding();
|
||||
int iconTextHeight = Utilities.calculateTextHeight(iconTextSizePx);
|
||||
int cellContentHeight = iconSizePx + iconDrawablePaddingPx + iconTextHeight;
|
||||
|
||||
while (iconSizePx > mIconSizeSteps.minimumIconSize()
|
||||
&& cellContentHeight > cellHeightPx) {
|
||||
int extraHeightRequired = cellContentHeight - cellHeightPx;
|
||||
int newPadding = iconDrawablePaddingPx - extraHeightRequired;
|
||||
if (newPadding >= 0) {
|
||||
// Responsive uses the padding without scaling
|
||||
iconDrawablePaddingPx = iconDrawablePaddingOriginalPx = newPadding;
|
||||
cellTextAndPaddingHeight =
|
||||
iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx);
|
||||
} else {
|
||||
iconDrawablePaddingPx -= cellContentHeight - cellHeightPx;
|
||||
if (iconDrawablePaddingPx < 0) {
|
||||
// get a smaller icon size
|
||||
iconSizePx = mIconSizeSteps.getNextLowerIconSize(iconSizePx);
|
||||
iconDrawablePaddingPx = getNormalizedIconDrawablePadding();
|
||||
}
|
||||
// calculate new cellContentHeight
|
||||
cellContentHeight = iconSizePx + cellTextAndPaddingHeight;
|
||||
cellContentHeight = iconSizePx + iconDrawablePaddingPx + iconTextHeight;
|
||||
}
|
||||
|
||||
cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2;
|
||||
} else if (mIsScalableGrid) {
|
||||
iconDrawablePaddingPx = (int) (getNormalizedIconDrawablePadding() * iconScale);
|
||||
cellWidthPx = pxFromDp(inv.minCellSize[mTypeIndex].x, mMetrics, scale);
|
||||
cellHeightPx = pxFromDp(inv.minCellSize[mTypeIndex].y, mMetrics, scale);
|
||||
|
||||
@@ -995,6 +1006,8 @@ public class DeviceProfile {
|
||||
}
|
||||
}
|
||||
|
||||
int cellTextAndPaddingHeight =
|
||||
iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx);
|
||||
int cellContentHeight = iconSizePx + cellTextAndPaddingHeight;
|
||||
if (cellHeightPx < cellContentHeight) {
|
||||
// If cellHeight no longer fit iconSize, reduce borderSpace to make cellHeight
|
||||
@@ -1030,6 +1043,7 @@ public class DeviceProfile {
|
||||
desiredWorkspaceHorizontalMarginPx =
|
||||
(int) (desiredWorkspaceHorizontalMarginOriginalPx * scale);
|
||||
} else {
|
||||
iconDrawablePaddingPx = (int) (getNormalizedIconDrawablePadding() * iconScale);
|
||||
cellWidthPx = iconSizePx + iconDrawablePaddingPx;
|
||||
cellHeightPx = (int) Math.ceil(iconSizePx * ICON_OVERLAP_FACTOR)
|
||||
+ iconDrawablePaddingPx
|
||||
@@ -1103,7 +1117,7 @@ public class DeviceProfile {
|
||||
if (mIsScalableGrid) {
|
||||
allAppsIconSizePx = pxFromDp(inv.allAppsIconSize[mTypeIndex], mMetrics);
|
||||
allAppsIconTextSizePx = pxFromSp(inv.allAppsIconTextSize[mTypeIndex], mMetrics);
|
||||
allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
|
||||
allAppsIconDrawablePaddingPx = getNormalizedIconDrawablePadding();
|
||||
allAppsCellWidthPx = pxFromDp(inv.allAppsCellSize[mTypeIndex].x, mMetrics, scale);
|
||||
|
||||
if (allAppsCellWidthPx < allAppsIconSizePx) {
|
||||
@@ -1145,7 +1159,7 @@ public class DeviceProfile {
|
||||
private void updateAllAppsWithResponsiveMeasures() {
|
||||
allAppsIconSizePx = iconSizePx;
|
||||
allAppsIconTextSizePx = iconTextSizePx;
|
||||
allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
|
||||
allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
|
||||
|
||||
allAppsBorderSpacePx = new Point(
|
||||
mAllAppsResponsiveWidthSpec.getGutterPx(),
|
||||
@@ -1279,8 +1293,7 @@ public class DeviceProfile {
|
||||
|
||||
}
|
||||
|
||||
folderChildDrawablePaddingPx = Math.max(0,
|
||||
(folderCellHeightPx - folderChildIconSizePx - textHeight) / 3);
|
||||
folderChildDrawablePaddingPx = getNormalizedFolderChildDrawablePaddingPx(textHeight);
|
||||
}
|
||||
|
||||
public void updateInsets(Rect insets) {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.launcher3;
|
||||
|
||||
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
|
||||
import static android.content.Context.RECEIVER_EXPORTED;
|
||||
|
||||
import static com.android.launcher3.LauncherPrefs.ICON_STATE;
|
||||
import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
|
||||
@@ -26,6 +27,7 @@ import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.content.pm.LauncherApps;
|
||||
@@ -38,7 +40,6 @@ import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.graphics.IconShape;
|
||||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.icons.IconProvider;
|
||||
@@ -112,8 +113,9 @@ public class LauncherAppState implements SafeCloseable {
|
||||
new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
|
||||
modelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED,
|
||||
ACTION_DEVICE_POLICY_RESOURCE_UPDATED);
|
||||
if (FeatureFlags.IS_STUDIO_BUILD) {
|
||||
modelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
|
||||
if (BuildConfig.IS_STUDIO_BUILD) {
|
||||
mContext.registerReceiver(modelChangeReceiver, new IntentFilter(ACTION_FORCE_ROLOAD),
|
||||
RECEIVER_EXPORTED);
|
||||
}
|
||||
mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver));
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MA
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.Person;
|
||||
import android.app.WallpaperManager;
|
||||
import android.content.Context;
|
||||
@@ -560,6 +561,17 @@ public final class Utilities {
|
||||
handler.sendMessage(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to allow background activity launch for the provided activity options
|
||||
*/
|
||||
public static ActivityOptions allowBGLaunch(ActivityOptions options) {
|
||||
if (ATLEAST_U) {
|
||||
options.setPendingIntentBackgroundActivityStartMode(
|
||||
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full drawable for info without any flattening or pre-processing.
|
||||
*
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.launcher3.notification;
|
||||
|
||||
import static com.android.launcher3.AbstractFloatingView.TYPE_ACTION_POPUP;
|
||||
import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS;
|
||||
import static com.android.launcher3.Utilities.allowBGLaunch;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_LAUNCH_TAP;
|
||||
|
||||
import android.app.ActivityOptions;
|
||||
@@ -26,7 +27,6 @@ import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Bundle;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.view.View;
|
||||
|
||||
@@ -103,10 +103,10 @@ public class NotificationInfo implements View.OnClickListener {
|
||||
return;
|
||||
}
|
||||
final ActivityContext context = ActivityContext.lookupContext(view.getContext());
|
||||
Bundle activityOptions = ActivityOptions.makeClipRevealAnimation(
|
||||
view, 0, 0, view.getWidth(), view.getHeight()).toBundle();
|
||||
ActivityOptions options = allowBGLaunch(ActivityOptions.makeClipRevealAnimation(
|
||||
view, 0, 0, view.getWidth(), view.getHeight()));
|
||||
try {
|
||||
intent.send(null, 0, null, null, null, null, activityOptions);
|
||||
intent.send(null, 0, null, null, null, null, options.toBundle());
|
||||
context.getStatsLogManager().logger().withItemInfo(mItemInfo)
|
||||
.log(LAUNCHER_NOTIFICATION_LAUNCH_TAP);
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
|
||||
package com.android.launcher3.pm;
|
||||
|
||||
import static com.android.launcher3.Utilities.allowBGLaunch;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
@@ -138,8 +141,10 @@ public abstract class ShortcutConfigActivityInfo implements ComponentWithLabelAn
|
||||
}
|
||||
IntentSender is = activity.getSystemService(LauncherApps.class)
|
||||
.getShortcutConfigActivityIntent(mInfo);
|
||||
ActivityOptions options = allowBGLaunch(ActivityOptions.makeBasic());
|
||||
try {
|
||||
activity.startIntentSenderForResult(is, requestCode, null, 0, 0, 0);
|
||||
activity.startIntentSenderForResult(is, requestCode, null, 0, 0, 0,
|
||||
options.toBundle());
|
||||
return true;
|
||||
} catch (IntentSender.SendIntentException e) {
|
||||
Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
package com.android.launcher3.popup;
|
||||
|
||||
import static com.android.launcher3.Utilities.allowBGLaunch;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.RemoteAction;
|
||||
import android.content.Context;
|
||||
@@ -84,6 +86,8 @@ public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> {
|
||||
final WeakReference<BaseDraggingActivity> weakTarget = new WeakReference<>(mTarget);
|
||||
final String actionIdentity = mAction.getTitle() + ", "
|
||||
+ mItemInfo.getTargetComponent().getPackageName();
|
||||
|
||||
ActivityOptions options = allowBGLaunch(ActivityOptions.makeBasic());
|
||||
try {
|
||||
if (DEBUG) Log.d(TAG, "Sending action: " + actionIdentity);
|
||||
mAction.getActionIntent().send(
|
||||
@@ -103,7 +107,9 @@ public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> {
|
||||
}
|
||||
}
|
||||
},
|
||||
MAIN_EXECUTOR.getHandler());
|
||||
MAIN_EXECUTOR.getHandler(),
|
||||
null,
|
||||
options.toBundle());
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
Log.e(TAG, "Remote action canceled: " + actionIdentity, e);
|
||||
Toast.makeText(mTarget, mTarget.getString(
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.launcher3.views;
|
||||
import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR;
|
||||
|
||||
import static com.android.launcher3.LauncherSettings.Animation.DEFAULT_NO_ICON;
|
||||
import static com.android.launcher3.Utilities.allowBGLaunch;
|
||||
import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.HIDE;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_KEYBOARD_CLOSED;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_PENDING_INTENT;
|
||||
@@ -38,7 +39,6 @@ import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.Process;
|
||||
import android.os.StrictMode;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
@@ -414,8 +414,7 @@ public interface ActivityContext {
|
||||
}
|
||||
}
|
||||
ActivityOptions options =
|
||||
ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
|
||||
|
||||
allowBGLaunch(ActivityOptions.makeClipRevealAnimation(v, left, top, width, height));
|
||||
options.setLaunchDisplayId(
|
||||
(v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId()
|
||||
: Display.DEFAULT_DISPLAY);
|
||||
@@ -427,7 +426,7 @@ public interface ActivityContext {
|
||||
* Creates a default activity option and we do not want association with any launcher element.
|
||||
*/
|
||||
default ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) {
|
||||
ActivityOptions options = ActivityOptions.makeBasic();
|
||||
ActivityOptions options = allowBGLaunch(ActivityOptions.makeBasic());
|
||||
if (Utilities.ATLEAST_T) {
|
||||
options.setSplashScreenStyle(splashScreenStyle);
|
||||
}
|
||||
|
||||
@@ -275,9 +275,15 @@ public class LauncherWidgetHolder {
|
||||
protected Bundle getConfigurationActivityOptions(@NonNull BaseDraggingActivity activity,
|
||||
int widgetId) {
|
||||
LauncherAppWidgetHostView view = mViews.get(widgetId);
|
||||
if (view == null) return null;
|
||||
if (view == null) {
|
||||
return activity.makeDefaultActivityOptions(
|
||||
-1 /* SPLASH_SCREEN_STYLE_UNDEFINED */).toBundle();
|
||||
}
|
||||
Object tag = view.getTag();
|
||||
if (!(tag instanceof ItemInfo)) return null;
|
||||
if (!(tag instanceof ItemInfo)) {
|
||||
return activity.makeDefaultActivityOptions(
|
||||
-1 /* SPLASH_SCREEN_STYLE_UNDEFINED */).toBundle();
|
||||
}
|
||||
Bundle bundle = activity.getActivityLaunchOptions(view, (ItemInfo) tag).toBundle();
|
||||
bundle.putInt(KEY_SPLASH_SCREEN_STYLE, SPLASH_SCREEN_STYLE_EMPTY);
|
||||
return bundle;
|
||||
|
||||
@@ -64,8 +64,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tinv.numColumns: 5\n" +
|
||||
"\tinv.numSearchContainerColumns: 5\n" +
|
||||
"\tminCellSize: PointF(0.0, 0.0)dp\n" +
|
||||
"\tcellWidthPx: 165.0px (62.857143dp)\n" +
|
||||
"\tcellHeightPx: 235.0px (89.52381dp)\n" +
|
||||
"\tcellWidthPx: 159.0px (60.57143dp)\n" +
|
||||
"\tcellHeightPx: 229.0px (87.2381dp)\n" +
|
||||
"\tgetCellSize().x: 207.0px (78.85714dp)\n" +
|
||||
"\tgetCellSize().y: 379.0px (144.38095dp)\n" +
|
||||
"\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
|
||||
@@ -76,14 +76,14 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tcellLayoutPaddingPx.bottom: 28.0px (10.666667dp)\n" +
|
||||
"\ticonSizePx: 147.0px (56.0dp)\n" +
|
||||
"\ticonTextSizePx: 38.0px (14.476191dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 18.0px (6.857143dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 12.0px (4.571429dp)\n" +
|
||||
"\tinv.numFolderRows: 4\n" +
|
||||
"\tinv.numFolderColumns: 4\n" +
|
||||
"\tfolderCellWidthPx: 195.0px (74.28571dp)\n" +
|
||||
"\tfolderCellHeightPx: 230.0px (87.61905dp)\n" +
|
||||
"\tfolderChildIconSizePx: 147.0px (56.0dp)\n" +
|
||||
"\tfolderChildTextSizePx: 38.0px (14.476191dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 4.0px (1.5238096dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
|
||||
"\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
|
||||
@@ -139,8 +139,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tworkspacePadding.bottom: 203.0px (77.333336dp)\n" +
|
||||
"\ticonScale: 1.0px (0.3809524dp)\n" +
|
||||
"\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
|
||||
"\textraSpace: 722.0px (275.0476dp)\n" +
|
||||
"\tunscaled extraSpace: 722.0px (275.0476dp)\n" +
|
||||
"\textraSpace: 752.0px (286.4762dp)\n" +
|
||||
"\tunscaled extraSpace: 752.0px (286.4762dp)\n" +
|
||||
"\tmaxEmptySpace: 0.0px (0.0dp)\n" +
|
||||
"\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
|
||||
"\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
|
||||
@@ -201,8 +201,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tinv.numColumns: 5\n" +
|
||||
"\tinv.numSearchContainerColumns: 5\n" +
|
||||
"\tminCellSize: PointF(0.0, 0.0)dp\n" +
|
||||
"\tcellWidthPx: 165.0px (62.857143dp)\n" +
|
||||
"\tcellHeightPx: 235.0px (89.52381dp)\n" +
|
||||
"\tcellWidthPx: 159.0px (60.57143dp)\n" +
|
||||
"\tcellHeightPx: 229.0px (87.2381dp)\n" +
|
||||
"\tgetCellSize().x: 207.0px (78.85714dp)\n" +
|
||||
"\tgetCellSize().y: 383.0px (145.90475dp)\n" +
|
||||
"\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
|
||||
@@ -213,14 +213,14 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tcellLayoutPaddingPx.bottom: 28.0px (10.666667dp)\n" +
|
||||
"\ticonSizePx: 147.0px (56.0dp)\n" +
|
||||
"\ticonTextSizePx: 38.0px (14.476191dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 18.0px (6.857143dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 12.0px (4.571429dp)\n" +
|
||||
"\tinv.numFolderRows: 4\n" +
|
||||
"\tinv.numFolderColumns: 4\n" +
|
||||
"\tfolderCellWidthPx: 195.0px (74.28571dp)\n" +
|
||||
"\tfolderCellHeightPx: 230.0px (87.61905dp)\n" +
|
||||
"\tfolderChildIconSizePx: 147.0px (56.0dp)\n" +
|
||||
"\tfolderChildTextSizePx: 38.0px (14.476191dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 4.0px (1.5238096dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
|
||||
"\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
|
||||
@@ -276,8 +276,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tworkspacePadding.bottom: 245.0px (93.333336dp)\n" +
|
||||
"\ticonScale: 1.0px (0.3809524dp)\n" +
|
||||
"\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
|
||||
"\textraSpace: 743.0px (283.0476dp)\n" +
|
||||
"\tunscaled extraSpace: 743.0px (283.0476dp)\n" +
|
||||
"\textraSpace: 773.0px (294.4762dp)\n" +
|
||||
"\tunscaled extraSpace: 773.0px (294.4762dp)\n" +
|
||||
"\tmaxEmptySpace: 0.0px (0.0dp)\n" +
|
||||
"\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
|
||||
"\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
|
||||
@@ -338,7 +338,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tinv.numColumns: 5\n" +
|
||||
"\tinv.numSearchContainerColumns: 5\n" +
|
||||
"\tminCellSize: PointF(0.0, 0.0)dp\n" +
|
||||
"\tcellWidthPx: 158.0px (60.190475dp)\n" +
|
||||
"\tcellWidthPx: 152.0px (57.904762dp)\n" +
|
||||
"\tcellHeightPx: 166.0px (63.238094dp)\n" +
|
||||
"\tgetCellSize().x: 368.0px (140.19048dp)\n" +
|
||||
"\tgetCellSize().y: 193.0px (73.52381dp)\n" +
|
||||
@@ -357,7 +357,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tfolderCellHeightPx: 205.0px (78.09524dp)\n" +
|
||||
"\tfolderChildIconSizePx: 131.0px (49.904762dp)\n" +
|
||||
"\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 9.0px (3.4285715dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 4.0px (1.5238096dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
|
||||
"\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
|
||||
@@ -475,7 +475,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tinv.numColumns: 5\n" +
|
||||
"\tinv.numSearchContainerColumns: 5\n" +
|
||||
"\tminCellSize: PointF(0.0, 0.0)dp\n" +
|
||||
"\tcellWidthPx: 158.0px (60.190475dp)\n" +
|
||||
"\tcellWidthPx: 152.0px (57.904762dp)\n" +
|
||||
"\tcellHeightPx: 166.0px (63.238094dp)\n" +
|
||||
"\tgetCellSize().x: 393.0px (149.71428dp)\n" +
|
||||
"\tgetCellSize().y: 180.0px (68.57143dp)\n" +
|
||||
@@ -494,7 +494,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tfolderCellHeightPx: 192.0px (73.14286dp)\n" +
|
||||
"\tfolderChildIconSizePx: 123.0px (46.857143dp)\n" +
|
||||
"\tfolderChildTextSizePx: 32.0px (12.190476dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 8.0px (3.047619dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 3.0px (1.1428572dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
|
||||
"\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
|
||||
@@ -625,14 +625,14 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tcellLayoutPaddingPx.bottom: 59.0px (29.5dp)\n" +
|
||||
"\ticonSizePx: 120.0px (60.0dp)\n" +
|
||||
"\ticonTextSizePx: 28.0px (14.0dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 14.0px (7.0dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 9.0px (4.5dp)\n" +
|
||||
"\tinv.numFolderRows: 3\n" +
|
||||
"\tinv.numFolderColumns: 3\n" +
|
||||
"\tfolderCellWidthPx: 240.0px (120.0dp)\n" +
|
||||
"\tfolderCellHeightPx: 208.0px (104.0dp)\n" +
|
||||
"\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
|
||||
"\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 16.0px (8.0dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 11.0px (5.5dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
|
||||
"\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" +
|
||||
@@ -649,7 +649,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tallAppsCloseDuration: 500\n" +
|
||||
"\tallAppsIconSizePx: 120.0px (60.0dp)\n" +
|
||||
"\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" +
|
||||
"\tallAppsIconDrawablePaddingPx: 14.0px (7.0dp)\n" +
|
||||
"\tallAppsIconDrawablePaddingPx: 9.0px (4.5dp)\n" +
|
||||
"\tallAppsCellHeightPx: 284.0px (142.0dp)\n" +
|
||||
"\tallAppsCellWidthPx: 252.0px (126.0dp)\n" +
|
||||
"\tallAppsBorderSpacePxX: 32.0px (16.0dp)\n" +
|
||||
@@ -763,14 +763,14 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tcellLayoutPaddingPx.bottom: 59.0px (29.5dp)\n" +
|
||||
"\ticonSizePx: 120.0px (60.0dp)\n" +
|
||||
"\ticonTextSizePx: 28.0px (14.0dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 14.0px (7.0dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 9.0px (4.5dp)\n" +
|
||||
"\tinv.numFolderRows: 3\n" +
|
||||
"\tinv.numFolderColumns: 3\n" +
|
||||
"\tfolderCellWidthPx: 240.0px (120.0dp)\n" +
|
||||
"\tfolderCellHeightPx: 208.0px (104.0dp)\n" +
|
||||
"\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
|
||||
"\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 16.0px (8.0dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 11.0px (5.5dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
|
||||
"\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" +
|
||||
@@ -787,7 +787,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tallAppsCloseDuration: 500\n" +
|
||||
"\tallAppsIconSizePx: 120.0px (60.0dp)\n" +
|
||||
"\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" +
|
||||
"\tallAppsIconDrawablePaddingPx: 14.0px (7.0dp)\n" +
|
||||
"\tallAppsIconDrawablePaddingPx: 9.0px (4.5dp)\n" +
|
||||
"\tallAppsCellHeightPx: 284.0px (142.0dp)\n" +
|
||||
"\tallAppsCellWidthPx: 252.0px (126.0dp)\n" +
|
||||
"\tallAppsBorderSpacePxX: 32.0px (16.0dp)\n" +
|
||||
@@ -901,14 +901,14 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tcellLayoutPaddingPx.bottom: 72.0px (36.0dp)\n" +
|
||||
"\ticonSizePx: 120.0px (60.0dp)\n" +
|
||||
"\ticonTextSizePx: 28.0px (14.0dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 14.0px (7.0dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 9.0px (4.5dp)\n" +
|
||||
"\tinv.numFolderRows: 3\n" +
|
||||
"\tinv.numFolderColumns: 3\n" +
|
||||
"\tfolderCellWidthPx: 204.0px (102.0dp)\n" +
|
||||
"\tfolderCellHeightPx: 240.0px (120.0dp)\n" +
|
||||
"\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
|
||||
"\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 27.0px (13.5dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 22.0px (11.0dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
|
||||
"\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" +
|
||||
@@ -925,7 +925,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tallAppsCloseDuration: 500\n" +
|
||||
"\tallAppsIconSizePx: 120.0px (60.0dp)\n" +
|
||||
"\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" +
|
||||
"\tallAppsIconDrawablePaddingPx: 14.0px (7.0dp)\n" +
|
||||
"\tallAppsIconDrawablePaddingPx: 9.0px (4.5dp)\n" +
|
||||
"\tallAppsCellHeightPx: 316.0px (158.0dp)\n" +
|
||||
"\tallAppsCellWidthPx: 192.0px (96.0dp)\n" +
|
||||
"\tallAppsBorderSpacePxX: 16.0px (8.0dp)\n" +
|
||||
@@ -1039,14 +1039,14 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tcellLayoutPaddingPx.bottom: 72.0px (36.0dp)\n" +
|
||||
"\ticonSizePx: 120.0px (60.0dp)\n" +
|
||||
"\ticonTextSizePx: 28.0px (14.0dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 14.0px (7.0dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 9.0px (4.5dp)\n" +
|
||||
"\tinv.numFolderRows: 3\n" +
|
||||
"\tinv.numFolderColumns: 3\n" +
|
||||
"\tfolderCellWidthPx: 204.0px (102.0dp)\n" +
|
||||
"\tfolderCellHeightPx: 240.0px (120.0dp)\n" +
|
||||
"\tfolderChildIconSizePx: 120.0px (60.0dp)\n" +
|
||||
"\tfolderChildTextSizePx: 28.0px (14.0dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 27.0px (13.5dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 22.0px (11.0dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
|
||||
"\tfolderContentPaddingLeftRight: 0.0px (0.0dp)\n" +
|
||||
@@ -1063,7 +1063,7 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tallAppsCloseDuration: 500\n" +
|
||||
"\tallAppsIconSizePx: 120.0px (60.0dp)\n" +
|
||||
"\tallAppsIconTextSizePx: 28.0px (14.0dp)\n" +
|
||||
"\tallAppsIconDrawablePaddingPx: 14.0px (7.0dp)\n" +
|
||||
"\tallAppsIconDrawablePaddingPx: 9.0px (4.5dp)\n" +
|
||||
"\tallAppsCellHeightPx: 316.0px (158.0dp)\n" +
|
||||
"\tallAppsCellWidthPx: 192.0px (96.0dp)\n" +
|
||||
"\tallAppsBorderSpacePxX: 16.0px (8.0dp)\n" +
|
||||
@@ -1170,8 +1170,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tinv.numColumns: 4\n" +
|
||||
"\tinv.numSearchContainerColumns: 4\n" +
|
||||
"\tminCellSize: PointF(0.0, 0.0)dp\n" +
|
||||
"\tcellWidthPx: 159.0px (60.57143dp)\n" +
|
||||
"\tcellHeightPx: 223.0px (84.95238dp)\n" +
|
||||
"\tcellWidthPx: 154.0px (58.666668dp)\n" +
|
||||
"\tcellHeightPx: 218.0px (83.04762dp)\n" +
|
||||
"\tgetCellSize().x: 270.0px (102.85714dp)\n" +
|
||||
"\tgetCellSize().y: 342.0px (130.28572dp)\n" +
|
||||
"\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
|
||||
@@ -1182,14 +1182,14 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tcellLayoutPaddingPx.bottom: 0.0px (0.0dp)\n" +
|
||||
"\ticonSizePx: 141.0px (53.714287dp)\n" +
|
||||
"\ticonTextSizePx: 34.0px (12.952381dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 18.0px (6.857143dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 13.0px (4.952381dp)\n" +
|
||||
"\tinv.numFolderRows: 3\n" +
|
||||
"\tinv.numFolderColumns: 4\n" +
|
||||
"\tfolderCellWidthPx: 189.0px (72.0dp)\n" +
|
||||
"\tfolderCellHeightPx: 219.0px (83.42857dp)\n" +
|
||||
"\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" +
|
||||
"\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 5.0px (1.9047619dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
|
||||
"\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
|
||||
@@ -1245,8 +1245,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tworkspacePadding.bottom: 330.0px (125.71429dp)\n" +
|
||||
"\ticonScale: 1.0px (0.3809524dp)\n" +
|
||||
"\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
|
||||
"\textraSpace: 478.0px (182.09525dp)\n" +
|
||||
"\tunscaled extraSpace: 478.0px (182.09525dp)\n" +
|
||||
"\textraSpace: 498.0px (189.71428dp)\n" +
|
||||
"\tunscaled extraSpace: 498.0px (189.71428dp)\n" +
|
||||
"\tmaxEmptySpace: 0.0px (0.0dp)\n" +
|
||||
"\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
|
||||
"\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
|
||||
@@ -1312,8 +1312,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tinv.numColumns: 4\n" +
|
||||
"\tinv.numSearchContainerColumns: 4\n" +
|
||||
"\tminCellSize: PointF(0.0, 0.0)dp\n" +
|
||||
"\tcellWidthPx: 159.0px (60.57143dp)\n" +
|
||||
"\tcellHeightPx: 223.0px (84.95238dp)\n" +
|
||||
"\tcellWidthPx: 154.0px (58.666668dp)\n" +
|
||||
"\tcellHeightPx: 218.0px (83.04762dp)\n" +
|
||||
"\tgetCellSize().x: 270.0px (102.85714dp)\n" +
|
||||
"\tgetCellSize().y: 342.0px (130.28572dp)\n" +
|
||||
"\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
|
||||
@@ -1324,14 +1324,14 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tcellLayoutPaddingPx.bottom: 0.0px (0.0dp)\n" +
|
||||
"\ticonSizePx: 141.0px (53.714287dp)\n" +
|
||||
"\ticonTextSizePx: 34.0px (12.952381dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 18.0px (6.857143dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 13.0px (4.952381dp)\n" +
|
||||
"\tinv.numFolderRows: 3\n" +
|
||||
"\tinv.numFolderColumns: 4\n" +
|
||||
"\tfolderCellWidthPx: 189.0px (72.0dp)\n" +
|
||||
"\tfolderCellHeightPx: 219.0px (83.42857dp)\n" +
|
||||
"\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" +
|
||||
"\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 5.0px (1.9047619dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
|
||||
"\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
|
||||
@@ -1387,8 +1387,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tworkspacePadding.bottom: 330.0px (125.71429dp)\n" +
|
||||
"\ticonScale: 1.0px (0.3809524dp)\n" +
|
||||
"\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
|
||||
"\textraSpace: 478.0px (182.09525dp)\n" +
|
||||
"\tunscaled extraSpace: 478.0px (182.09525dp)\n" +
|
||||
"\textraSpace: 498.0px (189.71428dp)\n" +
|
||||
"\tunscaled extraSpace: 498.0px (189.71428dp)\n" +
|
||||
"\tmaxEmptySpace: 0.0px (0.0dp)\n" +
|
||||
"\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
|
||||
"\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
|
||||
@@ -1454,8 +1454,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tinv.numColumns: 4\n" +
|
||||
"\tinv.numSearchContainerColumns: 4\n" +
|
||||
"\tminCellSize: PointF(0.0, 0.0)dp\n" +
|
||||
"\tcellWidthPx: 159.0px (60.57143dp)\n" +
|
||||
"\tcellHeightPx: 223.0px (84.95238dp)\n" +
|
||||
"\tcellWidthPx: 154.0px (58.666668dp)\n" +
|
||||
"\tcellHeightPx: 218.0px (83.04762dp)\n" +
|
||||
"\tgetCellSize().x: 224.0px (85.333336dp)\n" +
|
||||
"\tgetCellSize().y: 430.0px (163.80952dp)\n" +
|
||||
"\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
|
||||
@@ -1466,14 +1466,14 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tcellLayoutPaddingPx.bottom: 0.0px (0.0dp)\n" +
|
||||
"\ticonSizePx: 141.0px (53.714287dp)\n" +
|
||||
"\ticonTextSizePx: 34.0px (12.952381dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 18.0px (6.857143dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 13.0px (4.952381dp)\n" +
|
||||
"\tinv.numFolderRows: 3\n" +
|
||||
"\tinv.numFolderColumns: 4\n" +
|
||||
"\tfolderCellWidthPx: 189.0px (72.0dp)\n" +
|
||||
"\tfolderCellHeightPx: 219.0px (83.42857dp)\n" +
|
||||
"\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" +
|
||||
"\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 5.0px (1.9047619dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
|
||||
"\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
|
||||
@@ -1529,8 +1529,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tworkspacePadding.bottom: 330.0px (125.71429dp)\n" +
|
||||
"\ticonScale: 1.0px (0.3809524dp)\n" +
|
||||
"\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
|
||||
"\textraSpace: 829.0px (315.8095dp)\n" +
|
||||
"\tunscaled extraSpace: 829.0px (315.8095dp)\n" +
|
||||
"\textraSpace: 849.0px (323.42856dp)\n" +
|
||||
"\tunscaled extraSpace: 849.0px (323.42856dp)\n" +
|
||||
"\tmaxEmptySpace: 0.0px (0.0dp)\n" +
|
||||
"\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
|
||||
"\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
|
||||
@@ -1592,8 +1592,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tinv.numColumns: 4\n" +
|
||||
"\tinv.numSearchContainerColumns: 4\n" +
|
||||
"\tminCellSize: PointF(0.0, 0.0)dp\n" +
|
||||
"\tcellWidthPx: 159.0px (60.57143dp)\n" +
|
||||
"\tcellHeightPx: 223.0px (84.95238dp)\n" +
|
||||
"\tcellWidthPx: 154.0px (58.666668dp)\n" +
|
||||
"\tcellHeightPx: 218.0px (83.04762dp)\n" +
|
||||
"\tgetCellSize().x: 224.0px (85.333336dp)\n" +
|
||||
"\tgetCellSize().y: 430.0px (163.80952dp)\n" +
|
||||
"\tcellLayoutBorderSpacePx Horizontal: 0.0px (0.0dp)\n" +
|
||||
@@ -1604,14 +1604,14 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tcellLayoutPaddingPx.bottom: 0.0px (0.0dp)\n" +
|
||||
"\ticonSizePx: 141.0px (53.714287dp)\n" +
|
||||
"\ticonTextSizePx: 34.0px (12.952381dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 18.0px (6.857143dp)\n" +
|
||||
"\ticonDrawablePaddingPx: 13.0px (4.952381dp)\n" +
|
||||
"\tinv.numFolderRows: 3\n" +
|
||||
"\tinv.numFolderColumns: 4\n" +
|
||||
"\tfolderCellWidthPx: 189.0px (72.0dp)\n" +
|
||||
"\tfolderCellHeightPx: 219.0px (83.42857dp)\n" +
|
||||
"\tfolderChildIconSizePx: 141.0px (53.714287dp)\n" +
|
||||
"\tfolderChildTextSizePx: 34.0px (12.952381dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 10.0px (3.8095238dp)\n" +
|
||||
"\tfolderChildDrawablePaddingPx: 5.0px (1.9047619dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.x: 0.0px (0.0dp)\n" +
|
||||
"\tfolderCellLayoutBorderSpacePx.y: 0.0px (0.0dp)\n" +
|
||||
"\tfolderContentPaddingLeftRight: 21.0px (8.0dp)\n" +
|
||||
@@ -1667,8 +1667,8 @@ class DeviceProfileDumpTest : AbstractDeviceProfileTest() {
|
||||
"\tworkspacePadding.bottom: 330.0px (125.71429dp)\n" +
|
||||
"\ticonScale: 1.0px (0.3809524dp)\n" +
|
||||
"\tcellScaleToFit : 1.0px (0.3809524dp)\n" +
|
||||
"\textraSpace: 829.0px (315.8095dp)\n" +
|
||||
"\tunscaled extraSpace: 829.0px (315.8095dp)\n" +
|
||||
"\textraSpace: 849.0px (323.42856dp)\n" +
|
||||
"\tunscaled extraSpace: 849.0px (323.42856dp)\n" +
|
||||
"\tmaxEmptySpace: 0.0px (0.0dp)\n" +
|
||||
"\tworkspaceTopPadding: 0.0px (0.0dp)\n" +
|
||||
"\tworkspaceBottomPadding: 0.0px (0.0dp)\n" +
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.viewcapture_analysis;
|
||||
|
||||
import static com.android.launcher3.util.viewcapture_analysis.ViewCaptureAnalyzer.diagPathFromRoot;
|
||||
|
||||
import com.android.launcher3.util.viewcapture_analysis.ViewCaptureAnalyzer.AnalysisNode;
|
||||
import com.android.launcher3.util.viewcapture_analysis.ViewCaptureAnalyzer.AnomalyDetector;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Anomaly detector that triggers an error when alpha of a view changes too rapidly.
|
||||
* Invisible views are treated as if they had zero alpha.
|
||||
*/
|
||||
final class AlphaJumpDetector extends AnomalyDetector {
|
||||
// Paths of nodes that are excluded from analysis.
|
||||
private static final Collection<String> PATHS_TO_IGNORE = Set.of(
|
||||
"DecorView|LinearLayout|FrameLayout:id/content|LauncherRootView:id/launcher|DragLayer"
|
||||
+ ":id/drag_layer|SearchContainerView:id/apps_view|SearchRecyclerView:id"
|
||||
+ "/search_results_list_view|SearchResultSmallIconRow",
|
||||
"DecorView|LinearLayout|FrameLayout:id/content|LauncherRootView:id/launcher|DragLayer"
|
||||
+ ":id/drag_layer|SearchContainerView:id/apps_view|SearchRecyclerView:id"
|
||||
+ "/search_results_list_view|SearchResultIcon",
|
||||
"DecorView|LinearLayout|FrameLayout:id/content|LauncherRootView:id/launcher|DragLayer"
|
||||
+ ":id/drag_layer|LauncherRecentsView:id/overview_panel|TaskView",
|
||||
"DecorView|LinearLayout|FrameLayout:id/content|LauncherRootView:id/launcher|DragLayer"
|
||||
+ ":id/drag_layer|WidgetsFullSheet|SpringRelativeLayout:id/container"
|
||||
+ "|WidgetsRecyclerView:id/primary_widgets_list_view|WidgetsListHeader:id"
|
||||
+ "/widgets_list_header",
|
||||
"DecorView|LinearLayout|FrameLayout:id/content|LauncherRootView:id/launcher|DragLayer"
|
||||
+ ":id/drag_layer|WidgetsFullSheet|SpringRelativeLayout:id/container"
|
||||
+ "|WidgetsRecyclerView:id/primary_widgets_list_view"
|
||||
+ "|StickyHeaderLayout$EmptySpaceView",
|
||||
"DecorView|LinearLayout|FrameLayout:id/content|LauncherRootView:id/launcher|DragLayer"
|
||||
+ ":id/drag_layer|SearchContainerView:id/apps_view|AllAppsRecyclerView:id"
|
||||
+ "/apps_list_view|BubbleTextView:id/icon",
|
||||
"DecorView|LinearLayout|FrameLayout:id/content|LauncherRootView:id/launcher|DragLayer"
|
||||
+ ":id/drag_layer|LauncherRecentsView:id/overview_panel|ClearAllButton:id"
|
||||
+ "/clear_all",
|
||||
"DecorView|LinearLayout|FrameLayout:id/content|LauncherRootView:id/launcher|DragLayer"
|
||||
+ ":id/drag_layer|NexusOverviewActionsView:id/overview_actions_view"
|
||||
+ "|LinearLayout:id/action_buttons"
|
||||
);
|
||||
// Minimal increase or decrease of view's alpha between frames that triggers the error.
|
||||
private static final float ALPHA_JUMP_THRESHOLD = 1f;
|
||||
|
||||
@Override
|
||||
void initializeNode(AnalysisNode info) {
|
||||
// If the parent view ignores alpha jumps, its descendants will too.
|
||||
final boolean parentIgnoreAlphaJumps = info.parent != null && info.parent.ignoreAlphaJumps;
|
||||
info.ignoreAlphaJumps = parentIgnoreAlphaJumps
|
||||
|| PATHS_TO_IGNORE.contains(diagPathFromRoot(info));
|
||||
}
|
||||
|
||||
@Override
|
||||
void detectAnomalies(AnalysisNode oldInfo, AnalysisNode newInfo, int frameN) {
|
||||
// If the view was previously seen, proceed with analysis only if it was present in the
|
||||
// view hierarchy in the previous frame.
|
||||
if (oldInfo != null && oldInfo.frameN != frameN) return;
|
||||
|
||||
final AnalysisNode latestInfo = newInfo != null ? newInfo : oldInfo;
|
||||
if (latestInfo.ignoreAlphaJumps) return;
|
||||
|
||||
final float oldAlpha = oldInfo != null ? oldInfo.alpha : 0;
|
||||
final float newAlpha = newInfo != null ? newInfo.alpha : 0;
|
||||
final float alphaDeltaAbs = Math.abs(newAlpha - oldAlpha);
|
||||
|
||||
if (alphaDeltaAbs >= ALPHA_JUMP_THRESHOLD) {
|
||||
throw new AssertionError(
|
||||
String.format(
|
||||
"Alpha jump detected in ViewCapture data: alpha change: %s (%s -> %s)"
|
||||
+ ", threshold: %s, view: %s",
|
||||
alphaDeltaAbs, oldAlpha, newAlpha, ALPHA_JUMP_THRESHOLD, latestInfo));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.viewcapture_analysis;
|
||||
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.app.viewcapture.data.ExportedData;
|
||||
import com.android.app.viewcapture.data.FrameData;
|
||||
import com.android.app.viewcapture.data.ViewNode;
|
||||
import com.android.app.viewcapture.data.WindowData;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Utility that analyzes ViewCapture data and finds anomalies such as views appearing or
|
||||
* disappearing without alpha-fading.
|
||||
*/
|
||||
public class ViewCaptureAnalyzer {
|
||||
private static final String SCRIM_VIEW_CLASS = "com.android.launcher3.views.ScrimView";
|
||||
|
||||
/**
|
||||
* Detector of one kind of anomaly.
|
||||
*/
|
||||
abstract static class AnomalyDetector {
|
||||
/**
|
||||
* Initializes fields of the node that are specific to the anomaly detected by this
|
||||
* detector.
|
||||
*/
|
||||
abstract void initializeNode(@NonNull AnalysisNode info);
|
||||
|
||||
/**
|
||||
* Detects anomalies by looking at the last occurrence of a view, and the current one.
|
||||
* null value means that the view. 'oldInfo' and 'newInfo' cannot be both null.
|
||||
* If an anomaly is detected, an exception will be thrown.
|
||||
*
|
||||
* @param oldInfo the view, as seen in the last frame that contained it in the view
|
||||
* hierarchy before 'currentFrame'. 'null' means that the view is first seen
|
||||
* in the 'currentFrame'.
|
||||
* @param newInfo the view in the view hierarchy of the 'currentFrame'. 'null' means that
|
||||
* the view is not present in the 'currentFrame', but was present in earlier
|
||||
* frames.
|
||||
* @param frameN number of the current frame.
|
||||
*/
|
||||
abstract void detectAnomalies(
|
||||
@Nullable AnalysisNode oldInfo, @Nullable AnalysisNode newInfo, int frameN);
|
||||
}
|
||||
|
||||
// All detectors. They will be invoked in the order listed here.
|
||||
private static final Iterable<AnomalyDetector> ANOMALY_DETECTORS = Arrays.asList(
|
||||
new AlphaJumpDetector()
|
||||
);
|
||||
|
||||
// A view from view capture data converted to a form that's convenient for detecting anomalies.
|
||||
static class AnalysisNode {
|
||||
public String className;
|
||||
public String resourceId;
|
||||
public AnalysisNode parent;
|
||||
|
||||
// Window coordinates of the view.
|
||||
public float left;
|
||||
public float top;
|
||||
|
||||
// Visible scale and alpha, build recursively from the ancestor list.
|
||||
public float scaleX;
|
||||
public float scaleY;
|
||||
public float alpha;
|
||||
|
||||
public int frameN;
|
||||
public ViewNode viewCaptureNode;
|
||||
|
||||
public boolean ignoreAlphaJumps;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("window coordinates: (%s, %s), class path from the root: %s",
|
||||
left, top, diagPathFromRoot(this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a view capture record and throws an error if an anomaly is found.
|
||||
*/
|
||||
public static void assertNoAnomalies(ExportedData viewCaptureData) {
|
||||
final int scrimClassIndex = viewCaptureData.getClassnameList().indexOf(SCRIM_VIEW_CLASS);
|
||||
|
||||
final int windowDataCount = viewCaptureData.getWindowDataCount();
|
||||
for (int i = 0; i < windowDataCount; ++i) {
|
||||
analyzeWindowData(viewCaptureData, viewCaptureData.getWindowData(i), scrimClassIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void analyzeWindowData(ExportedData viewCaptureData, WindowData windowData,
|
||||
int scrimClassIndex) {
|
||||
// View hash code => Last seen node with this hash code.
|
||||
// The view is added when we analyze the first frame where it's visible.
|
||||
// After that, it gets updated for every frame where it's visible.
|
||||
// As we go though frames, if a view becomes invisible, it stays in the map.
|
||||
final Map<Integer, AnalysisNode> lastSeenNodes = new HashMap<>();
|
||||
|
||||
for (int frameN = 0; frameN < windowData.getFrameDataCount(); ++frameN) {
|
||||
analyzeFrame(frameN, windowData.getFrameData(frameN), viewCaptureData, lastSeenNodes,
|
||||
scrimClassIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void analyzeFrame(int frameN, FrameData frame, ExportedData viewCaptureData,
|
||||
Map<Integer, AnalysisNode> lastSeenNodes, int scrimClassIndex) {
|
||||
// Analyze the node tree starting from the root.
|
||||
analyzeView(
|
||||
frame.getNode(),
|
||||
/* parent = */ null,
|
||||
frameN,
|
||||
/* leftShift = */ 0,
|
||||
/* topShift = */ 0,
|
||||
viewCaptureData,
|
||||
lastSeenNodes,
|
||||
scrimClassIndex);
|
||||
|
||||
// Analyze transitions when a view visible in the last frame become invisible in the
|
||||
// current one.
|
||||
for (AnalysisNode info : lastSeenNodes.values()) {
|
||||
if (info.frameN == frameN - 1) {
|
||||
if (!info.viewCaptureNode.getWillNotDraw()) {
|
||||
ANOMALY_DETECTORS.forEach(
|
||||
detector -> detector.detectAnomalies(
|
||||
/* oldInfo = */ info,
|
||||
/* newInfo = */ null,
|
||||
frameN));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void analyzeView(ViewNode viewCaptureNode, AnalysisNode parent, int frameN,
|
||||
float leftShift, float topShift, ExportedData viewCaptureData,
|
||||
Map<Integer, AnalysisNode> lastSeenNodes, int scrimClassIndex) {
|
||||
// Skip analysis of invisible views
|
||||
final float parentAlpha = parent != null ? parent.alpha : 1;
|
||||
final float alpha = getVisibleAlpha(viewCaptureNode, parentAlpha);
|
||||
if (alpha <= 0.0) return;
|
||||
|
||||
// Calculate analysis node parameters
|
||||
final int hashcode = viewCaptureNode.getHashcode();
|
||||
final int classIndex = viewCaptureNode.getClassnameIndex();
|
||||
|
||||
final float parentScaleX = parent != null ? parent.scaleX : 1;
|
||||
final float parentScaleY = parent != null ? parent.scaleY : 1;
|
||||
final float scaleX = parentScaleX * viewCaptureNode.getScaleX();
|
||||
final float scaleY = parentScaleY * viewCaptureNode.getScaleY();
|
||||
|
||||
final float left = leftShift
|
||||
+ (viewCaptureNode.getLeft() + viewCaptureNode.getTranslationX()) * parentScaleX
|
||||
+ viewCaptureNode.getWidth() * (parentScaleX - scaleX) / 2;
|
||||
final float top = topShift
|
||||
+ (viewCaptureNode.getTop() + viewCaptureNode.getTranslationY()) * parentScaleY
|
||||
+ viewCaptureNode.getHeight() * (parentScaleY - scaleY) / 2;
|
||||
|
||||
// Initialize new analysis node
|
||||
final AnalysisNode newAnalysisNode = new AnalysisNode();
|
||||
newAnalysisNode.className = viewCaptureData.getClassname(classIndex);
|
||||
newAnalysisNode.resourceId = viewCaptureNode.getId();
|
||||
newAnalysisNode.parent = parent;
|
||||
newAnalysisNode.left = left;
|
||||
newAnalysisNode.top = top;
|
||||
newAnalysisNode.scaleX = scaleX;
|
||||
newAnalysisNode.scaleY = scaleY;
|
||||
newAnalysisNode.alpha = alpha;
|
||||
newAnalysisNode.frameN = frameN;
|
||||
newAnalysisNode.viewCaptureNode = viewCaptureNode;
|
||||
ANOMALY_DETECTORS.forEach(detector -> detector.initializeNode(newAnalysisNode));
|
||||
|
||||
// Detect anomalies for the view
|
||||
final AnalysisNode oldAnalysisNode = lastSeenNodes.get(hashcode); // may be null
|
||||
if (frameN != 0 && !viewCaptureNode.getWillNotDraw()) {
|
||||
ANOMALY_DETECTORS.forEach(
|
||||
detector -> detector.detectAnomalies(oldAnalysisNode, newAnalysisNode, frameN));
|
||||
}
|
||||
lastSeenNodes.put(hashcode, newAnalysisNode);
|
||||
|
||||
// Enumerate children starting from the topmost one. Stop at ScrimView, if present.
|
||||
final float leftShiftForChildren = left - viewCaptureNode.getScrollX();
|
||||
final float topShiftForChildren = top - viewCaptureNode.getScrollY();
|
||||
for (int i = viewCaptureNode.getChildrenCount() - 1; i >= 0; --i) {
|
||||
final ViewNode child = viewCaptureNode.getChildren(i);
|
||||
|
||||
// Don't analyze anything under scrim view because we don't know whether it's
|
||||
// transparent.
|
||||
if (child.getClassnameIndex() == scrimClassIndex) break;
|
||||
|
||||
analyzeView(child, newAnalysisNode, frameN, leftShiftForChildren, topShiftForChildren,
|
||||
viewCaptureData, lastSeenNodes,
|
||||
scrimClassIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private static float getVisibleAlpha(ViewNode node, float parenVisibleAlpha) {
|
||||
return node.getVisibility() == VISIBLE
|
||||
? parenVisibleAlpha * Math.max(0, Math.min(node.getAlpha(), 1))
|
||||
: 0f;
|
||||
}
|
||||
|
||||
private static String classNameToSimpleName(String className) {
|
||||
return className.substring(className.lastIndexOf(".") + 1);
|
||||
}
|
||||
|
||||
static String diagPathFromRoot(AnalysisNode nodeBox) {
|
||||
final StringBuilder path = new StringBuilder(diagPathElement(nodeBox));
|
||||
for (AnalysisNode ancestor = nodeBox.parent; ancestor != null; ancestor = ancestor.parent) {
|
||||
path.insert(0, diagPathElement(ancestor) + "|");
|
||||
}
|
||||
return path.toString();
|
||||
}
|
||||
|
||||
private static String diagPathElement(AnalysisNode nodeBox) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(classNameToSimpleName(nodeBox.className));
|
||||
if (!"NO_ID".equals(nodeBox.resourceId)) sb.append(":" + nodeBox.resourceId);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user