From 892fab2c140df73bfef93cceb5d229f0424eecf0 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 10 Feb 2023 14:13:51 -0800 Subject: [PATCH 1/6] Using DiffUtil for calculating widget diff instead of a custom implementation This uses the same version as all-apps and avoids whole list refresh when a widget entry is expanded for the first time Removing tests which were essentially testing the diffing logic Bug: 268646258 Test: Verified on device Change-Id: I4799c66b4674e754547317317e7a63f098fce735 --- .../widget/model/WidgetListSpaceEntry.java | 5 - .../widget/model/WidgetsListBaseEntry.java | 24 -- .../widget/model/WidgetsListContentEntry.java | 6 - .../widget/model/WidgetsListHeaderEntry.java | 6 - .../widget/picker/WidgetsDiffCallback.java | 64 ++++ .../widget/picker/WidgetsDiffReporter.java | 187 ----------- .../widget/picker/WidgetsFullSheet.java | 4 - .../widget/picker/WidgetsListAdapter.java | 15 +- .../android/launcher3/model/WidgetsModel.java | 4 +- .../picker/WidgetsDiffReporterTest.java | 310 ------------------ .../widget/picker/WidgetsListAdapterTest.java | 293 ----------------- 11 files changed, 74 insertions(+), 844 deletions(-) create mode 100644 src/com/android/launcher3/widget/picker/WidgetsDiffCallback.java delete mode 100644 src/com/android/launcher3/widget/picker/WidgetsDiffReporter.java delete mode 100644 tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java delete mode 100644 tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java diff --git a/src/com/android/launcher3/widget/model/WidgetListSpaceEntry.java b/src/com/android/launcher3/widget/model/WidgetListSpaceEntry.java index 7f24905f88..5b1da5b368 100644 --- a/src/com/android/launcher3/widget/model/WidgetListSpaceEntry.java +++ b/src/com/android/launcher3/widget/model/WidgetListSpaceEntry.java @@ -33,9 +33,4 @@ public class WidgetListSpaceEntry extends WidgetsListBaseEntry { Collections.EMPTY_LIST); mPkgItem.title = ""; } - - @Override - public int getRank() { - return RANK_WIDGETS_TOP_SPACE; - } } diff --git a/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java b/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java index f09f4c6b5d..0003b762c2 100644 --- a/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java +++ b/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java @@ -16,16 +16,11 @@ package com.android.launcher3.widget.model; -import static java.lang.annotation.RetentionPolicy.SOURCE; - -import androidx.annotation.IntDef; - import com.android.launcher3.model.WidgetItem; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.PackageItemInfo; import com.android.launcher3.widget.WidgetItemComparator; -import java.lang.annotation.Retention; import java.util.List; import java.util.stream.Collectors; @@ -48,23 +43,4 @@ public abstract class WidgetsListBaseEntry { this.mWidgets = items.stream().sorted(new WidgetItemComparator()).collect(Collectors.toList()); } - - /** - * Returns the ranking of this entry in the - * {@link com.android.launcher3.widget.picker.WidgetsListAdapter}. - * - *

Entries with smaller value should be shown first. See - * {@link com.android.launcher3.widget.picker.WidgetsDiffReporter} for more details. - */ - @Rank - public abstract int getRank(); - - @Retention(SOURCE) - @IntDef({RANK_WIDGETS_TOP_SPACE, RANK_WIDGETS_LIST_HEADER, RANK_WIDGETS_LIST_CONTENT}) - public @interface Rank { - } - - public static final int RANK_WIDGETS_TOP_SPACE = 1; - public static final int RANK_WIDGETS_LIST_HEADER = 2; - public static final int RANK_WIDGETS_LIST_CONTENT = 3; } diff --git a/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java b/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java index 73b17f1d4f..626e0b91c1 100644 --- a/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java +++ b/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java @@ -61,12 +61,6 @@ public final class WidgetsListContentEntry extends WidgetsListBaseEntry { + mMaxSpanSizeInCells; } - @Override - @Rank - public int getRank() { - return RANK_WIDGETS_LIST_CONTENT; - } - /** * Returns a copy of this {@link WidgetsListContentEntry} with updated * {@param maxSpanSizeInCells}. diff --git a/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java b/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java index bb0cf9284e..68f18aebf4 100644 --- a/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java +++ b/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java @@ -85,12 +85,6 @@ public final class WidgetsListHeaderEntry extends WidgetsListBaseEntry { return "Header:" + mPkgItem.packageName + ":" + mWidgets.size(); } - @Override - @Rank - public int getRank() { - return RANK_WIDGETS_LIST_HEADER; - } - public boolean isSearchEntry() { return mIsSearchEntry; } diff --git a/src/com/android/launcher3/widget/picker/WidgetsDiffCallback.java b/src/com/android/launcher3/widget/picker/WidgetsDiffCallback.java new file mode 100644 index 0000000000..e610ea9a94 --- /dev/null +++ b/src/com/android/launcher3/widget/picker/WidgetsDiffCallback.java @@ -0,0 +1,64 @@ +/* + * 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.widget.picker; + +import androidx.recyclerview.widget.DiffUtil.Callback; + +import com.android.launcher3.widget.model.WidgetsListBaseEntry; + +import java.util.List; + +/** + * DiffUtil callback to compare widgets + */ +public class WidgetsDiffCallback extends Callback { + + private final List mOldEntries; + private final List mNewEntries; + + public WidgetsDiffCallback( + List oldEntries, + List newEntries) { + mOldEntries = oldEntries; + mNewEntries = newEntries; + } + + @Override + public int getOldListSize() { + return mOldEntries.size(); + } + + @Override + public int getNewListSize() { + return mNewEntries.size(); + } + + @Override + public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { + // Items are same if they point to the same package entry + WidgetsListBaseEntry oldItem = mOldEntries.get(oldItemPosition); + WidgetsListBaseEntry newItem = mNewEntries.get(newItemPosition); + return oldItem.getClass().equals(newItem.getClass()) + && oldItem.mPkgItem.equals(newItem.mPkgItem); + } + + @Override + public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { + // Always update all entries since the icon may have changed + return false; + } +} diff --git a/src/com/android/launcher3/widget/picker/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/picker/WidgetsDiffReporter.java deleted file mode 100644 index d09fe84e7b..0000000000 --- a/src/com/android/launcher3/widget/picker/WidgetsDiffReporter.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2017 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.util.Log; - -import androidx.recyclerview.widget.RecyclerView; - -import com.android.launcher3.icons.IconCache; -import com.android.launcher3.model.data.PackageItemInfo; -import com.android.launcher3.widget.model.WidgetsListBaseEntry; -import com.android.launcher3.widget.model.WidgetsListContentEntry; -import com.android.launcher3.widget.model.WidgetsListHeaderEntry; -import com.android.launcher3.widget.picker.WidgetsListAdapter.WidgetListBaseRowEntryComparator; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/** - * Do diff on widget's tray list items and call the {@link RecyclerView.Adapter} - * methods accordingly. - */ -public class WidgetsDiffReporter { - private static final boolean DEBUG = false; - private static final String TAG = "WidgetsDiffReporter"; - - private final IconCache mIconCache; - private final RecyclerView.Adapter mListener; - - public WidgetsDiffReporter(IconCache iconCache, RecyclerView.Adapter listener) { - mIconCache = iconCache; - mListener = listener; - } - - /** - * Notifies the difference between {@code currentEntries} & {@code newEntries} by calling the - * relevant {@link androidx.recyclerview.widget.RecyclerView.RecyclerViewDataObserver} methods. - */ - public void process(ArrayList currentEntries, - List newEntries, - WidgetListBaseRowEntryComparator comparator) { - if (DEBUG) { - Log.d(TAG, "process oldEntries#=" + currentEntries.size() - + " newEntries#=" + newEntries.size()); - } - // Early exit if either of the list is empty - if (currentEntries.isEmpty() || newEntries.isEmpty()) { - // Skip if both list are empty. - // On rotation, we open the widget tray with empty. Then try to fetch the list again - // when the animation completes (which still gives empty). And we get the final result - // when the bind actually completes. - if (currentEntries.size() != newEntries.size()) { - currentEntries.clear(); - currentEntries.addAll(newEntries); - mListener.notifyDataSetChanged(); - } - return; - } - ArrayList orgEntries = - (ArrayList) currentEntries.clone(); - Iterator orgIter = orgEntries.iterator(); - Iterator newIter = newEntries.iterator(); - - WidgetsListBaseEntry orgRowEntry = orgIter.next(); - WidgetsListBaseEntry newRowEntry = newIter.next(); - - do { - int diff = compareAppNameAndType(orgRowEntry, newRowEntry, comparator); - if (DEBUG) { - Log.d(TAG, String.format("diff=%d orgRowEntry (%s) newRowEntry (%s)", - diff, orgRowEntry != null ? orgRowEntry.toString() : null, - newRowEntry != null ? newRowEntry.toString() : null)); - } - int index = -1; - if (diff < 0) { - index = currentEntries.indexOf(orgRowEntry); - mListener.notifyItemRemoved(index); - if (DEBUG) { - Log.d(TAG, String.format("notifyItemRemoved called (%d)%s", index, - orgRowEntry.mTitleSectionName)); - } - currentEntries.remove(index); - orgRowEntry = orgIter.hasNext() ? orgIter.next() : null; - } else if (diff > 0) { - index = orgRowEntry != null ? currentEntries.indexOf(orgRowEntry) - : currentEntries.size(); - currentEntries.add(index, newRowEntry); - if (DEBUG) { - Log.d(TAG, String.format("notifyItemInserted called (%d)%s", index, - newRowEntry.mTitleSectionName)); - } - newRowEntry = newIter.hasNext() ? newIter.next() : null; - mListener.notifyItemInserted(index); - - } else { - // same app name & type but, - // did the icon, title, etc, change? - // or did the header view changed due to user interactions? - // or did the widget size and desc, span, etc change? - if (!isSamePackageItemInfo(orgRowEntry.mPkgItem, newRowEntry.mPkgItem) - || hasHeaderUpdated(orgRowEntry, newRowEntry) - || hasWidgetsListContentChanged(orgRowEntry, newRowEntry)) { - index = currentEntries.indexOf(orgRowEntry); - currentEntries.set(index, newRowEntry); - mListener.notifyItemChanged(index); - if (DEBUG) { - Log.d(TAG, String.format("notifyItemChanged called (%d)%s", index, - newRowEntry.mTitleSectionName)); - } - } - orgRowEntry = orgIter.hasNext() ? orgIter.next() : null; - newRowEntry = newIter.hasNext() ? newIter.next() : null; - } - } while(orgRowEntry != null || newRowEntry != null); - } - - /** - * Compares the app name and then entry type for the given {@link WidgetsListBaseEntry}s. - * - * @Return 0 if both entries' order is the same. Negative integer if {@code newRowEntry} should - * order before {@code orgRowEntry}. Positive integer if {@code orgRowEntry} should - * order before {@code newRowEntry}. - */ - private int compareAppNameAndType(WidgetsListBaseEntry curRow, WidgetsListBaseEntry newRow, - WidgetListBaseRowEntryComparator comparator) { - if (curRow == null && newRow == null) { - throw new IllegalStateException( - "Cannot compare PackageItemInfo if both rows are null."); - } - - if (curRow == null && newRow != null) { - return 1; // new row needs to be inserted - } else if (curRow != null && newRow == null) { - return -1; // old row needs to be deleted - } - int diff = comparator.compare(curRow, newRow); - if (diff == 0) { - return newRow.getRank() - curRow.getRank(); - } - return diff; - } - - /** - * Returns {@code true} if both {@code curRow} & {@code newRow} are - * {@link WidgetsListContentEntry}s with a different list or arrangement of widgets. - */ - private boolean hasWidgetsListContentChanged(WidgetsListBaseEntry curRow, - WidgetsListBaseEntry newRow) { - if (!(curRow instanceof WidgetsListContentEntry) - || !(newRow instanceof WidgetsListContentEntry)) { - return false; - } - return !curRow.equals(newRow); - } - - /** - * Returns {@code true} if {@code newRow} is {@link WidgetsListHeaderEntry} and its content has - * been changed due to user interactions. - */ - private boolean hasHeaderUpdated(WidgetsListBaseEntry curRow, WidgetsListBaseEntry newRow) { - if (newRow instanceof WidgetsListHeaderEntry && curRow instanceof WidgetsListHeaderEntry) { - // Always refresh search header entries to reset rounded corners in their view holder. - return !curRow.equals(newRow) || ((WidgetsListHeaderEntry) curRow).isSearchEntry(); - } - return false; - } - - private boolean isSamePackageItemInfo(PackageItemInfo curInfo, PackageItemInfo newInfo) { - return curInfo.bitmap.icon.equals(newInfo.bitmap.icon) - && !mIconCache.isDefaultIcon(curInfo.bitmap, curInfo.user); - } -} diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index 2ce400ed68..57bd27cbcb 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -59,7 +59,6 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.Interpolators; @@ -943,8 +942,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet AdapterHolder(int adapterType) { mAdapterType = adapterType; Context context = getContext(); - LauncherAppState apps = LauncherAppState.getInstance(context); - HeaderChangeListener headerChangeListener = new HeaderChangeListener() { @Override public void onHeaderChanged(@NonNull PackageUserKey selectedHeader) { @@ -975,7 +972,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet mWidgetsListAdapter = new WidgetsListAdapter( context, LayoutInflater.from(context), - apps.getIconCache(), this::getEmptySpaceHeight, /* iconClickListener= */ WidgetsFullSheet.this, /* iconLongClickListener= */ WidgetsFullSheet.this, diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java index b5ff719ed6..20b1d9b75d 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java @@ -32,13 +32,14 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.DiffUtil.DiffResult; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.Adapter; import androidx.recyclerview.widget.RecyclerView.ViewHolder; import com.android.launcher3.R; -import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.data.PackageItemInfo; import com.android.launcher3.recyclerview.ViewHolderBinder; import com.android.launcher3.util.LabelComparator; @@ -82,7 +83,6 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC public static final int VIEW_TYPE_WIDGETS_HEADER = R.id.view_type_widgets_header; private final Context mContext; - private final WidgetsDiffReporter mDiffReporter; private final SparseArray mViewHolderBinders = new SparseArray<>(); private final WidgetListBaseRowEntryComparator mRowComparator = new WidgetListBaseRowEntryComparator(); @@ -102,12 +102,11 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC private int mMaxSpanSize = 4; public WidgetsListAdapter(Context context, LayoutInflater layoutInflater, - IconCache iconCache, IntSupplier emptySpaceHeightProvider, - OnClickListener iconClickListener, OnLongClickListener iconLongClickListener, + IntSupplier emptySpaceHeightProvider, OnClickListener iconClickListener, + OnLongClickListener iconLongClickListener, WidgetsFullSheet.HeaderChangeListener headerChangeListener) { mHeaderChangeListener = headerChangeListener; mContext = context; - mDiffReporter = new WidgetsDiffReporter(iconCache, this); mViewHolderBinders.put( VIEW_TYPE_WIDGETS_LIST, @@ -205,7 +204,11 @@ public class WidgetsListAdapter extends Adapter implements OnHeaderC }) .collect(Collectors.toList()); - mDiffReporter.process(mVisibleEntries, newVisibleEntries, mRowComparator); + DiffResult diffResult = DiffUtil.calculateDiff( + new WidgetsDiffCallback(mVisibleEntries, newVisibleEntries), false); + mVisibleEntries.clear(); + mVisibleEntries.addAll(newVisibleEntries); + diffResult.dispatchUpdatesTo(this); if (mPendingClickHeader != null) { // Get the position for the clicked header after adjusting the visible entries. The diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java index f4903338d1..1b743e8bdc 100644 --- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java +++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java @@ -40,7 +40,6 @@ import com.android.launcher3.widget.WidgetSections; import com.android.launcher3.widget.model.WidgetsListBaseEntry; import com.android.launcher3.widget.model.WidgetsListContentEntry; import com.android.launcher3.widget.model.WidgetsListHeaderEntry; -import com.android.launcher3.widget.picker.WidgetsDiffReporter; import java.util.ArrayList; import java.util.Arrays; @@ -73,8 +72,7 @@ public class WidgetsModel { /** * Returns a list of {@link WidgetsListBaseEntry}. All {@link WidgetItem} in a single row * are sorted (based on label and user), but the overall list of - * {@link WidgetsListBaseEntry}s is not sorted. This list is sorted at the UI when using - * {@link WidgetsDiffReporter} + * {@link WidgetsListBaseEntry}s is not sorted. * * @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List) */ diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java deleted file mode 100644 index 8c87957530..0000000000 --- a/tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java +++ /dev/null @@ -1,310 +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 static androidx.test.core.app.ApplicationProvider.getApplicationContext; - -import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; - -import android.appwidget.AppWidgetProviderInfo; -import android.content.ComponentName; -import android.content.Context; -import android.graphics.Bitmap; -import android.os.UserHandle; - -import androidx.recyclerview.widget.RecyclerView; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.icons.BitmapInfo; -import com.android.launcher3.icons.ComponentWithLabel; -import com.android.launcher3.icons.IconCache; -import com.android.launcher3.model.WidgetItem; -import com.android.launcher3.model.data.PackageItemInfo; -import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; -import com.android.launcher3.widget.model.WidgetsListBaseEntry; -import com.android.launcher3.widget.model.WidgetsListContentEntry; -import com.android.launcher3.widget.model.WidgetsListHeaderEntry; -import com.android.launcher3.widget.picker.WidgetsListAdapter.WidgetListBaseRowEntryComparator; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.List; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public final class WidgetsDiffReporterTest { - private static final String TEST_PACKAGE_PREFIX = "com.android.test"; - private static final WidgetListBaseRowEntryComparator COMPARATOR = - new WidgetListBaseRowEntryComparator(); - - @Mock private IconCache mIconCache; - @Mock private RecyclerView.Adapter mAdapter; - - private InvariantDeviceProfile mTestProfile; - private WidgetsDiffReporter mWidgetsDiffReporter; - private Context mContext; - private WidgetsListHeaderEntry mHeaderA; - private WidgetsListHeaderEntry mHeaderB; - private WidgetsListHeaderEntry mHeaderC; - private WidgetsListHeaderEntry mHeaderD; - private WidgetsListHeaderEntry mHeaderE; - private WidgetsListContentEntry mContentC; - private WidgetsListContentEntry mContentE; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mTestProfile = new InvariantDeviceProfile(); - mTestProfile.numRows = 5; - mTestProfile.numColumns = 5; - - doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0)) - .getComponent().getPackageName()) - .when(mIconCache).getTitleNoCache(any()); - - mContext = getApplicationContext(); - mWidgetsDiffReporter = new WidgetsDiffReporter(mIconCache, mAdapter); - mHeaderA = createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "A", - /* appName= */ "A", /* numOfWidgets= */ 3); - mHeaderB = createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "B", - /* appName= */ "B", /* numOfWidgets= */ 3); - mHeaderC = createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "C", - /* appName= */ "C", /* numOfWidgets= */ 3); - mContentC = createWidgetsContentEntry(TEST_PACKAGE_PREFIX + "C", - /* appName= */ "C", /* numOfWidgets= */ 3); - mHeaderD = createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "D", - /* appName= */ "D", /* numOfWidgets= */ 3); - mHeaderE = createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "E", - /* appName= */ "E", /* numOfWidgets= */ 3); - mContentE = createWidgetsContentEntry(TEST_PACKAGE_PREFIX + "E", - /* appName= */ "E", /* numOfWidgets= */ 3); - } - - @Test - public void listNotChanged_shouldNotInvokeAnyCallbacks() { - // GIVEN the current list has app headers [A, B, C]. - ArrayList currentList = new ArrayList<>( - List.of(mHeaderA, mHeaderB, mHeaderC)); - - // WHEN computing the list difference. - mWidgetsDiffReporter.process(currentList, currentList, COMPARATOR); - - // THEN there is no adaptor callback. - verifyZeroInteractions(mAdapter); - // THEN the current list contains the same entries. - assertThat(currentList).containsExactly(mHeaderA, mHeaderB, mHeaderC); - } - - @Test - public void headersOnly_emptyListToNonEmpty_shouldInvokeNotifyDataSetChanged() { - // GIVEN the current list has app headers [A, B, C]. - ArrayList currentList = new ArrayList<>(); - - List newList = List.of( - createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "A", "A", 3), - createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "B", "B", 3), - createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "C", "C", 3)); - - // WHEN computing the list difference. - mWidgetsDiffReporter.process(currentList, newList, COMPARATOR); - - // THEN notifyDataSetChanged is called - verify(mAdapter).notifyDataSetChanged(); - // THEN the current list contains all elements from the new list. - assertThat(currentList).containsExactlyElementsIn(newList); - } - - @Test - public void headersOnly_nonEmptyToEmptyList_shouldInvokeNotifyDataSetChanged() { - // GIVEN the current list has app headers [A, B, C]. - ArrayList currentList = new ArrayList<>( - List.of(mHeaderA, mHeaderB, mHeaderC)); - // GIVEN the new list is empty. - List newList = List.of(); - - // WHEN computing the list difference. - mWidgetsDiffReporter.process(currentList, newList, COMPARATOR); - - // THEN notifyDataSetChanged is called. - verify(mAdapter).notifyDataSetChanged(); - // THEN the current list isEmpty. - assertThat(currentList).isEmpty(); - } - - @Test - public void headersOnly_itemAddedAndRemovedInTheNewList_shouldInvokeCorrectCallbacks() { - // GIVEN the current list has app headers [A, B, D]. - ArrayList currentList = new ArrayList<>( - List.of(mHeaderA, mHeaderB, mHeaderD)); - // GIVEN the new list has app headers [A, C, E]. - List newList = List.of(mHeaderA, mHeaderC, mHeaderE); - - // WHEN computing the list difference. - mWidgetsDiffReporter.process(currentList, newList, COMPARATOR); - - // THEN "B" is removed from position 1. - verify(mAdapter).notifyItemRemoved(/* position= */ 1); - // THEN "D" is removed from position 2. - verify(mAdapter).notifyItemRemoved(/* position= */ 2); - // THEN "C" is inserted at position 1. - verify(mAdapter).notifyItemInserted(/* position= */ 1); - // THEN "E" is inserted at position 2. - verify(mAdapter).notifyItemInserted(/* position= */ 2); - // THEN the current list contains all elements from the new list. - assertThat(currentList).containsExactlyElementsIn(newList); - } - - @Test - public void headersContentsMix_itemAddedAndRemovedInTheNewList_shouldInvokeCorrectCallbacks() { - // GIVEN the current list has app headers [A, B, E content]. - ArrayList currentList = new ArrayList<>( - List.of(mHeaderA, mHeaderB, mContentE)); - // GIVEN the new list has app headers [A, C content, D]. - List newList = List.of(mHeaderA, mContentC, mHeaderD); - - // WHEN computing the list difference. - mWidgetsDiffReporter.process(currentList, newList, COMPARATOR); - - // THEN "B" is removed from position 1. - verify(mAdapter).notifyItemRemoved(/* position= */ 1); - // THEN "C content" is inserted at position 1. - verify(mAdapter).notifyItemInserted(/* position= */ 1); - // THEN "D" is inserted at position 2. - verify(mAdapter).notifyItemInserted(/* position= */ 2); - // THEN "E content" is removed from position 3. - verify(mAdapter).notifyItemRemoved(/* position= */ 3); - // THEN the current list contains all elements from the new list. - assertThat(currentList).containsExactlyElementsIn(newList); - } - - @Test - public void headersContentsMix_userInteractWithHeader_shouldInvokeCorrectCallbacks() { - // GIVEN the current list has app headers [A, B, E content]. - ArrayList currentList = new ArrayList<>( - List.of(mHeaderA, mHeaderB, mContentE)); - // GIVEN the new list has app headers [A, B, E content] and the user has interacted with B. - List newList = - List.of(mHeaderA, mHeaderB.withWidgetListShown(), mContentE); - - // WHEN computing the list difference. - mWidgetsDiffReporter.process(currentList, newList, COMPARATOR); - - // THEN notify "B" has been changed. - verify(mAdapter).notifyItemChanged(/* position= */ 1); - // THEN the current list contains all elements from the new list. - assertThat(currentList).containsExactlyElementsIn(newList); - } - - @Test - public void headersContentsMix_headerWidgetsModified_shouldInvokeCorrectCallbacks() { - // GIVEN the current list has app headers [A, B, E content]. - ArrayList currentList = new ArrayList<>( - List.of(mHeaderA, mHeaderB, mContentE)); - // GIVEN the new list has one of the headers widgets list modified. - List newList = List.of( - WidgetsListHeaderEntry.create( - mHeaderA.mPkgItem, mHeaderA.mTitleSectionName, - mHeaderA.mWidgets.subList(0, 1)), - mHeaderB, mContentE); - - // WHEN computing the list difference. - mWidgetsDiffReporter.process(currentList, newList, COMPARATOR); - - // THEN notify "A" has been changed. - verify(mAdapter).notifyItemChanged(/* position= */ 0); - // THEN the current list contains all elements from the new list. - assertThat(currentList).containsExactlyElementsIn(newList); - } - - @Test - public void headersContentsMix_contentMaxSpanSizeModified_shouldInvokeCorrectCallbacks() { - // GIVEN the current list has app headers [A, B, E content]. - ArrayList currentList = new ArrayList<>( - List.of(mHeaderA, mHeaderB, mContentE)); - // GIVEN the new list has max span size in "E content" modified. - List newList = List.of( - mHeaderA, - mHeaderB, - new WidgetsListContentEntry( - mContentE.mPkgItem, - mContentE.mTitleSectionName, - mContentE.mWidgets, - mContentE.getMaxSpanSizeInCells() + 1)); - - // WHEN computing the list difference. - mWidgetsDiffReporter.process(currentList, newList, COMPARATOR); - - // THEN notify "E content" has been changed. - verify(mAdapter).notifyItemChanged(/* position= */ 2); - // THEN the current list contains all elements from the new list. - assertThat(currentList).containsExactlyElementsIn(newList); - } - - - private WidgetsListHeaderEntry createWidgetsHeaderEntry(String packageName, String appName, - int numOfWidgets) { - List widgetItems = generateWidgetItems(packageName, numOfWidgets); - PackageItemInfo pInfo = createPackageItemInfo(packageName, appName, - widgetItems.get(0).user); - - return WidgetsListHeaderEntry.create(pInfo, /* titleSectionName= */ "", widgetItems); - } - - private WidgetsListContentEntry createWidgetsContentEntry(String packageName, String appName, - int numOfWidgets) { - List widgetItems = generateWidgetItems(packageName, numOfWidgets); - PackageItemInfo pInfo = createPackageItemInfo(packageName, appName, - widgetItems.get(0).user); - - return new WidgetsListContentEntry(pInfo, /* titleSectionName= */ "", widgetItems); - } - - private PackageItemInfo createPackageItemInfo(String packageName, String appName, - UserHandle userHandle) { - PackageItemInfo pInfo = new PackageItemInfo(packageName, userHandle); - pInfo.title = appName; - pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0); - return pInfo; - } - - private List generateWidgetItems(String packageName, int numOfWidgets) { - ArrayList widgetItems = new ArrayList<>(); - for (int i = 0; i < numOfWidgets; i++) { - ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i); - AppWidgetProviderInfo widgetInfo = createAppWidgetProviderInfo(cn); - - WidgetItem widgetItem = new WidgetItem( - LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo), - mTestProfile, mIconCache); - widgetItems.add(widgetItem); - } - return widgetItems; - } -} diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java deleted file mode 100644 index 0044d046b1..0000000000 --- a/tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (C) 2017 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 androidx.test.core.app.ApplicationProvider.getApplicationContext; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isNull; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.verify; - -import android.appwidget.AppWidgetProviderInfo; -import android.content.ComponentName; -import android.content.Context; -import android.graphics.Bitmap; -import android.os.Process; -import android.os.UserHandle; -import android.view.LayoutInflater; - -import androidx.recyclerview.widget.RecyclerView; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.icons.BitmapInfo; -import com.android.launcher3.icons.ComponentWithLabel; -import com.android.launcher3.icons.IconCache; -import com.android.launcher3.model.WidgetItem; -import com.android.launcher3.model.data.PackageItemInfo; -import com.android.launcher3.util.ActivityContextWrapper; -import com.android.launcher3.util.PackageUserKey; -import com.android.launcher3.util.WidgetUtils; -import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; -import com.android.launcher3.widget.model.WidgetsListBaseEntry; -import com.android.launcher3.widget.model.WidgetsListContentEntry; -import com.android.launcher3.widget.model.WidgetsListHeaderEntry; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.List; - -/** - * Unit tests for WidgetsListAdapter - * Note that all indices matching are shifted by 1 to account for the empty space at the start. - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public final class WidgetsListAdapterTest { - private static final String TEST_PACKAGE_PLACEHOLDER = "com.google.test"; - - @Mock private LayoutInflater mMockLayoutInflater; - @Mock private RecyclerView.AdapterDataObserver mListener; - @Mock private IconCache mIconCache; - - private WidgetsListAdapter mAdapter; - private InvariantDeviceProfile mTestProfile; - private UserHandle mUserHandle; - private Context mContext; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - mContext = new ActivityContextWrapper(getApplicationContext()); - mTestProfile = new InvariantDeviceProfile(); - mTestProfile.numRows = 5; - mTestProfile.numColumns = 5; - mUserHandle = Process.myUserHandle(); - mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, - mIconCache, () -> 0, null, null, null); - mAdapter.registerAdapterDataObserver(mListener); - - doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0)) - .getComponent().getPackageName()) - .when(mIconCache).getTitleNoCache(any()); - } - - @Test - public void setWidgets_shouldNotifyDataSetChanged() { - mAdapter.setWidgets(generateSampleMap(1)); - - verify(mListener).onChanged(); - } - - @Test - public void setWidgets_withItemInserted_shouldNotifyItemInserted() { - mAdapter.setWidgets(generateSampleMap(1)); - mAdapter.setWidgets(generateSampleMap(2)); - - verify(mListener).onItemRangeInserted(eq(2), eq(1)); - } - - @Test - public void setWidgets_withItemRemoved_shouldNotifyItemRemoved() { - mAdapter.setWidgets(generateSampleMap(2)); - mAdapter.setWidgets(generateSampleMap(1)); - - verify(mListener).onItemRangeRemoved(eq(2), eq(1)); - } - - @Test - public void setWidgets_appIconChanged_shouldNotifyItemChanged() { - mAdapter.setWidgets(generateSampleMap(1)); - mAdapter.setWidgets(generateSampleMap(1)); - - verify(mListener).onItemRangeChanged(eq(1), eq(1), isNull()); - } - - @Test - public void headerClick_expanded_shouldNotifyItemChange() { - // GIVEN a list of widgets entries: - // [com.google.test0, com.google.test0 content, - // com.google.test1, com.google.test1 content, - // com.google.test2, com.google.test2 content] - // The visible widgets entries: [com.google.test0, com.google.test1, com.google.test2]. - mAdapter.setWidgets(generateSampleMap(3)); - - // WHEN com.google.test.1 header is expanded. - mAdapter.onHeaderClicked(/* showWidgets= */ true, - new PackageUserKey(TEST_PACKAGE_PLACEHOLDER + 1, mUserHandle)); - - // THEN the visible entries list becomes: - // [com.google.test0, com.google.test1, com.google.test1 content, com.google.test2] - // com.google.test.1 content is inserted into position 2. - verify(mListener).onItemRangeInserted(eq(3), eq(1)); - } - - @Test - public void setWidgets_expandedApp_moreWidgets_shouldNotifyItemChangedWithWidgetItemInfoDiff() { - // GIVEN the adapter was first populated with com.google.test0 & com.google.test1. Each app - // has one widget. - ArrayList allEntries = generateSampleMap(2); - mAdapter.setWidgets(allEntries); - // GIVEN test com.google.test1 is expanded. - // Visible entries in the adapter are: - // [com.google.test0, com.google.test1, com.google.test1 content] - mAdapter.onHeaderClicked(/* showWidgets= */ true, - new PackageUserKey(TEST_PACKAGE_PLACEHOLDER + 1, mUserHandle)); - Mockito.reset(mListener); - - // WHEN the adapter is updated with the same list of apps but com.google.test1 has 2 widgets - // now. - WidgetsListContentEntry testPackage1ContentEntry = - (WidgetsListContentEntry) allEntries.get(3); - WidgetItem widgetItem = testPackage1ContentEntry.mWidgets.get(0); - WidgetsListContentEntry newTestPackage1ContentEntry = new WidgetsListContentEntry( - testPackage1ContentEntry.mPkgItem, - testPackage1ContentEntry.mTitleSectionName, List.of(widgetItem, widgetItem)); - allEntries.set(3, newTestPackage1ContentEntry); - mAdapter.setWidgets(allEntries); - - // THEN the onItemRangeChanged is invoked for "com.google.test1 content" at index 2. - verify(mListener).onItemRangeChanged(eq(3), eq(1), isNull()); - } - - @Test - public void setWidgets_hodgepodge_shouldInvokeExpectedDataObserverCallbacks() { - // GIVEN a widgets entry list: - // Index: 0| 1 | 2| 3 | 4| 5 | 6| 7 | 8| 9 | - // [A, A content, B, B content, C, C content, D, D content, E, E content] - List allAppsWithWidgets = generateSampleMap(5); - // GIVEN the current widgets list consist of [A, A content, B, B content, E, E content]. - // GIVEN the visible widgets list consist of [A, B, E] - List currentList = List.of( - // A & A content - allAppsWithWidgets.get(0), allAppsWithWidgets.get(1), - // B & B content - allAppsWithWidgets.get(2), allAppsWithWidgets.get(3), - // E & E content - allAppsWithWidgets.get(8), allAppsWithWidgets.get(9)); - mAdapter.setWidgets(currentList); - - // WHEN the widgets list is updated to [A, A content, C, C content, D, D content]. - // WHEN the visible widgets list is updated to [A, C, D]. - List newList = List.of( - // A & A content - allAppsWithWidgets.get(0), allAppsWithWidgets.get(1), - // C & C content - allAppsWithWidgets.get(4), allAppsWithWidgets.get(5), - // D & D content - allAppsWithWidgets.get(6), allAppsWithWidgets.get(7)); - mAdapter.setWidgets(newList); - - // Account for 1st items as empty space - // Computation logic | [Intermediate list during computation] - // THEN B <> C < 0, removed B from index 1 | [A, E] - verify(mListener).onItemRangeRemoved(/* positionStart= */ 2, /* itemCount= */ 1); - // THEN E <> C > 0, C inserted to index 1 | [A, C, E] - verify(mListener).onItemRangeInserted(/* positionStart= */ 2, /* itemCount= */ 1); - // THEN E <> D > 0, D inserted to index 2 | [A, C, D, E] - verify(mListener).onItemRangeInserted(/* positionStart= */ 3, /* itemCount= */ 1); - // THEN E <> null = -1, E deleted from index 3 | [A, C, D] - verify(mListener).onItemRangeRemoved(/* positionStart= */ 4, /* itemCount= */ 1); - } - - @Test - public void setWidgetsOnSearch_expandedApp_shouldResetExpandedApp() { - // GIVEN a list of widgets entries: - // [Empty item - // com.google.test0, - // com.google.test0 content, - // com.google.test1, - // com.google.test1 content, - // com.google.test2, - // com.google.test2 content] - // The visible widgets entries: - // [Empty item, - // com.google.test0, - // com.google.test1, - // com.google.test2]. - ArrayList allEntries = generateSampleMap(3); - mAdapter.setWidgetsOnSearch(allEntries); - // GIVEN com.google.test.1 header is expanded. The visible entries list becomes: - // [Empty item, com.google.test0, com.google.test1, com.google.test1 content, - // com.google.test2] - mAdapter.onHeaderClicked(/* showWidgets= */ true, - new PackageUserKey(TEST_PACKAGE_PLACEHOLDER + 1, mUserHandle)); - Mockito.reset(mListener); - - // WHEN same widget entries are set again. - mAdapter.setWidgetsOnSearch(allEntries); - - // THEN expanded app is reset and the visible entries list becomes: - // [Empty item, com.google.test0, com.google.test1, com.google.test2] - verify(mListener).onItemRangeChanged(eq(2), eq(1), isNull()); - verify(mListener).onItemRangeRemoved(/* positionStart= */ 3, /* itemCount= */ 1); - } - - /** - * Generates a list of sample widget entries. - * - *

Each sample app has 1 widget only. An app is represented by 2 entries, - * {@link WidgetsListHeaderEntry} & {@link WidgetsListContentEntry}. Only - * {@link WidgetsListHeaderEntry} is always visible in the {@link WidgetsListAdapter}. - * {@link WidgetsListContentEntry} is only shown upon clicking the corresponding app's - * {@link WidgetsListHeaderEntry}. Only at most one {@link WidgetsListContentEntry} is shown at - * a time. - * - * @param num the number of apps that have widgets. - */ - private ArrayList generateSampleMap(int num) { - ArrayList result = new ArrayList<>(); - if (num <= 0) return result; - - for (int i = 0; i < num; i++) { - String packageName = TEST_PACKAGE_PLACEHOLDER + i; - - List widgetItems = generateWidgetItems(packageName, /* numOfWidgets= */ 1); - - PackageItemInfo pInfo = new PackageItemInfo(packageName, widgetItems.get(0).user); - pInfo.title = pInfo.packageName; - pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0); - - result.add(WidgetsListHeaderEntry.create( - pInfo, /* titleSectionName= */ "", widgetItems)); - result.add(new WidgetsListContentEntry(pInfo, /* titleSectionName= */ "", widgetItems)); - } - - return result; - } - - private List generateWidgetItems(String packageName, int numOfWidgets) { - ArrayList widgetItems = new ArrayList<>(); - for (int i = 0; i < numOfWidgets; i++) { - ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i); - AppWidgetProviderInfo widgetInfo = WidgetUtils.createAppWidgetProviderInfo(cn); - - widgetItems.add(new WidgetItem( - LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo), - mTestProfile, mIconCache)); - } - return widgetItems; - } -} From 3287c1d8f96fcf8cf88568a0bdd54d80f06cf074 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Tue, 14 Feb 2023 17:33:48 -0800 Subject: [PATCH 2/6] Hide smart actions if we enter split select from home Test: Enter split selection from workspace/all apps, with 1, 0, or more apps in overview Fixes: 269363674 Change-Id: I8eba01960dd7e3c2683fe8f1d21e81de3b8245a2 --- .../src/com/android/quickstep/views/RecentsView.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 5b408493b3..5ecc05abcf 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -1624,10 +1624,13 @@ public abstract class RecentsView Date: Wed, 15 Feb 2023 22:31:55 +0000 Subject: [PATCH 3/6] Suppress transient Taskbar features EDU in overview. The EDU explains how to drag to splitscreen applications, but this operation is unavailable in overview. Therefore, let's only attempt to show the EDU when we are in an application. Test: Manual Fix: 269363653 Change-Id: I97aa5c76b44562189862432fb3856bf5634c6e4b --- .../launcher3/taskbar/TaskbarTranslationController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java index 80f030fb69..a6b2a8abd0 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java @@ -113,7 +113,8 @@ public class TaskbarTranslationController implements TaskbarControllers.Loggable return; } reset(); - if (mControllers.taskbarStashController.isTaskbarVisibleAndNotStashing()) { + if (mControllers.taskbarStashController.isInApp() + && mControllers.taskbarStashController.isTaskbarVisibleAndNotStashing()) { mControllers.taskbarEduTooltipController.maybeShowFeaturesEdu(); } })); From cb640da2bb779a811291d6e2e61cca8e795b5d47 Mon Sep 17 00:00:00 2001 From: Fengjiang Li Date: Sat, 11 Feb 2023 11:01:58 -0800 Subject: [PATCH 4/6] [Predictive Back] Widget to home polish: show extra rows at bottom during animation 1. setClipChildren(false) for WidgetFullSheet and content view during animation 2. setClipToOutline(true) for WidgetsRecyclerView and provide ViewOutlineProvider to expand bottom by 5% of height 3. Override calculateExtraLayoutSpace() for ScrollableLayoutManager 4. Manually modify AbstractSlideInView#mContent's background drawable during scale animation bug: b/260956481 Test: manual Change-Id: Ic391639de887cf4a70bc4965dc0b1fd9bc12dd2c --- res/layout/widgets_bottom_sheet_content.xml | 1 - res/layout/widgets_full_sheet.xml | 1 - .../widgets_full_sheet_large_screen.xml | 1 - res/layout/widgets_full_sheet_paged_view.xml | 1 + ...ets_full_sheet_paged_view_large_screen.xml | 1 + .../widgets_full_sheet_recyclerview.xml | 1 + ...s_full_sheet_recyclerview_large_screen.xml | 1 + .../allapps/ActivityAllAppsContainerView.java | 4 +- .../launcher3/allapps/AllAppsGridAdapter.java | 14 ---- .../allapps/AllAppsTransitionController.java | 9 ++- .../util/ScrollableLayoutManager.java | 12 +++ .../launcher3/views/AbstractSlideInView.java | 79 +++++++++++++++++++ .../launcher3/widget/WidgetsBottomSheet.java | 1 + .../widget/picker/WidgetsFullSheet.java | 41 +++++++--- 14 files changed, 134 insertions(+), 33 deletions(-) diff --git a/res/layout/widgets_bottom_sheet_content.xml b/res/layout/widgets_bottom_sheet_content.xml index a5f72ef30a..b76eef7608 100644 --- a/res/layout/widgets_bottom_sheet_content.xml +++ b/res/layout/widgets_bottom_sheet_content.xml @@ -18,7 +18,6 @@ android:id="@+id/widgets_bottom_sheet" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/bg_rounded_corner_bottom_sheet" android:paddingTop="@dimen/bottom_sheet_handle_margin" android:orientation="vertical"> diff --git a/res/layout/widgets_full_sheet_large_screen.xml b/res/layout/widgets_full_sheet_large_screen.xml index 3dbe6f5bf8..1c0037d698 100644 --- a/res/layout/widgets_full_sheet_large_screen.xml +++ b/res/layout/widgets_full_sheet_large_screen.xml @@ -24,7 +24,6 @@ android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@drawable/bg_widgets_full_sheet" android:focusable="true" android:importantForAccessibility="no"> diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml index 2819b990a9..b02e3e3c3f 100644 --- a/res/layout/widgets_full_sheet_paged_view.xml +++ b/res/layout/widgets_full_sheet_paged_view.xml @@ -47,6 +47,7 @@ android:layout_height="wrap_content" android:layout_below="@id/collapse_handle" android:paddingBottom="0dp" + android:clipToOutline="true" android:orientation="vertical"> public void getOutline(View view, Outline outline) { @Px final int bottomOffsetPx = (int) (ActivityAllAppsContainerView.this.getMeasuredHeight() - * SWIPE_ALL_APPS_TO_HOME_MIN_SCALE); + * PREDICTIVE_BACK_MIN_SCALE); outline.setRect( 0, 0, diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 866932ab16..df383bf9f6 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -21,7 +21,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; -import androidx.annotation.Px; import androidx.core.view.accessibility.AccessibilityEventCompat; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import androidx.core.view.accessibility.AccessibilityRecordCompat; @@ -145,19 +144,6 @@ public class AllAppsGridAdapter extends cic.isSelected())); } - /** - * We need to extend all apps' RecyclerView's bottom by 5% of view height to ensure extra - * roll(s) of app icons is rendered at the bottom, so that they can fill the bottom gap - * created during predictive back's scale animation from all apps to home. - */ - @Override - protected void calculateExtraLayoutSpace(RecyclerView.State state, int[] extraLayoutSpace) { - super.calculateExtraLayoutSpace(state, extraLayoutSpace); - @Px int extraSpacePx = (int) (getHeight() - * (1 - AllAppsTransitionController.SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) / 2); - extraLayoutSpace[1] = Math.max(extraLayoutSpace[1], extraSpacePx); - } - /** * Returns the number of rows before {@param adapterPosition}, including this position * which should not be counted towards the collection info. diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index b6187244f2..92c017ce88 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -61,6 +61,7 @@ import com.android.launcher3.touch.AllAppsSwipeController; import com.android.launcher3.util.MultiPropertyFactory; import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; import com.android.launcher3.util.MultiValueAlpha; +import com.android.launcher3.util.ScrollableLayoutManager; import com.android.launcher3.util.Themes; import com.android.launcher3.util.VibratorWrapper; import com.android.launcher3.views.ScrimView; @@ -79,8 +80,7 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener { // This constant should match the second derivative of the animator interpolator. public static final float INTERP_COEFF = 1.7f; - public static final float SWIPE_ALL_APPS_TO_HOME_MIN_SCALE = 0.9f; - private static final int REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS = 200; + public static final int REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS = 200; private static final float NAV_BAR_COLOR_FORCE_UPDATE_THRESHOLD = 0.1f; private static final float SWIPE_DRAG_COMMIT_THRESHOLD = @@ -280,8 +280,9 @@ public class AllAppsTransitionController float deceleratedProgress = Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(backProgress); - float scaleProgress = SWIPE_ALL_APPS_TO_HOME_MIN_SCALE - + (1 - SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) * (1 - deceleratedProgress); + float scaleProgress = ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE + + (1 - ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE) + * (1 - deceleratedProgress); mAllAppScale.updateValue(scaleProgress); } diff --git a/src/com/android/launcher3/util/ScrollableLayoutManager.java b/src/com/android/launcher3/util/ScrollableLayoutManager.java index 9bc4ddce38..cb6ecaa664 100644 --- a/src/com/android/launcher3/util/ScrollableLayoutManager.java +++ b/src/com/android/launcher3/util/ScrollableLayoutManager.java @@ -20,6 +20,7 @@ import android.util.SparseIntArray; import android.view.View; import androidx.annotation.NonNull; +import androidx.annotation.Px; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.Adapter; @@ -31,6 +32,10 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder; */ public class ScrollableLayoutManager extends GridLayoutManager { + public static final float PREDICTIVE_BACK_MIN_SCALE = 0.9f; + private static final float EXTRA_BOTTOM_SPACE_BY_HEIGHT_PERCENT = + (1 - PREDICTIVE_BACK_MIN_SCALE) / 2; + // keyed on item type protected final SparseIntArray mCachedSizes = new SparseIntArray(); @@ -111,6 +116,13 @@ public class ScrollableLayoutManager extends GridLayoutManager { return adapter == null ? 0 : getItemsHeight(adapter, adapter.getItemCount()); } + @Override + protected void calculateExtraLayoutSpace(RecyclerView.State state, int[] extraLayoutSpace) { + super.calculateExtraLayoutSpace(state, extraLayoutSpace); + @Px int extraSpacePx = (int) (getHeight() * EXTRA_BOTTOM_SPACE_BY_HEIGHT_PERCENT); + extraLayoutSpace[1] = Math.max(extraLayoutSpace[1], extraSpacePx); + } + /** * Returns the sum of the height, in pixels, of this list adapter's items from index * 0 (inclusive) until {@code untilIndex} (exclusive). If untilIndex is same as the itemCount, diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java index f73347a7b2..e2f1c04ced 100644 --- a/src/com/android/launcher3/views/AbstractSlideInView.java +++ b/src/com/android/launcher3/views/AbstractSlideInView.java @@ -17,15 +17,20 @@ package com.android.launcher3.views; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.LauncherAnimUtils.TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS; +import static com.android.launcher3.allapps.AllAppsTransitionController.REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS; import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; +import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.content.Context; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Property; import android.view.MotionEvent; @@ -33,10 +38,13 @@ import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; +import androidx.annotation.FloatRange; import androidx.annotation.Nullable; +import androidx.annotation.Px; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.touch.BaseSwipeDetector; import com.android.launcher3.touch.SingleAxisSwipeDetector; @@ -85,6 +93,10 @@ public abstract class AbstractSlideInView protected @Nullable OnCloseListener mOnCloseBeginListener; protected List mOnCloseListeners = new ArrayList<>(); + private final AnimatedFloat mSlidInViewScale = new AnimatedFloat(this::onScaleProgressChanged); + private boolean mIsBackProgressing; + @Nullable private Drawable mContentBackground; + public AbstractSlideInView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mActivityContext = ActivityContext.lookupContext(context); @@ -105,6 +117,10 @@ public abstract class AbstractSlideInView mColorScrim = scrimColor != -1 ? createColorScrim(context, scrimColor) : null; } + protected void setContentBackground(Drawable drawable) { + mContentBackground = drawable; + } + protected void attachToContainer() { if (mColorScrim != null) { getPopupContainer().addView(mColorScrim); @@ -132,6 +148,7 @@ public abstract class AbstractSlideInView if (mColorScrim != null) { mColorScrim.setAlpha(1 - mTranslationShift); } + invalidate(); } @Override @@ -161,6 +178,68 @@ public abstract class AbstractSlideInView return true; } + @Override + public void onBackProgressed(@FloatRange(from = 0.0, to = 1.0) float progress) { + super.onBackProgressed(progress); + float deceleratedProgress = + Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(progress); + mIsBackProgressing = progress > 0f; + mSlidInViewScale.updateValue(PREDICTIVE_BACK_MIN_SCALE + + (1 - PREDICTIVE_BACK_MIN_SCALE) * (1 - deceleratedProgress)); + } + + private void onScaleProgressChanged() { + float scaleProgress = mSlidInViewScale.value; + SCALE_PROPERTY.set(this, scaleProgress); + setClipChildren(!mIsBackProgressing); + mContent.setClipChildren(!mIsBackProgressing); + invalidate(); + } + + @Override + public void onBackInvoked() { + super.onBackInvoked(); + animateSlideInViewToNoScale(); + } + + @Override + public void onBackCancelled() { + super.onBackCancelled(); + animateSlideInViewToNoScale(); + } + + protected void animateSlideInViewToNoScale() { + mSlidInViewScale.animateToValue(1f) + .setDuration(REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS) + .start(); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + drawScaledBackground(canvas); + super.dispatchDraw(canvas); + } + + /** Draw scaled background during predictive back animation. */ + protected void drawScaledBackground(Canvas canvas) { + if (mContentBackground == null) { + return; + } + mContentBackground.setBounds( + mContent.getLeft(), + mContent.getTop() + (int) mContent.getTranslationY(), + mContent.getRight(), + mContent.getBottom() + (mIsBackProgressing ? getBottomOffsetPx() : 0)); + mContentBackground.draw(canvas); + } + + /** Return extra space revealed during predictive back animation. */ + @Px + protected int getBottomOffsetPx() { + return (int) (getMeasuredHeight() + * (1 - PREDICTIVE_BACK_MIN_SCALE) / 2); + } + /** * Returns {@code true} if the touch event is over the visible area of the bottom sheet. * diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java index bf521cc7eb..4099302c07 100644 --- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java @@ -113,6 +113,7 @@ public class WidgetsBottomSheet extends BaseWidgetSheet { } mWidgetCellHorizontalPadding = getResources().getDimensionPixelSize( R.dimen.widget_cell_horizontal_padding); + setContentBackground(getContext().getDrawable(R.drawable.bg_rounded_corner_bottom_sheet)); } @Override diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index bb80e9b80b..49b40648d0 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -17,9 +17,7 @@ package com.android.launcher3.widget.picker; import static android.view.View.MeasureSpec.makeMeasureSpec; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; -import static com.android.launcher3.allapps.AllAppsTransitionController.SWIPE_ALL_APPS_TO_HOME_MIN_SCALE; import static com.android.launcher3.config.FeatureFlags.LARGE_SCREEN_WIDGET_PICKER; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED; import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL; @@ -31,6 +29,7 @@ import android.content.Context; import android.content.pm.LauncherApps; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.Outline; import android.graphics.Rect; import android.os.Process; import android.os.UserHandle; @@ -42,6 +41,7 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewOutlineProvider; import android.view.WindowInsets; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; @@ -62,7 +62,6 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.model.UserManagerState; @@ -170,6 +169,18 @@ public class WidgetsFullSheet extends BaseWidgetSheet } }; + private final ViewOutlineProvider mViewOutlineProvider = new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRect( + 0, + 0, + view.getMeasuredWidth(), + view.getMeasuredHeight() + getBottomOffsetPx() + ); + } + }; + private final int mTabsHeight; private final int mWidgetSheetContentHorizontalPadding; @@ -195,6 +206,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet private int mOrientation; private @Nullable WidgetsRecyclerView mCurrentTouchEventRecyclerView; + private RecyclerViewFastScroller mFastScroller; + public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); DeviceProfile dp = Launcher.getLauncher(context).getDeviceProfile(); @@ -213,6 +226,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet mUserManagerState.init(UserCache.INSTANCE.get(context), context.getSystemService(UserManager.class)); + setContentBackground(getContext().getDrawable(R.drawable.bg_widgets_full_sheet)); } public WidgetsFullSheet(Context context, AttributeSet attrs) { @@ -224,6 +238,9 @@ public class WidgetsFullSheet extends BaseWidgetSheet super.onFinishInflate(); mContent = findViewById(R.id.container); + mContent.setOutlineProvider(mViewOutlineProvider); + mContent.setClipToOutline(true); + LayoutInflater layoutInflater = LayoutInflater.from(getContext()); int contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view : R.layout.widgets_full_sheet_recyclerview; @@ -233,14 +250,17 @@ public class WidgetsFullSheet extends BaseWidgetSheet } layoutInflater.inflate(contentLayoutRes, mContent, true); - RecyclerViewFastScroller fastScroller = findViewById(R.id.fast_scroller); + mFastScroller = findViewById(R.id.fast_scroller); if (mIsTwoPane) { - fastScroller.setVisibility(GONE); + mFastScroller.setVisibility(GONE); } mAdapters.get(AdapterHolder.PRIMARY).setup(findViewById(R.id.primary_widgets_list_view)); mAdapters.get(AdapterHolder.SEARCH).setup(findViewById(R.id.search_widgets_list_view)); if (mHasWorkProfile) { mViewPager = findViewById(R.id.widgets_view_pager); + mViewPager.setOutlineProvider(mViewOutlineProvider); + mViewPager.setClipToOutline(true); + mViewPager.setClipChildren(false); mViewPager.initParentViews(this); mViewPager.getPageIndicator().setOnActivePageChangedListener(this); mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.PRIMARY); @@ -352,11 +372,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet @Override public void onBackProgressed(@FloatRange(from = 0.0, to = 1.0) float progress) { - float deceleratedProgress = - Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(progress); - float scaleProgress = SWIPE_ALL_APPS_TO_HOME_MIN_SCALE - + (1 - SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) * (1 - deceleratedProgress); - SCALE_PROPERTY.set(this, scaleProgress); + super.onBackProgressed(progress); + mFastScroller.setVisibility(progress > 0 ? View.INVISIBLE : View.VISIBLE); } private void attachScrollbarToRecyclerView(WidgetsRecyclerView recyclerView) { @@ -862,6 +879,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet public void onBackInvoked() { if (mIsInSearchMode) { mSearchBar.reset(); + animateSlideInViewToNoScale(); } else { super.onBackInvoked(); } @@ -1006,6 +1024,9 @@ public class WidgetsFullSheet extends BaseWidgetSheet void setup(WidgetsRecyclerView recyclerView) { mWidgetsRecyclerView = recyclerView; + mWidgetsRecyclerView.setOutlineProvider(mViewOutlineProvider); + mWidgetsRecyclerView.setClipToOutline(true); + mWidgetsRecyclerView.setClipChildren(false); mWidgetsRecyclerView.setAdapter(mWidgetsListAdapter); mWidgetsRecyclerView.setItemAnimator(mWidgetsListItemAnimator); mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this); From e76291f55c8983e8e2563bbda61ec82098ad38ec Mon Sep 17 00:00:00 2001 From: Helen Cheuk Date: Tue, 14 Feb 2023 17:11:05 +0000 Subject: [PATCH 5/6] =?UTF-8?q?Pad=20large=20screen=20hotseat=20in=C2=A0RT?= =?UTF-8?q?L=C2=A03=20button=20nav=20mode=20by=20swapping=20left=20and=20r?= =?UTF-8?q?ight=20padding=20to=20avoid=20overlapping=20with=20nav=20button?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hotseat was overlapped with 3 navigation buttons in large screen RTL mode. This fix is to pad hotseat properly by swapping the left and right paddings in RTL mode Bug: 268178394 Test: manual Change-Id: I2b073a1b532bf3aae3ab1bec98ad70713742c672 --- src/com/android/launcher3/DeviceProfile.java | 24 ++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 2c34b3f822..e63b0540cd 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -1315,23 +1315,29 @@ public class DeviceProfile { hotseatBarSizePx - hotseatBarBottomPadding - hotseatCellHeightPx; int hotseatWidth = getHotseatRequiredWidth(); - int leftSpacing = (availableWidthPx - hotseatWidth) / 2; - int rightSpacing = leftSpacing; + int startSpacing; + int endSpacing; // Hotseat aligns to the left with nav buttons if (hotseatBarEndOffset > 0) { - leftSpacing = inlineNavButtonsEndSpacing; - rightSpacing = availableWidthPx - hotseatWidth - leftSpacing + hotseatBorderSpace; + startSpacing = inlineNavButtonsEndSpacing; + endSpacing = availableWidthPx - hotseatWidth - startSpacing + hotseatBorderSpace; + } else { + startSpacing = (availableWidthPx - hotseatWidth) / 2; + endSpacing = startSpacing; } + startSpacing += getAdditionalQsbSpace(); - hotseatBarPadding.set(leftSpacing, hotseatBarTopPadding, rightSpacing, - hotseatBarBottomPadding); - + hotseatBarPadding.top = hotseatBarTopPadding; + hotseatBarPadding.bottom = hotseatBarBottomPadding; boolean isRtl = Utilities.isRtl(context.getResources()); if (isRtl) { - hotseatBarPadding.right += getAdditionalQsbSpace(); + hotseatBarPadding.left = endSpacing; + hotseatBarPadding.right = startSpacing; } else { - hotseatBarPadding.left += getAdditionalQsbSpace(); + hotseatBarPadding.left = startSpacing; + hotseatBarPadding.right = endSpacing; } + } else if (isScalableGrid) { int sideSpacing = (availableWidthPx - hotseatQsbWidth) / 2; hotseatBarPadding.set(sideSpacing, From 9a647d53cb155b1f08519b1b257194b965812d7a Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Thu, 16 Feb 2023 18:56:13 +0000 Subject: [PATCH 6/6] Log stackstrace if updateIconAlphaForHome is called after Taskbar is destroyed Bug: 269409332 Test: manual Change-Id: I0ff4be33b1e621b0e1a837f8211eb70c27368be1 --- .../launcher3/taskbar/TaskbarLauncherStateController.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index 80cdbe98cb..64321192c6 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -454,6 +454,10 @@ import java.util.StringJoiner; } private void updateIconAlphaForHome(float alpha) { + if (mControllers.taskbarActivityContext.isDestroyed()) { + Log.e("b/260135164", "updateIconAlphaForHome is called after Taskbar is destroyed", + new Exception()); + } mIconAlphaForHome.setValue(alpha); boolean hotseatVisible = alpha == 0 || (!mControllers.uiController.isHotseatIconOnTopWhenAligned()