mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-27 23:36:47 +00:00
Merge changes I7c303e66,Idc03b9d3 into main
* changes: Add floatingMaskView when animating to mimic bottom container. Add private_space_floating_mask_view flag.
This commit is contained in:
committed by
Android (Google) Code Review
commit
e369bd6502
@@ -42,3 +42,11 @@ flag {
|
||||
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"
|
||||
}
|
||||
|
||||
30
res/drawable/bg_ps_mask_left_corner.xml
Normal file
30
res/drawable/bg_ps_mask_left_corner.xml
Normal file
@@ -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>
|
||||
30
res/drawable/bg_ps_mask_right_corner.xml
Normal file
30
res/drawable/bg_ps_mask_right_corner.xml
Normal file
@@ -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>
|
||||
55
res/layout/private_space_mask_view.xml
Normal file
55
res/layout/private_space_mask_view.xml
Normal file
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
65
src/com/android/launcher3/allapps/FloatingMaskView.java
Normal file
65
src/com/android/launcher3/allapps/FloatingMaskView.java
Normal file
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,7 @@ import android.widget.TextView;
|
||||
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 @@ public class PrivateProfileManager extends UserProfileManager {
|
||||
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 @@ public class PrivateProfileManager extends UserProfileManager {
|
||||
private Runnable mOnPSHeaderAdded;
|
||||
@Nullable
|
||||
private RelativeLayout mPSHeader;
|
||||
private ConstraintLayout mFloatingMaskView;
|
||||
private final String mLockedStateContentDesc;
|
||||
private final String mUnLockedStateContentDesc;
|
||||
|
||||
@@ -142,6 +147,8 @@ public class PrivateProfileManager extends UserProfileManager {
|
||||
.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 @@ public class PrivateProfileManager extends UserProfileManager {
|
||||
.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 @@ public class PrivateProfileManager extends UserProfileManager {
|
||||
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 @@ public class PrivateProfileManager extends UserProfileManager {
|
||||
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 @@ public class PrivateProfileManager extends UserProfileManager {
|
||||
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 @@ public class PrivateProfileManager extends UserProfileManager {
|
||||
}
|
||||
}));
|
||||
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 @@ public class PrivateProfileManager extends UserProfileManager {
|
||||
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 @@ public class PrivateProfileManager extends UserProfileManager {
|
||||
});
|
||||
}
|
||||
|
||||
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 @@ public class PrivateProfileManager extends UserProfileManager {
|
||||
allAppsRecyclerView.addOnScrollListener(mOnIdleScrollListener);
|
||||
}
|
||||
|
||||
private float getFloatingMaskViewHeight() {
|
||||
return mFloatingMaskViewCornerRadius + getMainRecyclerView().getPaddingBottom();
|
||||
}
|
||||
|
||||
AllAppsRecyclerView getMainRecyclerView() {
|
||||
return mAllApps.mAH.get(ActivityAllAppsContainerView.AdapterHolder.MAIN).mRecyclerView;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user