From 803927bdae8f4db9c8ede435d95388eaa58ecb89 Mon Sep 17 00:00:00 2001 From: Stevie Kideckel Date: Mon, 14 Jun 2021 17:27:58 +0000 Subject: [PATCH 1/8] Use drawable states for list positions instead of swapping drawables Replacing drawables interrupts touch feedback of ripples. Implementing custom state lists allows altering the corner radius without cutting off ripples and also means we reduce unnecessary object creation. Fix: 190467676 Test: locally Change-Id: I10eec042aae57d278f8254327d42df945767c7ac --- res/layout/widgets_table_container.xml | 2 +- res/values/dimens.xml | 1 - ...WidgetsListHeaderViewHolderBinderTest.java | 5 +- ...sListSearchHeaderViewHolderBinderTest.java | 5 +- .../WidgetsListTableViewHolderBinderTest.java | 1 + .../widget/picker/WidgetsListAdapter.java | 13 +- .../picker/WidgetsListDrawableFactory.java | 113 ++++++++++++++++++ .../picker/WidgetsListDrawableState.java | 44 +++++++ .../widget/picker/WidgetsListDrawables.java | 60 ---------- .../widget/picker/WidgetsListHeader.java | 43 ++++--- .../WidgetsListHeaderViewHolderBinder.java | 14 ++- ...dgetsListSearchHeaderViewHolderBinder.java | 14 ++- .../widget/picker/WidgetsListTableView.java | 59 +++++++++ .../WidgetsListTableViewHolderBinder.java | 38 +++--- .../widget/picker/WidgetsRowViewHolder.java | 7 +- 15 files changed, 292 insertions(+), 127 deletions(-) create mode 100644 src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java create mode 100644 src/com/android/launcher3/widget/picker/WidgetsListDrawableState.java delete mode 100644 src/com/android/launcher3/widget/picker/WidgetsListDrawables.java create mode 100644 src/com/android/launcher3/widget/picker/WidgetsListTableView.java diff --git a/res/layout/widgets_table_container.xml b/res/layout/widgets_table_container.xml index c6b70aa4aa..4f70a05374 100644 --- a/res/layout/widgets_table_container.xml +++ b/res/layout/widgets_table_container.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -28dp 4dp - 0dp 20dp 2dp diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java index 84a03d572c..4e2a508394 100644 --- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java +++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java @@ -107,7 +107,10 @@ public final class WidgetsListHeaderViewHolderBinderTest { /* iconClickListener= */ view -> {}, /* iconLongClickListener= */ view -> false); mViewHolderBinder = new WidgetsListHeaderViewHolderBinder( - LayoutInflater.from(mTestActivity), mOnHeaderClickListener, widgetsListAdapter); + LayoutInflater.from(mTestActivity), + mOnHeaderClickListener, + new WidgetsListDrawableFactory(mTestActivity), + widgetsListAdapter); } @After diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java index 075c58db6b..d6aea554f2 100644 --- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java +++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java @@ -107,7 +107,10 @@ public final class WidgetsListSearchHeaderViewHolderBinderTest { /* iconClickListener= */ view -> {}, /* iconLongClickListener= */ view -> false); mViewHolderBinder = new WidgetsListSearchHeaderViewHolderBinder( - LayoutInflater.from(mTestActivity), mOnHeaderClickListener, widgetsListAdapter); + LayoutInflater.from(mTestActivity), + mOnHeaderClickListener, + new WidgetsListDrawableFactory(mTestActivity), + widgetsListAdapter); } @After diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java index 0c6e71711d..2f1326fdc0 100644 --- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java +++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java @@ -118,6 +118,7 @@ public final class WidgetsListTableViewHolderBinderTest { mOnIconClickListener, mOnLongClickListener, mWidgetPreviewLoader, + new WidgetsListDrawableFactory(mTestActivity), widgetsListAdapter); } diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java index e89aea7507..6863c60a37 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java @@ -103,18 +103,25 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) { mLauncher = Launcher.getLauncher(context); mDiffReporter = new WidgetsDiffReporter(iconCache, this); + WidgetsListDrawableFactory listDrawableFactory = new WidgetsListDrawableFactory(context); mWidgetsListTableViewHolderBinder = new WidgetsListTableViewHolderBinder(context, layoutInflater, iconClickListener, iconLongClickListener, - widgetPreviewLoader, /* listAdapter= */ this); + widgetPreviewLoader, listDrawableFactory, /* listAdapter= */ this); mViewHolderBinders.put(VIEW_TYPE_WIDGETS_LIST, mWidgetsListTableViewHolderBinder); mViewHolderBinders.put( VIEW_TYPE_WIDGETS_HEADER, new WidgetsListHeaderViewHolderBinder( - layoutInflater, /* onHeaderClickListener= */this, /* listAdapter= */ this)); + layoutInflater, + /* onHeaderClickListener= */ this, + listDrawableFactory, + /* listAdapter= */ this)); mViewHolderBinders.put( VIEW_TYPE_WIDGETS_SEARCH_HEADER, new WidgetsListSearchHeaderViewHolderBinder( - layoutInflater, /*onHeaderClickListener=*/ this, /* listAdapter= */ this)); + layoutInflater, + /* onHeaderClickListener= */ this, + listDrawableFactory, + /* listAdapter= */ this)); } @Override diff --git a/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java b/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java new file mode 100644 index 0000000000..c61e3a4349 --- /dev/null +++ b/src/com/android/launcher3/widget/picker/WidgetsListDrawableFactory.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.widget.picker; + +import static com.android.launcher3.widget.picker.WidgetsListDrawableState.FIRST; +import static com.android.launcher3.widget.picker.WidgetsListDrawableState.FIRST_EXPANDED; +import static com.android.launcher3.widget.picker.WidgetsListDrawableState.LAST; +import static com.android.launcher3.widget.picker.WidgetsListDrawableState.MIDDLE; +import static com.android.launcher3.widget.picker.WidgetsListDrawableState.MIDDLE_EXPANDED; +import static com.android.launcher3.widget.picker.WidgetsListDrawableState.SINGLE; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.RippleDrawable; +import android.graphics.drawable.StateListDrawable; + +import com.android.launcher3.R; +import com.android.launcher3.util.Themes; + +/** Factory for creating drawables to use as background for list elements. */ +final class WidgetsListDrawableFactory { + + private final float mTopBottomCornerRadius; + private final float mMiddleCornerRadius; + private final ColorStateList mSurfaceColor; + private final ColorStateList mRippleColor; + + WidgetsListDrawableFactory(Context context) { + Resources res = context.getResources(); + mTopBottomCornerRadius = res.getDimension(R.dimen.widget_list_top_bottom_corner_radius); + mMiddleCornerRadius = res.getDimension(R.dimen.widget_list_content_corner_radius); + mSurfaceColor = context.getColorStateList(R.color.surface); + mRippleColor = ColorStateList.valueOf( + Themes.getAttrColor(context, android.R.attr.colorControlHighlight)); + } + + /** + * Creates a drawable for widget header list items. This drawable supports all positions + * in {@link WidgetsListDrawableState}. + */ + Drawable createHeaderBackgroundDrawable() { + StateListDrawable stateList = new StateListDrawable(); + stateList.addState( + SINGLE.mStateSet, + createRoundedRectDrawable(mTopBottomCornerRadius, mTopBottomCornerRadius)); + stateList.addState( + FIRST_EXPANDED.mStateSet, + createRoundedRectDrawable(mTopBottomCornerRadius, 0)); + stateList.addState( + FIRST.mStateSet, + createRoundedRectDrawable(mTopBottomCornerRadius, mMiddleCornerRadius)); + stateList.addState( + MIDDLE_EXPANDED.mStateSet, + createRoundedRectDrawable(mMiddleCornerRadius, 0)); + stateList.addState( + MIDDLE.mStateSet, + createRoundedRectDrawable(mMiddleCornerRadius, mMiddleCornerRadius)); + stateList.addState( + LAST.mStateSet, + createRoundedRectDrawable(mMiddleCornerRadius, mTopBottomCornerRadius)); + return new RippleDrawable(mRippleColor, /* content= */ stateList, /* mask= */ stateList); + } + + /** + * Creates a drawable for widget content list items. This state list supports the middle and + * last states. + */ + Drawable createContentBackgroundDrawable() { + StateListDrawable stateList = new StateListDrawable(); + stateList.addState( + MIDDLE.mStateSet, + createRoundedRectDrawable(0, mMiddleCornerRadius)); + stateList.addState( + LAST.mStateSet, + createRoundedRectDrawable(0, mTopBottomCornerRadius)); + return new RippleDrawable(mRippleColor, /* content= */ stateList, /* mask= */ stateList); + } + + /** Creates a rounded-rect drawable with the specified radii. */ + private Drawable createRoundedRectDrawable(float topRadius, float bottomRadius) { + GradientDrawable backgroundMask = new GradientDrawable(); + backgroundMask.setColor(mSurfaceColor); + backgroundMask.setShape(GradientDrawable.RECTANGLE); + backgroundMask.setCornerRadii( + new float[]{ + topRadius, + topRadius, + topRadius, + topRadius, + bottomRadius, + bottomRadius, + bottomRadius, + bottomRadius + }); + return backgroundMask; + } +} diff --git a/src/com/android/launcher3/widget/picker/WidgetsListDrawableState.java b/src/com/android/launcher3/widget/picker/WidgetsListDrawableState.java new file mode 100644 index 0000000000..94f292beb2 --- /dev/null +++ b/src/com/android/launcher3/widget/picker/WidgetsListDrawableState.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.widget.picker; + +/** + * Different possible list position states for an item in the widgets list to have. Note that only + * headers use the expanded state. + */ +enum WidgetsListDrawableState { + FIRST(new int[]{android.R.attr.state_first}), + FIRST_EXPANDED(new int[]{android.R.attr.state_first, android.R.attr.state_expanded}), + MIDDLE(new int[]{android.R.attr.state_middle}), + MIDDLE_EXPANDED(new int[]{android.R.attr.state_middle, android.R.attr.state_expanded}), + LAST(new int[]{android.R.attr.state_last}), + SINGLE(new int[]{android.R.attr.state_single}); + + final int[] mStateSet; + + WidgetsListDrawableState(int[] stateSet) { + mStateSet = stateSet; + } + + static WidgetsListDrawableState obtain(boolean isFirst, boolean isLast, boolean isExpanded) { + if (isFirst && isLast) return SINGLE; + if (isFirst && isExpanded) return FIRST_EXPANDED; + if (isFirst) return FIRST; + if (isLast) return LAST; + if (isExpanded) return MIDDLE_EXPANDED; + return MIDDLE; + } +} diff --git a/src/com/android/launcher3/widget/picker/WidgetsListDrawables.java b/src/com/android/launcher3/widget/picker/WidgetsListDrawables.java deleted file mode 100644 index b3bb544cd7..0000000000 --- a/src/com/android/launcher3/widget/picker/WidgetsListDrawables.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.launcher3.widget.picker; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.GradientDrawable; -import android.graphics.drawable.RippleDrawable; - -import com.android.launcher3.R; -import com.android.launcher3.util.Themes; - -/** Helper class for creating drawables to use as background for list elements. */ -final class WidgetsListDrawables { - - private WidgetsListDrawables() {} - - /** Creates a list background drawable with the specified radii. */ - static Drawable createListBackgroundDrawable( - Context context, - float topRadius, - float bottomRadius) { - GradientDrawable backgroundMask = new GradientDrawable(); - backgroundMask.setColor(context.getColorStateList(R.color.surface)); - backgroundMask.setShape(GradientDrawable.RECTANGLE); - - backgroundMask.setCornerRadii( - new float[]{ - topRadius, - topRadius, - topRadius, - topRadius, - bottomRadius, - bottomRadius, - bottomRadius, - bottomRadius - }); - - return new RippleDrawable( - /* color= */ ColorStateList.valueOf( - Themes.getAttrColor(context, android.R.attr.colorControlHighlight)), - /* content= */ backgroundMask, - /* mask= */ backgroundMask); - } - -} diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java index fece359607..cdab964279 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java +++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java @@ -60,9 +60,6 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd @Nullable private Drawable mIconDrawable; private final int mIconSize; private final int mBottomMarginSize; - private final float mTopBottomCornerRadius; - private final float mMiddleCornerRadius; - private final float mJoinedCornerRadius; private ImageView mAppIcon; private TextView mTitle; @@ -70,6 +67,7 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd private CheckBox mExpandToggle; private boolean mIsExpanded = false; + @Nullable private WidgetsListDrawableState mListDrawableState; public WidgetsListHeader(Context context) { this(context, /* attrs= */ null); @@ -90,12 +88,6 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd grid.iconSizePx); mBottomMarginSize = getResources().getDimensionPixelSize(R.dimen.widget_list_entry_bottom_margin); - mTopBottomCornerRadius = - getResources().getDimension(R.dimen.widget_list_top_bottom_corner_radius); - mMiddleCornerRadius = - getResources().getDimension(R.dimen.widget_list_content_corner_radius); - mJoinedCornerRadius = - getResources().getDimension(R.dimen.widget_list_content_joined_corner_radius); } @Override @@ -163,6 +155,14 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd } } + /** Sets the {@link WidgetsListDrawableState} and refreshes the background drawable. */ + @UiThread + public void setListDrawableState(WidgetsListDrawableState state) { + if (state == mListDrawableState) return; + this.mListDrawableState = state; + refreshDrawableState(); + } + /** Apply app icon, labels and tag using a generic {@link WidgetsListHeaderEntry}. */ @UiThread public void applyFromItemInfoWithIcon(WidgetsListHeaderEntry entry) { @@ -263,20 +263,6 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd verifyHighRes(); } - /** Updates the list to have a background drawable with the appropriate corner radii. */ - @UiThread - public void updateListBackground(boolean isFirst, boolean isLast, boolean isExpanded) { - float topRadius = isFirst ? mTopBottomCornerRadius : mMiddleCornerRadius; - float bottomRadius = isLast - ? mTopBottomCornerRadius - : isExpanded - ? mJoinedCornerRadius - : mMiddleCornerRadius; - setBackground( - WidgetsListDrawables.createListBackgroundDrawable( - getContext(), topRadius, bottomRadius)); - } - private void setTitles(WidgetsListSearchHeaderEntry entry) { mTitle.setText(entry.mPkgItem.title); @@ -300,6 +286,17 @@ public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpd } } + @Override + protected int[] onCreateDrawableState(int extraSpace) { + if (mListDrawableState == null) return super.onCreateDrawableState(extraSpace); + // Augment the state set from the super implementation with the custom states from + // mListDrawableState. + int[] drawableState = + super.onCreateDrawableState(extraSpace + mListDrawableState.mStateSet.length); + mergeDrawableStates(drawableState, mListDrawableState.mStateSet); + return drawableState; + } + /** Verifies that the current icon is high-res otherwise posts a request to load the icon. */ public void verifyHighRes() { if (mIconLoadRequest != null) { diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java index 22d6d227c4..2f8f1bacfc 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java +++ b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java @@ -30,13 +30,16 @@ public final class WidgetsListHeaderViewHolderBinder implements ViewHolderBinder { private final LayoutInflater mLayoutInflater; private final OnHeaderClickListener mOnHeaderClickListener; + private final WidgetsListDrawableFactory mListDrawableFactory; private final WidgetsListAdapter mWidgetsListAdapter; public WidgetsListHeaderViewHolderBinder(LayoutInflater layoutInflater, OnHeaderClickListener onHeaderClickListener, + WidgetsListDrawableFactory listDrawableFactory, WidgetsListAdapter listAdapter) { mLayoutInflater = layoutInflater; mOnHeaderClickListener = onHeaderClickListener; + mListDrawableFactory = listDrawableFactory; mWidgetsListAdapter = listAdapter; } @@ -44,7 +47,7 @@ public final class WidgetsListHeaderViewHolderBinder implements public WidgetsListHeaderHolder newViewHolder(ViewGroup parent) { WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate( R.layout.widgets_list_row_header, parent, false); - + header.setBackground(mListDrawableFactory.createHeaderBackgroundDrawable()); return new WidgetsListHeaderHolder(header); } @@ -52,12 +55,13 @@ public final class WidgetsListHeaderViewHolderBinder implements public void bindViewHolder(WidgetsListHeaderHolder viewHolder, WidgetsListHeaderEntry data, int position) { WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader; - widgetsListHeader.updateListBackground( - /* isFirst= */ position == 0, - /* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1, - /* isExpanded= */ data.isWidgetListShown()); widgetsListHeader.applyFromItemInfoWithIcon(data); widgetsListHeader.setExpanded(data.isWidgetListShown()); + widgetsListHeader.setListDrawableState( + WidgetsListDrawableState.obtain( + /* isFirst= */ position == 0, + /* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1, + /* isExpanded= */ data.isWidgetListShown())); widgetsListHeader.setOnExpandChangeListener(isExpanded -> mOnHeaderClickListener.onHeaderClicked( isExpanded, diff --git a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java index d5e03a4fe0..31dd9ee0ae 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java +++ b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java @@ -31,13 +31,16 @@ public final class WidgetsListSearchHeaderViewHolderBinder implements ViewHolderBinder { private final LayoutInflater mLayoutInflater; private final OnHeaderClickListener mOnHeaderClickListener; + private final WidgetsListDrawableFactory mListDrawableFactory; private final WidgetsListAdapter mWidgetsListAdapter; public WidgetsListSearchHeaderViewHolderBinder(LayoutInflater layoutInflater, OnHeaderClickListener onHeaderClickListener, + WidgetsListDrawableFactory listDrawableFactory, WidgetsListAdapter listAdapter) { mLayoutInflater = layoutInflater; mOnHeaderClickListener = onHeaderClickListener; + mListDrawableFactory = listDrawableFactory; mWidgetsListAdapter = listAdapter; } @@ -45,7 +48,7 @@ public final class WidgetsListSearchHeaderViewHolderBinder implements public WidgetsListSearchHeaderHolder newViewHolder(ViewGroup parent) { WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate( R.layout.widgets_list_row_header, parent, false); - + header.setBackground(mListDrawableFactory.createHeaderBackgroundDrawable()); return new WidgetsListSearchHeaderHolder(header); } @@ -53,12 +56,13 @@ public final class WidgetsListSearchHeaderViewHolderBinder implements public void bindViewHolder(WidgetsListSearchHeaderHolder viewHolder, WidgetsListSearchHeaderEntry data, int position) { WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader; - widgetsListHeader.updateListBackground( - /* isFirst= */ position == 0, - /* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1, - /* isExpanded= */ data.isWidgetListShown()); widgetsListHeader.applyFromItemInfoWithIcon(data); widgetsListHeader.setExpanded(data.isWidgetListShown()); + widgetsListHeader.setListDrawableState( + WidgetsListDrawableState.obtain( + /* isFirst= */ position == 0, + /* isLast= */ position == mWidgetsListAdapter.getItemCount() - 1, + /* isExpanded= */ data.isWidgetListShown())); widgetsListHeader.setOnExpandChangeListener(isExpanded -> mOnHeaderClickListener.onHeaderClicked(isExpanded, new PackageUserKey(data.mPkgItem.packageName, data.mPkgItem.user))); diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableView.java b/src/com/android/launcher3/widget/picker/WidgetsListTableView.java new file mode 100644 index 0000000000..d30e7b68ae --- /dev/null +++ b/src/com/android/launcher3/widget/picker/WidgetsListTableView.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.widget.picker; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.TableLayout; + +import androidx.annotation.Nullable; +import androidx.annotation.UiThread; + +/** + * Extension of {@link TableLayout} to support the drawable states used by + * {@link WidgetsListDrawableState}. + */ +public class WidgetsListTableView extends TableLayout { + + @Nullable private WidgetsListDrawableState mListDrawableState; + + public WidgetsListTableView(Context context) { + super(context); + } + + public WidgetsListTableView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + /** Sets the {@link WidgetsListDrawableState} and refreshes the background drawable. */ + @UiThread + public void setListDrawableState(WidgetsListDrawableState state) { + if (state == mListDrawableState) return; + mListDrawableState = state; + refreshDrawableState(); + } + + @Override + protected int[] onCreateDrawableState(int extraSpace) { + if (mListDrawableState == null) return super.onCreateDrawableState(extraSpace); + // Augment the state set from the super implementation with the custom states from + // mListDrawableState. + int[] drawableState = + super.onCreateDrawableState(extraSpace + mListDrawableState.mStateSet.length); + mergeDrawableStates(drawableState, mListDrawableState.mStateSet); + return drawableState; + } +} diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java index 8e310c5d83..7e8c55bfe6 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java +++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java @@ -15,8 +15,10 @@ */ package com.android.launcher3.widget.picker; +import static com.android.launcher3.widget.picker.WidgetsListDrawableState.LAST; +import static com.android.launcher3.widget.picker.WidgetsListDrawableState.MIDDLE; + import android.content.Context; -import android.content.res.Resources; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; @@ -51,10 +53,8 @@ public final class WidgetsListTableViewHolderBinder private final OnClickListener mIconClickListener; private final OnLongClickListener mIconLongClickListener; private final WidgetPreviewLoader mWidgetPreviewLoader; + private final WidgetsListDrawableFactory mListDrawableFactory; private final WidgetsListAdapter mWidgetsListAdapter; - private final float mTopBottomCornerRadius; - private final float mMiddleCornerRadius; - private final float mJoinedCornerRadius; private boolean mApplyBitmapDeferred = false; public WidgetsListTableViewHolderBinder( @@ -63,19 +63,14 @@ public final class WidgetsListTableViewHolderBinder OnClickListener iconClickListener, OnLongClickListener iconLongClickListener, WidgetPreviewLoader widgetPreviewLoader, + WidgetsListDrawableFactory listDrawableFactory, WidgetsListAdapter listAdapter) { mLayoutInflater = layoutInflater; mIconClickListener = iconClickListener; mIconLongClickListener = iconLongClickListener; mWidgetPreviewLoader = widgetPreviewLoader; + mListDrawableFactory = listDrawableFactory; mWidgetsListAdapter = listAdapter; - Resources resources = context.getResources(); - mTopBottomCornerRadius = - resources.getDimension(R.dimen.widget_list_top_bottom_corner_radius); - mMiddleCornerRadius = - resources.getDimension(R.dimen.widget_list_content_corner_radius); - mJoinedCornerRadius = - resources.getDimension(R.dimen.widget_list_content_joined_corner_radius); } /** @@ -97,28 +92,25 @@ public final class WidgetsListTableViewHolderBinder Log.v(TAG, "\nonCreateViewHolder"); } - ViewGroup container = (ViewGroup) mLayoutInflater.inflate( - R.layout.widgets_table_container, parent, false); - return new WidgetsRowViewHolder(container); + WidgetsRowViewHolder viewHolder = + new WidgetsRowViewHolder(mLayoutInflater.inflate( + R.layout.widgets_table_container, parent, false)); + viewHolder.mTableContainer.setBackgroundDrawable( + mListDrawableFactory.createContentBackgroundDrawable()); + return viewHolder; } @Override public void bindViewHolder(WidgetsRowViewHolder holder, WidgetsListContentEntry entry, int position) { - TableLayout table = holder.mTableContainer; + WidgetsListTableView table = holder.mTableContainer; if (DEBUG) { Log.d(TAG, String.format("onBindViewHolder [widget#=%d, table.getChildCount=%d]", entry.mWidgets.size(), table.getChildCount())); } - // The content is always joined to an expanded header above. - float topRadius = mJoinedCornerRadius; - float bottomRadius = position == mWidgetsListAdapter.getItemCount() - 1 - ? mTopBottomCornerRadius - : mMiddleCornerRadius; - table.setBackgroundDrawable( - WidgetsListDrawables.createListBackgroundDrawable( - holder.itemView.getContext(), topRadius, bottomRadius)); + table.setListDrawableState( + position == mWidgetsListAdapter.getItemCount() - 1 ? LAST : MIDDLE); List> widgetItemsTable = WidgetsTableUtils.groupWidgetItemsIntoTable(entry.mWidgets, mMaxSpansPerRow); diff --git a/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java index aef1103c2c..618e2cbbd6 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java +++ b/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java @@ -15,8 +15,7 @@ */ package com.android.launcher3.widget.picker; -import android.view.ViewGroup; -import android.widget.TableLayout; +import android.view.View; import androidx.recyclerview.widget.RecyclerView.ViewHolder; @@ -25,9 +24,9 @@ import com.android.launcher3.R; /** A {@link ViewHolder} for showing widgets of an app in the full widget picker. */ public final class WidgetsRowViewHolder extends ViewHolder { - public final TableLayout mTableContainer; + public final WidgetsListTableView mTableContainer; - public WidgetsRowViewHolder(ViewGroup v) { + public WidgetsRowViewHolder(View v) { super(v); mTableContainer = v.findViewById(R.id.widgets_table); From 74a6d2ea55f65151716e0771ee68908f490d891b Mon Sep 17 00:00:00 2001 From: Thiru Ramasamy Date: Tue, 15 Jun 2021 10:38:19 -0700 Subject: [PATCH 2/8] Enable logging for 'Themed icons' setting. Eldar: http://eldar/510864786 Bug: 191161241 Test: local - http://gpaste/5934701681311744 & http://gpaste/6715297709948928 Change-Id: I18ad97f68b372997e222f06dabf8bbe83f15d92f --- .../quickstep/logging/SettingsChangeLogger.java | 11 +++++++++++ .../android/launcher3/logging/StatsLogManager.java | 8 +++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java index 4c10822fb8..7eca360224 100644 --- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java +++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java @@ -27,8 +27,11 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_DISABLED; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_ENABLED; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_THEMED_ICON_DISABLED; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_THEMED_ICON_ENABLED; import static com.android.launcher3.model.QuickstepModelDelegate.LAST_PREDICTION_ENABLED_STATE; import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI; +import static com.android.launcher3.util.Themes.KEY_THEMED_ICONS; import android.content.Context; import android.content.SharedPreferences; @@ -40,6 +43,7 @@ import android.util.Xml; import com.android.launcher3.AutoInstallsLayout; import com.android.launcher3.R; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.InstanceIdSequence; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager.StatsLogger; @@ -170,6 +174,13 @@ public class SettingsChangeLogger implements if (gridSizeChangedEvent != null) { logger.log(gridSizeChangedEvent); } + + if (FeatureFlags.ENABLE_THEMED_ICONS.get()) { + logger.log(prefs.getBoolean(KEY_THEMED_ICONS, false) + ? LAUNCHER_THEMED_ICON_ENABLED + : LAUNCHER_THEMED_ICON_DISABLED); + } + mLoggablePrefs.forEach((key, lp) -> logger.log(() -> prefs.getBoolean(key, lp.defaultValue) ? lp.eventIdOn : lp.eventIdOff)); } diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java index 418e46de71..345a2ac594 100644 --- a/src/com/android/launcher3/logging/StatsLogManager.java +++ b/src/com/android/launcher3/logging/StatsLogManager.java @@ -473,7 +473,13 @@ public class StatsLogManager implements ResourceBasedOverride { LAUNCHER_WIDGET_RESIZE_COMPLETED(824), @UiEvent(doc = "User reconfigured a widget on their home screen.") - LAUNCHER_WIDGET_RECONFIGURED(821) + LAUNCHER_WIDGET_RECONFIGURED(821), + + @UiEvent(doc = "User enabled themed icons option in wallpaper & style settings.") + LAUNCHER_THEMED_ICON_ENABLED(836), + + @UiEvent(doc = "User disabled themed icons option in wallpaper & style settings.") + LAUNCHER_THEMED_ICON_DISABLED(837) ; // ADD MORE From 9d3e31e67c509a0c9d09228ba95fdb893cf61a5a Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Tue, 15 Jun 2021 12:04:17 -0700 Subject: [PATCH 3/8] Use RectFSpringAnim for auto-enter-pip Make SwipePipToHomeAnimator a subclass of RectFSpringAnim and use the bounds calculated from latter for auto-enter-pip transition. Note that the landscape to portrait case may not fully comply to RectFSpringAnim as we calculate the position separately in SwipePipToHomeAnimator. Video: http://recall/-/aaaaaabFQoRHlzixHdtY/CWBPU2A6vB8CeTFRanQLx Bug: 184789411 Test: manual, see video Change-Id: I67dae09e682b7d8f1a1f08995eb3bcf76bdf7417 --- .../android/quickstep/AbsSwipeUpHandler.java | 27 +++---- .../util/SwipePipToHomeAnimator.java | 79 +++++++++---------- 2 files changed, 48 insertions(+), 58 deletions(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 8f42993b62..57dcad65c6 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -45,8 +45,6 @@ import static com.android.quickstep.GestureState.STATE_END_TARGET_SET; import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE; -import static com.android.quickstep.util.SwipePipToHomeAnimator.FRACTION_END; -import static com.android.quickstep.util.SwipePipToHomeAnimator.FRACTION_START; import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME; @@ -248,7 +246,6 @@ public abstract class AbsSwipeUpHandler, private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch; - private static final long SWIPE_PIP_TO_HOME_DURATION = 425; private SwipePipToHomeAnimator mSwipePipToHomeAnimator; protected boolean mIsSwipingPipToHome; @@ -1127,17 +1124,14 @@ public abstract class AbsSwipeUpHandler, createHomeAnimationFactory(cookies, duration, isTranslucent, appCanEnterPip, runningTaskTarget); mIsSwipingPipToHome = homeAnimFactory.supportSwipePipToHome() && appCanEnterPip; + final RectFSpringAnim windowAnim; if (mIsSwipingPipToHome) { - mSwipePipToHomeAnimator = getSwipePipToHomeAnimator( + mSwipePipToHomeAnimator = createWindowAnimationToPip( homeAnimFactory, runningTaskTarget, start); - mSwipePipToHomeAnimator.setDuration(SWIPE_PIP_TO_HOME_DURATION); - mSwipePipToHomeAnimator.setInterpolator(interpolator); - mSwipePipToHomeAnimator.setFloatValues(FRACTION_START, FRACTION_END); - mSwipePipToHomeAnimator.start(); - mRunningWindowAnim = RunningWindowAnim.wrap(mSwipePipToHomeAnimator); + windowAnim = mSwipePipToHomeAnimator; } else { mSwipePipToHomeAnimator = null; - RectFSpringAnim windowAnim = createWindowAnimationToHome(start, homeAnimFactory); + windowAnim = createWindowAnimationToHome(start, homeAnimFactory); windowAnim.addAnimatorListener(new AnimationSuccessListener() { @Override public void onAnimationSuccess(Animator animator) { @@ -1151,9 +1145,9 @@ public abstract class AbsSwipeUpHandler, mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED); } }); - windowAnim.start(mContext, velocityPxPerMs); - mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim); } + windowAnim.start(mContext, velocityPxPerMs); + mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim); homeAnimFactory.setSwipeVelocity(velocityPxPerMs.y); homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y); mLauncherTransitionController = null; @@ -1209,7 +1203,7 @@ public abstract class AbsSwipeUpHandler, } } - private SwipePipToHomeAnimator getSwipePipToHomeAnimator(HomeAnimationFactory homeAnimFactory, + private SwipePipToHomeAnimator createWindowAnimationToPip(HomeAnimationFactory homeAnimFactory, RemoteAnimationTargetCompat runningTaskTarget, float startProgress) { // Directly animate the app to PiP (picture-in-picture) mode final ActivityManager.RunningTaskInfo taskInfo = mGestureState.getRunningTask(); @@ -1222,16 +1216,15 @@ public abstract class AbsSwipeUpHandler, runningTaskTarget.taskInfo.pictureInPictureParams, homeRotation, mDp.hotseatBarSizePx); - final Rect startBounds = new Rect(); - updateProgressForStartRect(new Matrix(), startProgress).round(startBounds); final SwipePipToHomeAnimator swipePipToHomeAnimator = new SwipePipToHomeAnimator( + mContext, runningTaskTarget.taskId, taskInfo.topActivity, runningTaskTarget.leash.getSurfaceControl(), TaskInfoCompat.getPipSourceRectHint( runningTaskTarget.taskInfo.pictureInPictureParams), TaskInfoCompat.getWindowConfigurationBounds(taskInfo), - startBounds, + updateProgressForStartRect(new Matrix(), startProgress), destinationBounds, mRecentsView.getPipCornerRadius(), mRecentsView); @@ -1243,7 +1236,7 @@ public abstract class AbsSwipeUpHandler, } AnimatorPlaybackController activityAnimationToHome = homeAnimFactory.createActivityAnimationToHome(); - swipePipToHomeAnimator.addListener(new AnimatorListenerAdapter() { + swipePipToHomeAnimator.addAnimatorListener(new AnimatorListenerAdapter() { private boolean mHasAnimationEnded; @Override public void onAnimationStart(Animator animation) { diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java index 61a2eafe6d..67a635b70b 100644 --- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java +++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java @@ -20,8 +20,8 @@ import static com.android.systemui.shared.system.InteractionJankMonitorWrapper.C import android.animation.Animator; import android.animation.RectEvaluator; -import android.animation.ValueAnimator; import android.content.ComponentName; +import android.content.Context; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Rect; @@ -44,30 +44,24 @@ import com.android.systemui.shared.pip.PipSurfaceTransactionHelper; import com.android.systemui.shared.system.InteractionJankMonitorWrapper; /** - * An {@link Animator} that animates an Activity to PiP (picture-in-picture) window when - * swiping up (in gesture navigation mode). Note that this class is derived from - * {@link com.android.wm.shell.pip.PipAnimationController.PipTransitionAnimator}. - * - * TODO: consider sharing this class including the animator and leash operations between - * Launcher and SysUI. Also, there should be one source of truth for the corner radius of the - * PiP window, which would ideally be on SysUI side as well. + * Subclass of {@link RectFSpringAnim} that animates an Activity to PiP (picture-in-picture) window + * when swiping up (in gesture navigation mode). */ -public class SwipePipToHomeAnimator extends ValueAnimator { +public class SwipePipToHomeAnimator extends RectFSpringAnim { private static final String TAG = SwipePipToHomeAnimator.class.getSimpleName(); - public static final float FRACTION_START = 0f; - public static final float FRACTION_END = 1f; + private static final float END_PROGRESS = 1.0f; private final int mTaskId; private final ComponentName mComponentName; private final SurfaceControl mLeash; private final Rect mAppBounds = new Rect(); private final Rect mStartBounds = new Rect(); + private final Rect mCurrentBounds = new Rect(); private final Rect mDestinationBounds = new Rect(); private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; - /** for calculating the transform in {@link #onAnimationUpdate(ValueAnimator)} */ - private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect()); + /** for calculating transform in {@link #onAnimationUpdate(AppCloseConfig, RectF, float)} */ private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect()); private final Rect mSourceHintRectInsets; private final Rect mSourceInsets = new Rect(); @@ -90,6 +84,7 @@ public class SwipePipToHomeAnimator extends ValueAnimator { private SurfaceControl mContentOverlay; /** + * @param context {@link Context} provides Launcher resources * @param taskId Task id associated with this animator, see also {@link #getTaskId()} * @param componentName Component associated with this animator, * see also {@link #getComponentName()} @@ -102,20 +97,22 @@ public class SwipePipToHomeAnimator extends ValueAnimator { * @param destinationBounds Bounds of the destination this animator ends to * @param cornerRadius Corner radius in pixel value for PiP window */ - public SwipePipToHomeAnimator(int taskId, + public SwipePipToHomeAnimator(@NonNull Context context, + int taskId, @NonNull ComponentName componentName, @NonNull SurfaceControl leash, @Nullable Rect sourceRectHint, @NonNull Rect appBounds, - @NonNull Rect startBounds, + @NonNull RectF startBounds, @NonNull Rect destinationBounds, int cornerRadius, @NonNull View view) { + super(startBounds, new RectF(destinationBounds), context); mTaskId = taskId; mComponentName = componentName; mLeash = leash; mAppBounds.set(appBounds); - mStartBounds.set(startBounds); + startBounds.round(mStartBounds); mDestinationBounds.set(destinationBounds); mDestinationBoundsTransformed.set(mDestinationBounds); mDestinationBoundsAnimation.set(mDestinationBounds); @@ -151,10 +148,10 @@ public class SwipePipToHomeAnimator extends ValueAnimator { t.reparent(mContentOverlay, mLeash); t.apply(); - addUpdateListener(valueAnimator -> { - float alpha = valueAnimator.getAnimatedFraction() < 0.5f + addOnUpdateListener((values, currentRect, progress) -> { + float alpha = progress < 0.5f ? 0 - : Utilities.mapToRange(valueAnimator.getAnimatedFraction(), 0.5f, 1f, + : Utilities.mapToRange(Math.min(progress, 1f), 0.5f, 1f, 0f, 1f, Interpolators.FAST_OUT_SLOW_IN); t.setAlpha(mContentOverlay, alpha); t.apply(); @@ -166,7 +163,7 @@ public class SwipePipToHomeAnimator extends ValueAnimator { appBounds.bottom - sourceRectHint.bottom); } - addListener(new AnimationSuccessListener() { + addAnimatorListener(new AnimationSuccessListener() { @Override public void onAnimationStart(Animator animation) { InteractionJankMonitorWrapper.begin(view, CUJ_APP_CLOSE_TO_PIP); @@ -191,7 +188,7 @@ public class SwipePipToHomeAnimator extends ValueAnimator { mHasAnimationEnded = true; } }); - addUpdateListener(this::onAnimationUpdate); + addOnUpdateListener(this::onAnimationUpdate); } /** sets the from rotation if it's different from the target rotation. */ @@ -219,34 +216,34 @@ public class SwipePipToHomeAnimator extends ValueAnimator { mAppBounds.top + mDestinationBounds.height()); } - private void onAnimationUpdate(ValueAnimator animator) { + private void onAnimationUpdate(@Nullable AppCloseConfig values, RectF currentRect, + float progress) { if (mHasAnimationEnded) return; final SurfaceControl.Transaction tx = PipSurfaceTransactionHelper.newSurfaceControlTransaction(); - onAnimationUpdate(tx, animator.getAnimatedFraction()); + onAnimationUpdate(tx, currentRect, progress); tx.apply(); } private PictureInPictureSurfaceTransaction onAnimationUpdate(SurfaceControl.Transaction tx, - float fraction) { - final Rect bounds = mRectEvaluator.evaluate(fraction, mStartBounds, - mDestinationBoundsAnimation); + RectF currentRect, float progress) { + currentRect.round(mCurrentBounds); final PictureInPictureSurfaceTransaction op; if (mSourceHintRectInsets == null) { // no source rect hint been set, directly scale the window down - op = onAnimationScale(fraction, tx, bounds); + op = onAnimationScale(progress, tx, mCurrentBounds); } else { // scale and crop according to the source rect hint - op = onAnimationScaleAndCrop(fraction, tx, bounds); + op = onAnimationScaleAndCrop(progress, tx, mCurrentBounds); } return op; } /** scale the window directly with no source rect hint being set */ private PictureInPictureSurfaceTransaction onAnimationScale( - float fraction, SurfaceControl.Transaction tx, Rect bounds) { + float progress, SurfaceControl.Transaction tx, Rect bounds) { if (mFromRotation == Surface.ROTATION_90 || mFromRotation == Surface.ROTATION_270) { - final RotatedPosition rotatedPosition = getRotatedPosition(fraction); + final RotatedPosition rotatedPosition = getRotatedPosition(progress); return mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds, rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY); } else { @@ -256,12 +253,12 @@ public class SwipePipToHomeAnimator extends ValueAnimator { /** scale and crop the window with source rect hint */ private PictureInPictureSurfaceTransaction onAnimationScaleAndCrop( - float fraction, SurfaceControl.Transaction tx, + float progress, SurfaceControl.Transaction tx, Rect bounds) { - final Rect insets = mInsetsEvaluator.evaluate(fraction, mSourceInsets, + final Rect insets = mInsetsEvaluator.evaluate(progress, mSourceInsets, mSourceHintRectInsets); if (mFromRotation == Surface.ROTATION_90 || mFromRotation == Surface.ROTATION_270) { - final RotatedPosition rotatedPosition = getRotatedPosition(fraction); + final RotatedPosition rotatedPosition = getRotatedPosition(progress); return mSurfaceTransactionHelper.scaleAndRotate(tx, mLeash, mAppBounds, bounds, insets, rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY); } else { @@ -291,22 +288,22 @@ public class SwipePipToHomeAnimator extends ValueAnimator { // get the final leash operations but do not apply to the leash. final SurfaceControl.Transaction tx = PipSurfaceTransactionHelper.newSurfaceControlTransaction(); - return onAnimationUpdate(tx, FRACTION_END); + return onAnimationUpdate(tx, new RectF(mDestinationBounds), END_PROGRESS); } - private RotatedPosition getRotatedPosition(float fraction) { + private RotatedPosition getRotatedPosition(float progress) { final float degree, positionX, positionY; if (mFromRotation == Surface.ROTATION_90) { - degree = -90 * fraction; - positionX = fraction * (mDestinationBoundsTransformed.left - mStartBounds.left) + degree = -90 * progress; + positionX = progress * (mDestinationBoundsTransformed.left - mStartBounds.left) + mStartBounds.left; - positionY = fraction * (mDestinationBoundsTransformed.bottom - mStartBounds.top) + positionY = progress * (mDestinationBoundsTransformed.bottom - mStartBounds.top) + mStartBounds.top; } else { - degree = 90 * fraction; - positionX = fraction * (mDestinationBoundsTransformed.right - mStartBounds.left) + degree = 90 * progress; + positionX = progress * (mDestinationBoundsTransformed.right - mStartBounds.left) + mStartBounds.left; - positionY = fraction * (mDestinationBoundsTransformed.top - mStartBounds.top) + positionY = progress * (mDestinationBoundsTransformed.top - mStartBounds.top) + mStartBounds.top; } return new RotatedPosition(degree, positionX, positionY); From 12e9345f4f38be1274283f768fe0422b90cf5801 Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Tue, 1 Jun 2021 14:43:14 -0700 Subject: [PATCH 4/8] Skip item infos that fail session info verification. trichromelibrary.canary is failing session info verification, but is still being added as blank icon on the workspace in some cases. Added logic to skip apps that fail this verification until the root cause can be found. Bug: 168788486 Test: none Change-Id: I8a7fe76f321de493b2c6c67ba6922eae23067ff3 --- .../model/AddWorkspaceItemsTask.java | 5 +-- .../launcher3/pm/InstallSessionHelper.java | 32 +++++++++++-------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java index 3be2c3af40..365cab1a4e 100644 --- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java +++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java @@ -130,8 +130,9 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { packageName); if (!packageInstaller.verifySessionInfo(sessionInfo)) { - FileLog.d(LOG, "Item info failed session info verification: " - + workspaceInfo); + FileLog.d(LOG, "Item info failed session info verification. " + + "Skipping : " + workspaceInfo); + continue; } List activities = launcherApps diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java index 52554900ea..2bd9ba0037 100644 --- a/src/com/android/launcher3/pm/InstallSessionHelper.java +++ b/src/com/android/launcher3/pm/InstallSessionHelper.java @@ -217,7 +217,8 @@ public class InstallSessionHelper { void tryQueuePromiseAppIcon(PackageInstaller.SessionInfo sessionInfo) { if (FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get() && SessionCommitReceiver.isEnabled(mAppContext) - && verifySessionInfo(sessionInfo)) { + && verifySessionInfo(sessionInfo) + && !promiseIconAddedForId(sessionInfo.getSessionId())) { FileLog.d(LOG, "Adding package name to install queue: " + sessionInfo.getAppPackageName()); @@ -234,25 +235,28 @@ public class InstallSessionHelper { && sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER && sessionInfo.getAppIcon() != null && !TextUtils.isEmpty(sessionInfo.getAppLabel()) - && !promiseIconAddedForId(sessionInfo.getSessionId()) && !new PackageManagerHelper(mAppContext).isAppInstalled( sessionInfo.getAppPackageName(), getUserHandle(sessionInfo)); if (sessionInfo != null) { Bitmap appIcon = sessionInfo.getAppIcon(); - FileLog.d(LOG, String.format( - "Verifying session info. Valid: %b, Session verified: %b, Install reason valid:" - + " %b, App icon: %s, App label: %s, Promise icon added: %b, " - + "App installed: %b.", - validSessionInfo, - verify(sessionInfo) != null, - sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER, - appIcon == null ? "null" : IOUtils.toBase64String(appIcon), - sessionInfo.getAppLabel(), - promiseIconAddedForId(sessionInfo.getSessionId()), - new PackageManagerHelper(mAppContext).isAppInstalled( - sessionInfo.getAppPackageName(), getUserHandle(sessionInfo)))); + if (Utilities.IS_DEBUG_DEVICE) { + FileLog.d(LOG, String.format( + "Verifying session info. Valid: %b," + + " Session verified: %b," + + " Install reason valid: %b," + + " App icon: %s," + + " App label: %s," + + " App installed: %b.", + validSessionInfo, + verify(sessionInfo) != null, + sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER, + appIcon == null ? "null" : IOUtils.toBase64String(appIcon), + sessionInfo.getAppLabel(), + new PackageManagerHelper(mAppContext).isAppInstalled( + sessionInfo.getAppPackageName(), getUserHandle(sessionInfo)))); + } } else { FileLog.d(LOG, "Verifying session info failed: session info null."); } From dfc274364e6d89bfb6a1681e4b28c785ab7e9f87 Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Mon, 14 Jun 2021 18:27:44 -0500 Subject: [PATCH 5/8] Add work toggle to work apps paused message This also fixes scrolling issues in landscape Bug: 186857875 Bug: 161929312 Bug: 189983516 Bug: 190567199 screenshot: https://screenshot.googleplex.com/BCjcyJFqJo6W4Tr Test: local Change-Id: I7bcf23cc4be2fca936eba41342731789eb8f3292 --- res/layout/work_apps_edu.xml | 52 +++++++------ res/layout/work_apps_paused.xml | 20 +++-- res/values/dimens.xml | 2 +- .../allapps/AllAppsContainerView.java | 24 +----- .../allapps/AllAppsRecyclerView.java | 10 +++ .../launcher3/allapps/FloatingHeaderView.java | 14 ++++ .../launcher3/allapps/WorkEduCard.java | 17 ++++- .../launcher3/allapps/WorkModeSwitch.java | 19 +++-- .../launcher3/allapps/WorkPausedCard.java | 75 +++++++++++++++++++ 9 files changed, 172 insertions(+), 61 deletions(-) create mode 100644 src/com/android/launcher3/allapps/WorkPausedCard.java diff --git a/res/layout/work_apps_edu.xml b/res/layout/work_apps_edu.xml index 2c58907e4b..ffbf962d35 100644 --- a/res/layout/work_apps_edu.xml +++ b/res/layout/work_apps_edu.xml @@ -16,30 +16,38 @@ - + android:orientation="vertical" + android:padding="@dimen/work_edu_card_margin" + android:background="@drawable/work_card" + android:layout_gravity="center_horizontal" + android:gravity="center" + android:id="@+id/wrapper"> -