Merge changes I7c303e66,Idc03b9d3 into main
* changes:
Add floatingMaskView when animating to mimic bottom container.
Add private_space_floating_mask_view flag.
diff --git a/aconfig/launcher_search.aconfig b/aconfig/launcher_search.aconfig
index 31d8d34..b243922 100644
--- a/aconfig/launcher_search.aconfig
+++ b/aconfig/launcher_search.aconfig
@@ -42,3 +42,11 @@
description: "This flag disables drag and drop for Private Space Items."
bug: "289223923"
}
+
+
+flag {
+ name: "private_space_floating_mask_view"
+ namespace: "launcher_search"
+ description: "This flag enables the floating mask view as part of the Private Space animation. "
+ bug: "339850589"
+}
diff --git a/res/drawable/bg_ps_mask_left_corner.xml b/res/drawable/bg_ps_mask_left_corner.xml
new file mode 100644
index 0000000..43eeedb
--- /dev/null
+++ b/res/drawable/bg_ps_mask_left_corner.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <vector
+ android:viewportWidth="28"
+ android:viewportHeight="28"
+ android:width="@dimen/ps_floating_mask_corner_radius"
+ android:height="@dimen/ps_floating_mask_corner_radius">
+ <path
+ android:pathData="M0 28H28C24.3228 28 20.6821 27.2759 17.2847 25.8687C13.8877 24.4614 10.8013 22.3989 8.20117 19.7988C5.60107 17.1987 3.53857 14.1123 2.13135 10.7153C0.724121 7.31787 0 3.67725 0 0V28Z"
+ android:fillType="evenOdd"
+ android:fillColor="?attr/allAppsScrimColor" />
+ </vector>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/bg_ps_mask_right_corner.xml b/res/drawable/bg_ps_mask_right_corner.xml
new file mode 100644
index 0000000..d63b866
--- /dev/null
+++ b/res/drawable/bg_ps_mask_right_corner.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <vector
+ android:viewportWidth="28"
+ android:viewportHeight="28"
+ android:width="@dimen/ps_floating_mask_corner_radius"
+ android:height="@dimen/ps_floating_mask_corner_radius">
+ <path
+ android:pathData="M28 28V0C28 3.67725 27.2759 7.31787 25.8687 10.7153C24.4614 14.1123 22.3989 17.1987 19.7988 19.7988C17.1987 22.3989 14.1123 24.4614 10.7153 25.8687C7.31787 27.2759 3.67725 28 0 28H28Z"
+ android:fillType="evenOdd"
+ android:fillColor="?attr/allAppsScrimColor" />
+ </vector>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/layout/private_space_mask_view.xml b/res/layout/private_space_mask_view.xml
new file mode 100644
index 0000000..44e2797
--- /dev/null
+++ b/res/layout/private_space_mask_view.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<com.android.launcher3.allapps.FloatingMaskView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_marginLeft="@dimen/ps_floating_mask_end_padding"
+ android:layout_marginRight="@dimen/ps_floating_mask_end_padding"
+ android:importantForAccessibility="noHideDescendants"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/left_corner"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintStart_toStartOf="parent"
+ android:importantForAccessibility="no"
+ android:background="@drawable/bg_ps_mask_left_corner"/>
+
+ <ImageView
+ android:id="@+id/right_corner"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scaleType="centerCrop"
+ app:layout_constraintEnd_toEndOf="parent"
+ android:importantForAccessibility="no"
+ android:background="@drawable/bg_ps_mask_right_corner"/>
+
+ <ImageView
+ android:id="@+id/bottom_box"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:layout_constraintStart_toStartOf="@id/left_corner"
+ app:layout_constraintEnd_toEndOf="@id/right_corner"
+ app:layout_constraintTop_toBottomOf="@id/left_corner"
+ android:importantForAccessibility="no"
+ android:background="?attr/allAppsScrimColor"/>
+
+</com.android.launcher3.allapps.FloatingMaskView>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 54edab4..74fadda 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -536,6 +536,8 @@
<dimen name="ps_lock_icon_text_margin_start_expanded">8dp</dimen>
<dimen name="ps_lock_icon_text_margin_end_expanded">6dp</dimen>
<dimen name="ps_lock_button_background_padding">10dp</dimen>
+ <dimen name="ps_floating_mask_corner_radius">28dp</dimen>
+ <dimen name="ps_floating_mask_end_padding">16dp</dimen>
<!-- WindowManagerProxy -->
<dimen name="max_width_and_height_of_small_display_cutout">136px</dimen>
diff --git a/src/com/android/launcher3/allapps/FloatingMaskView.java b/src/com/android/launcher3/allapps/FloatingMaskView.java
new file mode 100644
index 0000000..606eb03
--- /dev/null
+++ b/src/com/android/launcher3/allapps/FloatingMaskView.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.allapps;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.launcher3.R;
+import com.android.launcher3.views.ActivityContext;
+
+public class FloatingMaskView extends ConstraintLayout {
+
+ private final ActivityContext mActivityContext;
+ private ImageView mBottomBox;
+
+ public FloatingMaskView(Context context) {
+ this(context, null, 0);
+ }
+
+ public FloatingMaskView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public FloatingMaskView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mActivityContext = ActivityContext.lookupContext(context);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mBottomBox = findViewById(R.id.bottom_box);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) getLayoutParams();
+ AllAppsRecyclerView allAppsContainerView =
+ mActivityContext.getAppsView().getActiveRecyclerView();
+ if (lp != null) {
+ lp.rightMargin = allAppsContainerView.getPaddingRight();
+ lp.leftMargin = allAppsContainerView.getPaddingLeft();
+ mBottomBox.setMinimumHeight(allAppsContainerView.getPaddingBottom());
+ }
+ }
+}
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index 53f9cfc..27340a3 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -60,6 +60,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView;
@@ -96,14 +97,17 @@
private static final int TEXT_UNLOCK_OPACITY_DURATION = 300;
private static final int TEXT_LOCK_OPACITY_DURATION = 50;
private static final int APP_OPACITY_DURATION = 400;
+ private static final int MASK_VIEW_DURATION = 200;
private static final int APP_OPACITY_DELAY = 400;
private static final int SETTINGS_AND_LOCK_GROUP_TRANSITION_DELAY = 400;
private static final int SETTINGS_OPACITY_DELAY = 400;
private static final int LOCK_TEXT_OPACITY_DELAY = 500;
+ private static final int MASK_VIEW_DELAY = 400;
private static final int NO_DELAY = 0;
private final ActivityAllAppsContainerView<?> mAllApps;
private final Predicate<UserHandle> mPrivateProfileMatcher;
private final int mPsHeaderHeight;
+ private final int mFloatingMaskViewCornerRadius;
private final RecyclerView.OnScrollListener mOnIdleScrollListener =
new RecyclerView.OnScrollListener() {
@Override
@@ -123,6 +127,7 @@
private Runnable mOnPSHeaderAdded;
@Nullable
private RelativeLayout mPSHeader;
+ private ConstraintLayout mFloatingMaskView;
private final String mLockedStateContentDesc;
private final String mUnLockedStateContentDesc;
@@ -142,6 +147,8 @@
.getString(R.string.ps_container_lock_button_content_description);
mUnLockedStateContentDesc = mAllApps.getContext()
.getString(R.string.ps_container_unlock_button_content_description);
+ mFloatingMaskViewCornerRadius = mAllApps.getContext().getResources().getDimensionPixelSize(
+ R.dimen.ps_floating_mask_corner_radius);
}
/** Adds Private Space Header to the layout. */
@@ -219,6 +226,7 @@
.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED);
int updatedState = isEnabled ? STATE_ENABLED : STATE_DISABLED;
setCurrentState(updatedState);
+ mFloatingMaskView = null;
if (mPSHeader != null) {
mPSHeader.setAlpha(1);
}
@@ -494,12 +502,15 @@
RecyclerView.LayoutManager layoutManager = allAppsRecyclerView.getLayoutManager();
if (layoutManager != null) {
startAnimationScroll(allAppsRecyclerView, layoutManager, smoothScroller);
- currentItem.decorationInfo = null;
+ // Preserve decorator if floating mask view exists.
+ if (mFloatingMaskView == null) {
+ currentItem.decorationInfo = null;
+ }
}
break;
}
// Make the private space apps gone to "collapse".
- if (isPrivateSpaceItem(currentItem)) {
+ if (mFloatingMaskView == null && isPrivateSpaceItem(currentItem)) {
RecyclerView.ViewHolder viewHolder =
allAppsRecyclerView.findViewHolderForAdapterPosition(i);
if (viewHolder != null) {
@@ -637,6 +648,7 @@
setAnimationRunning(false);
return;
}
+ attachFloatingMaskView(expand);
ViewGroup settingsAndLockGroup = mPSHeader.findViewById(R.id.settingsAndLockGroup);
if (settingsAndLockGroup.getLayoutTransition() == null) {
// Set a new transition if the current ViewGroup does not already contain one as each
@@ -662,6 +674,11 @@
mPSHeader.findViewById(R.id.lock_text).setVisibility(expand ? VISIBLE : GONE);
setAnimationRunning(true);
}
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ detachFloatingMaskView();
+ }
});
animatorSet.addListener(forEndCallback(() -> {
setAnimationRunning(false);
@@ -681,13 +698,17 @@
}
}));
if (expand) {
- animatorSet.playTogether(animateAlphaOfIcons(true));
+ animatorSet.playTogether(animateAlphaOfIcons(true),
+ translateFloatingMaskView(false));
} else {
if (isPrivateSpaceHidden()) {
- animatorSet.playSequentially(animateAlphaOfIcons(false),
- animateCollapseAnimation(), fadeOutHeaderAlpha());
+ animatorSet.playSequentially(translateFloatingMaskView(false),
+ animateAlphaOfIcons(false),
+ animateCollapseAnimation(),
+ fadeOutHeaderAlpha());
} else {
- animatorSet.playSequentially(animateAlphaOfIcons(false),
+ animatorSet.playSequentially(translateFloatingMaskView(true),
+ animateAlphaOfIcons(false),
animateCollapseAnimation());
}
}
@@ -715,6 +736,27 @@
return alphaAnim;
}
+ /** Fades out the private space container. */
+ private ValueAnimator translateFloatingMaskView(boolean animateIn) {
+ if (!Flags.privateSpaceFloatingMaskView() || mFloatingMaskView == null) {
+ return new ValueAnimator();
+ }
+ // Translate base on the height amount. Translates out on expand and in on collapse.
+ float floatingMaskViewHeight = getFloatingMaskViewHeight();
+ float from = animateIn ? floatingMaskViewHeight : 0;
+ float to = animateIn ? 0 : floatingMaskViewHeight;
+ ValueAnimator alphaAnim = ObjectAnimator.ofFloat(from, to);
+ alphaAnim.setDuration(MASK_VIEW_DURATION);
+ alphaAnim.setStartDelay(MASK_VIEW_DELAY);
+ alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ mFloatingMaskView.setTranslationY((float) valueAnimator.getAnimatedValue());
+ }
+ });
+ return alphaAnim;
+ }
+
/** Animates the layout changes when the text of the button becomes visible/gone. */
private void enableLayoutTransition(ViewGroup settingsAndLockGroup) {
LayoutTransition settingsAndLockTransition = new LayoutTransition();
@@ -806,6 +848,28 @@
});
}
+ private void attachFloatingMaskView(boolean expand) {
+ if (!Flags.privateSpaceFloatingMaskView()) {
+ return;
+ }
+ mFloatingMaskView = (FloatingMaskView) mAllApps.getLayoutInflater().inflate(
+ R.layout.private_space_mask_view, mAllApps, false);
+ mAllApps.addView(mFloatingMaskView);
+ // Translate off the screen first if its collapsing so this header view isn't visible to
+ // user when animation starts.
+ if (!expand) {
+ mFloatingMaskView.setTranslationY(getFloatingMaskViewHeight());
+ }
+ mFloatingMaskView.setVisibility(VISIBLE);
+ }
+
+ private void detachFloatingMaskView() {
+ if (mFloatingMaskView != null) {
+ mAllApps.removeView(mFloatingMaskView);
+ }
+ mFloatingMaskView = null;
+ }
+
/** Starts the smooth scroll with the provided smoothScroller and add idle listener. */
private void startAnimationScroll(AllAppsRecyclerView allAppsRecyclerView,
RecyclerView.LayoutManager layoutManager, RecyclerView.SmoothScroller smoothScroller) {
@@ -815,6 +879,10 @@
allAppsRecyclerView.addOnScrollListener(mOnIdleScrollListener);
}
+ private float getFloatingMaskViewHeight() {
+ return mFloatingMaskViewCornerRadius + getMainRecyclerView().getPaddingBottom();
+ }
+
AllAppsRecyclerView getMainRecyclerView() {
return mAllApps.mAH.get(ActivityAllAppsContainerView.AdapterHolder.MAIN).mRecyclerView;
}