From 91c37bbd09432ae3c8a162d1e6dffe211bfcff43 Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Thu, 17 Feb 2022 10:48:30 -0800 Subject: [PATCH 01/25] Restrict the area for the input to unsatsh the taskbar The input to unstash the taskbar should only be 48dp more than taskbar_stashed_handle_width or 316dp for wich I created a new variable. Bug: 204166104 Test: Manually stashing and unstashing the taskbar. Change-Id: I94e2e289fcd1169ed0e38a0c45abca6c0ae5c502 --- quickstep/res/values/dimens.xml | 1 + .../TaskbarStashInputConsumer.java | 33 ++++++++++++++----- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 926e10c8b7..70e366fe2c 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -217,6 +217,7 @@ 35dp 24dp 220dp + 316dp 4dp 25dp 4dp diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java index dbe260ac65..7836ecef14 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java @@ -22,6 +22,7 @@ import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; +import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.taskbar.TaskbarActivityContext; import com.android.quickstep.InputConsumer; @@ -36,14 +37,20 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { private final GestureDetector mLongPressDetector; private final float mSquaredTouchSlop; + private float mDownX, mDownY; private boolean mCanceledUnstashHint; + private final float mUnstashArea; + private final float mScreenWidth; public TaskbarStashInputConsumer(Context context, InputConsumer delegate, InputMonitorCompat inputMonitor, TaskbarActivityContext taskbarActivityContext) { super(delegate, inputMonitor); mTaskbarActivityContext = taskbarActivityContext; mSquaredTouchSlop = Utilities.squaredTouchSlop(context); + mScreenWidth = context.getResources().getDisplayMetrics().widthPixels; + mUnstashArea = context.getResources() + .getDimensionPixelSize(R.dimen.taskbar_unstash_input_area); mLongPressDetector = new GestureDetector(context, new SimpleOnGestureListener() { @Override @@ -69,11 +76,13 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { final float y = ev.getRawY(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: - mDownX = x; - mDownY = y; - mTaskbarActivityContext.startTaskbarUnstashHint( - /* animateForward = */ true); - mCanceledUnstashHint = false; + if (isInArea(x)) { + mDownX = x; + mDownY = y; + mTaskbarActivityContext.startTaskbarUnstashHint( + /* animateForward = */ true); + mCanceledUnstashHint = false; + } break; case MotionEvent.ACTION_MOVE: if (!mCanceledUnstashHint @@ -95,10 +104,18 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { } } + private boolean isInArea(float x) { + float areaFromMiddle = mUnstashArea / 2.0f; + float distFromMiddle = Math.abs(mScreenWidth / 2.0f - x); + return distFromMiddle < areaFromMiddle; + } + private void onLongPressDetected(MotionEvent motionEvent) { - if (mTaskbarActivityContext != null - && mTaskbarActivityContext.onLongPressToUnstashTaskbar()) { - setActive(motionEvent); + if (mTaskbarActivityContext != null && isInArea(motionEvent.getRawX())) { + boolean taskBarPressed = mTaskbarActivityContext.onLongPressToUnstashTaskbar(); + if (taskBarPressed) { + setActive(motionEvent); + } } } } From 6badc405ad24b59b0dcca2a109c866c057869563 Mon Sep 17 00:00:00 2001 From: Abhilasha Chahal Date: Mon, 7 Feb 2022 14:00:12 +0000 Subject: [PATCH 02/25] Extract out common adapter logic to support different AllApps layouts Test: Manual tests. Refactoring, all existing tests should pass. Bug: 216150568 Change-Id: I1068e75d0b4a33d402a7d68e237d2484ab3a1e01 --- .../allapps/TaskbarAllAppsContainerView.java | 10 + .../allapps/ActivityAllAppsContainerView.java | 7 + .../launcher3/allapps/AllAppsGridAdapter.java | 341 ++---------------- .../allapps/AlphabeticalAppsList.java | 6 +- .../launcher3/allapps/BaseAllAppsAdapter.java | 333 +++++++++++++++++ .../allapps/BaseAllAppsContainerView.java | 13 +- .../search/AllAppsSearchBarController.java | 2 +- .../search/AppsSearchContainerLayout.java | 2 +- .../search/DefaultAppSearchAlgorithm.java | 2 +- 9 files changed, 391 insertions(+), 325 deletions(-) create mode 100644 src/com/android/launcher3/allapps/BaseAllAppsAdapter.java diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java index b36b9f1ffd..37cd753ba4 100644 --- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContainerView.java @@ -25,6 +25,9 @@ import android.view.WindowInsets; import androidx.recyclerview.widget.RecyclerView; import com.android.launcher3.allapps.AllAppsGridAdapter; +import com.android.launcher3.allapps.AlphabeticalAppsList; +import com.android.launcher3.allapps.BaseAdapterProvider; +import com.android.launcher3.allapps.BaseAllAppsAdapter; import com.android.launcher3.allapps.BaseAllAppsContainerView; import com.android.launcher3.allapps.search.SearchAdapterProvider; @@ -79,4 +82,11 @@ public class TaskbarAllAppsContainerView extends BaseAllAppsContainerView mAppsList, + BaseAdapterProvider[] adapterProviders) { + return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), mAppsList, + adapterProviders); + } } diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java index 114f8130c7..b94a61251b 100644 --- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java @@ -256,4 +256,11 @@ public class ActivityAllAppsContainerView extend layoutParams.removeRule(RelativeLayout.BELOW); layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP); } + + @Override + protected BaseAllAppsAdapter getAdapter(AlphabeticalAppsList mAppsList, + BaseAdapterProvider[] adapterProviders) { + return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), mAppsList, + adapterProviders); + } } diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index f1ca9c0c99..58df50ca82 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -15,36 +15,20 @@ */ package com.android.launcher3.allapps; -import static com.android.launcher3.touch.ItemLongClickListener.INSTANCE_ALL_APPS; - import android.content.Context; -import android.content.res.Resources; -import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnClickListener; -import android.view.View.OnFocusChangeListener; -import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; -import android.widget.TextView; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.core.view.accessibility.AccessibilityEventCompat; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import androidx.core.view.accessibility.AccessibilityRecordCompat; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.android.launcher3.BubbleTextView; -import com.android.launcher3.R; -import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.model.data.AppInfo; -import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.views.ActivityContext; -import java.util.Arrays; import java.util.List; /** @@ -53,111 +37,26 @@ import java.util.List; * @param Type of context inflating all apps. */ public class AllAppsGridAdapter extends - RecyclerView.Adapter { + BaseAllAppsAdapter { public static final String TAG = "AppsGridAdapter"; + private final GridLayoutManager mGridLayoutMgr; + private final GridSpanSizer mGridSizer; - // A normal icon - public static final int VIEW_TYPE_ICON = 1 << 1; - // The message shown when there are no filtered results - public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 2; - // The message to continue to a market search when there are no filtered results - public static final int VIEW_TYPE_SEARCH_MARKET = 1 << 3; - - // We use various dividers for various purposes. They share enough attributes to reuse layouts, - // but differ in enough attributes to require different view types - - // A divider that separates the apps list and the search market button - public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 4; - - // Common view type masks - public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER; - public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON; - - - private final BaseAdapterProvider[] mAdapterProviders; - - /** - * ViewHolder for each icon. - */ - public static class ViewHolder extends RecyclerView.ViewHolder { - - public ViewHolder(View v) { - super(v); - } + public AllAppsGridAdapter(T activityContext, LayoutInflater inflater, + AlphabeticalAppsList apps, BaseAdapterProvider[] adapterProviders) { + super(activityContext, inflater, apps, adapterProviders); + mGridSizer = new GridSpanSizer(); + mGridLayoutMgr = new AppsGridLayoutManager(mActivityContext); + mGridLayoutMgr.setSpanSizeLookup(mGridSizer); + setAppsPerRow(activityContext.getDeviceProfile().numShownAllAppsColumns); } /** - * Info about a particular adapter item (can be either section or app) + * Returns the grid layout manager. */ - public static class AdapterItem { - /** Common properties */ - // The index of this adapter item in the list - public int position; - // The type of this item - public int viewType; - - // The section name of this item. Note that there can be multiple items with different - // sectionNames in the same section - public String sectionName = null; - // The row that this item shows up on - public int rowIndex; - // The index of this app in the row - public int rowAppIndex; - // The associated ItemInfoWithIcon for the item - public ItemInfoWithIcon itemInfo = null; - // The index of this app not including sections - public int appIndex = -1; - // Search section associated to result - public DecorationInfo decorationInfo = null; - - /** - * Factory method for AppIcon AdapterItem - */ - public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo, - int appIndex) { - AdapterItem item = new AdapterItem(); - item.viewType = VIEW_TYPE_ICON; - item.position = pos; - item.sectionName = sectionName; - item.itemInfo = appInfo; - item.appIndex = appIndex; - return item; - } - - /** - * Factory method for empty search results view - */ - public static AdapterItem asEmptySearch(int pos) { - AdapterItem item = new AdapterItem(); - item.viewType = VIEW_TYPE_EMPTY_SEARCH; - item.position = pos; - return item; - } - - /** - * Factory method for a dividerView in AllAppsSearch - */ - public static AdapterItem asAllAppsDivider(int pos) { - AdapterItem item = new AdapterItem(); - item.viewType = VIEW_TYPE_ALL_APPS_DIVIDER; - item.position = pos; - return item; - } - - /** - * Factory method for a market search button - */ - public static AdapterItem asMarketSearch(int pos) { - AdapterItem item = new AdapterItem(); - item.viewType = VIEW_TYPE_SEARCH_MARKET; - item.position = pos; - return item; - } - - protected boolean isCountedForAccessibility() { - return viewType == VIEW_TYPE_ICON || viewType == VIEW_TYPE_SEARCH_MARKET; - } + public RecyclerView.LayoutManager getLayoutManager() { + return mGridLayoutMgr; } /** @@ -217,7 +116,7 @@ public class AllAppsGridAdapter extends */ private int getRowsNotForAccessibility(int adapterPosition) { List items = mApps.getAdapterItems(); - adapterPosition = Math.max(adapterPosition, mApps.getAdapterItems().size() - 1); + adapterPosition = Math.max(adapterPosition, items.size() - 1); int extraRows = 0; for (int i = 0; i <= adapterPosition; i++) { if (!isViewType(items.get(i).viewType, VIEW_TYPE_MASK_ICON)) { @@ -228,6 +127,20 @@ public class AllAppsGridAdapter extends } } + @Override + public void setAppsPerRow(int appsPerRow) { + mAppsPerRow = appsPerRow; + int totalSpans = mAppsPerRow; + for (BaseAdapterProvider adapterProvider : mAdapterProviders) { + for (int itemPerRow : adapterProvider.getSupportedItemsPerRowArray()) { + if (totalSpans % itemPerRow != 0) { + totalSpans *= itemPerRow; + } + } + } + mGridLayoutMgr.setSpanCount(totalSpans); + } + /** * Helper class to size the grid items. */ @@ -255,202 +168,4 @@ public class AllAppsGridAdapter extends } } } - - private final T mActivityContext; - private final LayoutInflater mLayoutInflater; - private final AlphabeticalAppsList mApps; - private final GridLayoutManager mGridLayoutMgr; - private final GridSpanSizer mGridSizer; - - private final OnClickListener mOnIconClickListener; - private OnLongClickListener mOnIconLongClickListener = INSTANCE_ALL_APPS; - - private int mAppsPerRow; - - private OnFocusChangeListener mIconFocusListener; - - // The text to show when there are no search results and no market search handler. - protected String mEmptySearchMessage; - // The click listener to send off to the market app, updated each time the search query changes. - private OnClickListener mMarketSearchClickListener; - - private final int mExtraHeight; - - public AllAppsGridAdapter(T activityContext, LayoutInflater inflater, - AlphabeticalAppsList apps, BaseAdapterProvider[] adapterProviders) { - Resources res = activityContext.getResources(); - mActivityContext = activityContext; - mApps = apps; - mEmptySearchMessage = res.getString(R.string.all_apps_loading_message); - mGridSizer = new GridSpanSizer(); - mGridLayoutMgr = new AppsGridLayoutManager(mActivityContext); - mGridLayoutMgr.setSpanSizeLookup(mGridSizer); - mLayoutInflater = inflater; - - mOnIconClickListener = mActivityContext.getItemOnClickListener(); - - mAdapterProviders = adapterProviders; - setAppsPerRow(mActivityContext.getDeviceProfile().numShownAllAppsColumns); - mExtraHeight = mActivityContext.getResources().getDimensionPixelSize( - R.dimen.all_apps_height_extra); - } - - public void setAppsPerRow(int appsPerRow) { - mAppsPerRow = appsPerRow; - int totalSpans = mAppsPerRow; - for (BaseAdapterProvider adapterProvider : mAdapterProviders) { - for (int itemPerRow : adapterProvider.getSupportedItemsPerRowArray()) { - if (totalSpans % itemPerRow != 0) { - totalSpans *= itemPerRow; - } - } - } - mGridLayoutMgr.setSpanCount(totalSpans); - } - - /** - * Sets the long click listener for icons - */ - public void setOnIconLongClickListener(@Nullable OnLongClickListener listener) { - mOnIconLongClickListener = listener; - } - - public static boolean isDividerViewType(int viewType) { - return isViewType(viewType, VIEW_TYPE_MASK_DIVIDER); - } - - public static boolean isIconViewType(int viewType) { - return isViewType(viewType, VIEW_TYPE_MASK_ICON); - } - - public static boolean isViewType(int viewType, int viewTypeMask) { - return (viewType & viewTypeMask) != 0; - } - - public void setIconFocusListener(OnFocusChangeListener focusListener) { - mIconFocusListener = focusListener; - } - - /** - * Sets the last search query that was made, used to show when there are no results and to also - * seed the intent for searching the market. - */ - public void setLastSearchQuery(String query, OnClickListener marketSearchClickListener) { - Resources res = mActivityContext.getResources(); - mEmptySearchMessage = res.getString(R.string.all_apps_no_search_results, query); - mMarketSearchClickListener = marketSearchClickListener; - } - - /** - * Returns the grid layout manager. - */ - public GridLayoutManager getLayoutManager() { - return mGridLayoutMgr; - } - - @Override - public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - switch (viewType) { - case VIEW_TYPE_ICON: - int layout = !FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get() ? R.layout.all_apps_icon - : R.layout.all_apps_icon_twoline; - BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( - layout, parent, false); - icon.setLongPressTimeoutFactor(1f); - icon.setOnFocusChangeListener(mIconFocusListener); - icon.setOnClickListener(mOnIconClickListener); - icon.setOnLongClickListener(mOnIconLongClickListener); - // Ensure the all apps icon height matches the workspace icons in portrait mode. - icon.getLayoutParams().height = - mActivityContext.getDeviceProfile().allAppsCellHeightPx; - if (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get()) { - icon.getLayoutParams().height += mExtraHeight; - } - return new ViewHolder(icon); - case VIEW_TYPE_EMPTY_SEARCH: - return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search, - parent, false)); - case VIEW_TYPE_SEARCH_MARKET: - View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market, - parent, false); - searchMarketView.setOnClickListener(mMarketSearchClickListener); - return new ViewHolder(searchMarketView); - case VIEW_TYPE_ALL_APPS_DIVIDER: - return new ViewHolder(mLayoutInflater.inflate( - R.layout.all_apps_divider, parent, false)); - default: - BaseAdapterProvider adapterProvider = getAdapterProvider(viewType); - if (adapterProvider != null) { - return adapterProvider.onCreateViewHolder(mLayoutInflater, parent, viewType); - } - throw new RuntimeException("Unexpected view type" + viewType); - } - } - - @Override - public void onBindViewHolder(ViewHolder holder, int position) { - switch (holder.getItemViewType()) { - case VIEW_TYPE_ICON: - AdapterItem adapterItem = mApps.getAdapterItems().get(position); - BubbleTextView icon = (BubbleTextView) holder.itemView; - icon.reset(); - if (adapterItem.itemInfo instanceof AppInfo) { - icon.applyFromApplicationInfo((AppInfo) adapterItem.itemInfo); - } else { - icon.applyFromItemInfoWithIcon(adapterItem.itemInfo); - } - break; - case VIEW_TYPE_EMPTY_SEARCH: - TextView emptyViewText = (TextView) holder.itemView; - emptyViewText.setText(mEmptySearchMessage); - emptyViewText.setGravity(mApps.hasNoFilteredResults() ? Gravity.CENTER : - Gravity.START | Gravity.CENTER_VERTICAL); - break; - case VIEW_TYPE_SEARCH_MARKET: - TextView searchView = (TextView) holder.itemView; - if (mMarketSearchClickListener != null) { - searchView.setVisibility(View.VISIBLE); - } else { - searchView.setVisibility(View.GONE); - } - break; - case VIEW_TYPE_ALL_APPS_DIVIDER: - // nothing to do - break; - default: - BaseAdapterProvider adapterProvider = getAdapterProvider(holder.getItemViewType()); - if (adapterProvider != null) { - adapterProvider.onBindView(holder, position); - } - } - } - - @Override - public void onViewRecycled(@NonNull ViewHolder holder) { - super.onViewRecycled(holder); - } - - @Override - public boolean onFailedToRecycleView(ViewHolder holder) { - // Always recycle and we will reset the view when it is bound - return true; - } - - @Override - public int getItemCount() { - return mApps.getAdapterItems().size(); - } - - @Override - public int getItemViewType(int position) { - AdapterItem item = mApps.getAdapterItems().get(position); - return item.viewType; - } - - @Nullable - private BaseAdapterProvider getAdapterProvider(int viewType) { - return Arrays.stream(mAdapterProviders).filter( - adapterProvider -> adapterProvider.isViewSupported(viewType)).findFirst().orElse( - null); - } } diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 1a1d5211c2..a4a58b5f3a 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -18,7 +18,7 @@ package com.android.launcher3.allapps; import android.content.Context; -import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem; +import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.util.ItemInfoMatcher; @@ -82,7 +82,7 @@ public class AlphabeticalAppsList implement // The of ordered component names as a result of a search query private ArrayList mSearchResults; - private AllAppsGridAdapter mAdapter; + private BaseAllAppsAdapter mAdapter; private AppInfoComparator mAppNameComparator; private final int mNumAppsPerRow; private int mNumAppRowsInAdapter; @@ -106,7 +106,7 @@ public class AlphabeticalAppsList implement /** * Sets the adapter to notify when this dataset changes. */ - public void setAdapter(AllAppsGridAdapter adapter) { + public void setAdapter(BaseAllAppsAdapter adapter) { mAdapter = adapter; } diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java new file mode 100644 index 0000000000..1d1960d6e2 --- /dev/null +++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.allapps; + +import static com.android.launcher3.touch.ItemLongClickListener.INSTANCE_ALL_APPS; + +import android.content.Context; +import android.content.res.Resources; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnFocusChangeListener; +import android.view.View.OnLongClickListener; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.R; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.ItemInfoWithIcon; +import com.android.launcher3.views.ActivityContext; + +import java.util.Arrays; + +/** + * Adapter for all the apps. + * + * @param Type of context inflating all apps. + */ +public abstract class BaseAllAppsAdapter extends + RecyclerView.Adapter { + + public static final String TAG = "BaseAllAppsAdapter"; + + // A normal icon + public static final int VIEW_TYPE_ICON = 1 << 1; + // The message shown when there are no filtered results + public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 2; + // The message to continue to a market search when there are no filtered results + public static final int VIEW_TYPE_SEARCH_MARKET = 1 << 3; + + // We use various dividers for various purposes. They share enough attributes to reuse layouts, + // but differ in enough attributes to require different view types + + // A divider that separates the apps list and the search market button + public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 4; + + // Common view type masks + public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER; + public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON; + + + protected final BaseAdapterProvider[] mAdapterProviders; + + /** + * ViewHolder for each icon. + */ + public static class ViewHolder extends RecyclerView.ViewHolder { + + public ViewHolder(View v) { + super(v); + } + } + + /** Sets the number of apps to be displayed in one row of the all apps screen. */ + public abstract void setAppsPerRow(int appsPerRow); + + /** + * Info about a particular adapter item (can be either section or app) + */ + public static class AdapterItem { + /** Common properties */ + // The index of this adapter item in the list + public int position; + // The type of this item + public int viewType; + + // The section name of this item. Note that there can be multiple items with different + // sectionNames in the same section + public String sectionName = null; + // The row that this item shows up on + public int rowIndex; + // The index of this app in the row + public int rowAppIndex; + // The associated ItemInfoWithIcon for the item + public ItemInfoWithIcon itemInfo = null; + // The index of this app not including sections + public int appIndex = -1; + // Search section associated to result + public DecorationInfo decorationInfo = null; + + /** + * Factory method for AppIcon AdapterItem + */ + public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo, + int appIndex) { + AdapterItem item = new AdapterItem(); + item.viewType = VIEW_TYPE_ICON; + item.position = pos; + item.sectionName = sectionName; + item.itemInfo = appInfo; + item.appIndex = appIndex; + return item; + } + + /** + * Factory method for empty search results view + */ + public static AdapterItem asEmptySearch(int pos) { + AdapterItem item = new AdapterItem(); + item.viewType = VIEW_TYPE_EMPTY_SEARCH; + item.position = pos; + return item; + } + + /** + * Factory method for a dividerView in AllAppsSearch + */ + public static AdapterItem asAllAppsDivider(int pos) { + AdapterItem item = new AdapterItem(); + item.viewType = VIEW_TYPE_ALL_APPS_DIVIDER; + item.position = pos; + return item; + } + + /** + * Factory method for a market search button + */ + public static AdapterItem asMarketSearch(int pos) { + AdapterItem item = new AdapterItem(); + item.viewType = VIEW_TYPE_SEARCH_MARKET; + item.position = pos; + return item; + } + + protected boolean isCountedForAccessibility() { + return viewType == VIEW_TYPE_ICON || viewType == VIEW_TYPE_SEARCH_MARKET; + } + } + + protected final T mActivityContext; + protected final AlphabeticalAppsList mApps; + // The text to show when there are no search results and no market search handler. + protected String mEmptySearchMessage; + protected int mAppsPerRow; + + private final LayoutInflater mLayoutInflater; + private final OnClickListener mOnIconClickListener; + private OnLongClickListener mOnIconLongClickListener = INSTANCE_ALL_APPS; + private OnFocusChangeListener mIconFocusListener; + // The click listener to send off to the market app, updated each time the search query changes. + private OnClickListener mMarketSearchClickListener; + private final int mExtraHeight; + + public BaseAllAppsAdapter(T activityContext, LayoutInflater inflater, + AlphabeticalAppsList apps, BaseAdapterProvider[] adapterProviders) { + Resources res = activityContext.getResources(); + mActivityContext = activityContext; + mApps = apps; + mEmptySearchMessage = res.getString(R.string.all_apps_loading_message); + mLayoutInflater = inflater; + + mOnIconClickListener = mActivityContext.getItemOnClickListener(); + + mAdapterProviders = adapterProviders; + mExtraHeight = res.getDimensionPixelSize(R.dimen.all_apps_height_extra); + } + + /** + * Sets the long click listener for icons + */ + public void setOnIconLongClickListener(@Nullable OnLongClickListener listener) { + mOnIconLongClickListener = listener; + } + + /** Checks if the passed viewType represents all apps divider. */ + public static boolean isDividerViewType(int viewType) { + return isViewType(viewType, VIEW_TYPE_MASK_DIVIDER); + } + + /** Checks if the passed viewType represents all apps icon. */ + public static boolean isIconViewType(int viewType) { + return isViewType(viewType, VIEW_TYPE_MASK_ICON); + } + + public void setIconFocusListener(OnFocusChangeListener focusListener) { + mIconFocusListener = focusListener; + } + + /** + * Sets the last search query that was made, used to show when there are no results and to also + * seed the intent for searching the market. + */ + public void setLastSearchQuery(String query, OnClickListener marketSearchClickListener) { + Resources res = mActivityContext.getResources(); + mEmptySearchMessage = res.getString(R.string.all_apps_no_search_results, query); + mMarketSearchClickListener = marketSearchClickListener; + } + + /** + * Returns the layout manager. + */ + public abstract RecyclerView.LayoutManager getLayoutManager(); + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + switch (viewType) { + case VIEW_TYPE_ICON: + int layout = !FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get() ? R.layout.all_apps_icon + : R.layout.all_apps_icon_twoline; + BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( + layout, parent, false); + icon.setLongPressTimeoutFactor(1f); + icon.setOnFocusChangeListener(mIconFocusListener); + icon.setOnClickListener(mOnIconClickListener); + icon.setOnLongClickListener(mOnIconLongClickListener); + // Ensure the all apps icon height matches the workspace icons in portrait mode. + icon.getLayoutParams().height = + mActivityContext.getDeviceProfile().allAppsCellHeightPx; + if (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get()) { + icon.getLayoutParams().height += mExtraHeight; + } + return new ViewHolder(icon); + case VIEW_TYPE_EMPTY_SEARCH: + return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search, + parent, false)); + case VIEW_TYPE_SEARCH_MARKET: + View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market, + parent, false); + searchMarketView.setOnClickListener(mMarketSearchClickListener); + return new ViewHolder(searchMarketView); + case VIEW_TYPE_ALL_APPS_DIVIDER: + return new ViewHolder(mLayoutInflater.inflate( + R.layout.all_apps_divider, parent, false)); + default: + BaseAdapterProvider adapterProvider = getAdapterProvider(viewType); + if (adapterProvider != null) { + return adapterProvider.onCreateViewHolder(mLayoutInflater, parent, viewType); + } + throw new RuntimeException("Unexpected view type" + viewType); + } + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + switch (holder.getItemViewType()) { + case VIEW_TYPE_ICON: + AdapterItem adapterItem = mApps.getAdapterItems().get(position); + BubbleTextView icon = (BubbleTextView) holder.itemView; + icon.reset(); + if (adapterItem.itemInfo instanceof AppInfo) { + icon.applyFromApplicationInfo((AppInfo) adapterItem.itemInfo); + } else { + icon.applyFromItemInfoWithIcon(adapterItem.itemInfo); + } + break; + case VIEW_TYPE_EMPTY_SEARCH: + TextView emptyViewText = (TextView) holder.itemView; + emptyViewText.setText(mEmptySearchMessage); + emptyViewText.setGravity(mApps.hasNoFilteredResults() ? Gravity.CENTER : + Gravity.START | Gravity.CENTER_VERTICAL); + break; + case VIEW_TYPE_SEARCH_MARKET: + TextView searchView = (TextView) holder.itemView; + if (mMarketSearchClickListener != null) { + searchView.setVisibility(View.VISIBLE); + } else { + searchView.setVisibility(View.GONE); + } + break; + case VIEW_TYPE_ALL_APPS_DIVIDER: + // nothing to do + break; + default: + BaseAdapterProvider adapterProvider = getAdapterProvider(holder.getItemViewType()); + if (adapterProvider != null) { + adapterProvider.onBindView(holder, position); + } + } + } + + @Override + public void onViewRecycled(@NonNull ViewHolder holder) { + super.onViewRecycled(holder); + } + + @Override + public boolean onFailedToRecycleView(ViewHolder holder) { + // Always recycle and we will reset the view when it is bound + return true; + } + + @Override + public int getItemCount() { + return mApps.getAdapterItems().size(); + } + + @Override + public int getItemViewType(int position) { + AdapterItem item = mApps.getAdapterItems().get(position); + return item.viewType; + } + + protected static boolean isViewType(int viewType, int viewTypeMask) { + return (viewType & viewTypeMask) != 0; + } + + @Nullable + protected BaseAdapterProvider getAdapterProvider(int viewType) { + return Arrays.stream(mAdapterProviders).filter( + adapterProvider -> adapterProvider.isViewSupported(viewType)).findFirst().orElse( + null); + } +} diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java index bfc75153a5..f542d8eff8 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java @@ -44,7 +44,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.graphics.ColorUtils; -import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.android.launcher3.DeviceProfile; @@ -697,18 +696,21 @@ public abstract class BaseAllAppsContainerView mAppsList, + BaseAdapterProvider[] adapterProviders); + protected int getHeaderBottom() { return (int) getTranslationY(); } - /** Holds a {@link AllAppsGridAdapter} and related fields. */ + /** Holds a {@link BaseAllAppsAdapter} and related fields. */ public class AdapterHolder { public static final int MAIN = 0; public static final int WORK = 1; private final boolean mIsWork; - public final AllAppsGridAdapter adapter; - final LinearLayoutManager mLayoutManager; + public final BaseAllAppsAdapter adapter; + final RecyclerView.LayoutManager mLayoutManager; final AlphabeticalAppsList mAppsList; final Rect mPadding = new Rect(); AllAppsRecyclerView mRecyclerView; @@ -724,8 +726,7 @@ public abstract class BaseAllAppsContainerView(mActivityContext, getLayoutInflater(), mAppsList, - adapterProviders); + adapter = getAdapter(mAppsList, adapterProviders); mAppsList.setAdapter(adapter); mLayoutManager = adapter.getLayoutManager(); } diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java index 0137e2a2c0..fd8945a0f6 100644 --- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java +++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java @@ -33,7 +33,7 @@ import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.ExtendedEditText; import com.android.launcher3.Launcher; import com.android.launcher3.Utilities; -import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem; +import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.search.SearchAlgorithm; import com.android.launcher3.search.SearchCallback; diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java index 4a886a457f..cb459eacb3 100644 --- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java +++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java @@ -38,9 +38,9 @@ import com.android.launcher3.ExtendedEditText; import com.android.launcher3.Insettable; import com.android.launcher3.R; import com.android.launcher3.allapps.ActivityAllAppsContainerView; -import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem; import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.allapps.AlphabeticalAppsList; +import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem; import com.android.launcher3.allapps.SearchUiManager; import com.android.launcher3.search.SearchCallback; diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java index 1f854c6787..222c8fea47 100644 --- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java +++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java @@ -23,7 +23,7 @@ import android.os.Handler; import androidx.annotation.AnyThread; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem; +import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem; import com.android.launcher3.model.AllAppsList; import com.android.launcher3.model.BaseModelUpdateTask; import com.android.launcher3.model.BgDataModel; From 01bbf1a8f3f2e5882278cd427611fdb4ebf427f9 Mon Sep 17 00:00:00 2001 From: Evan Rosky Date: Wed, 2 Mar 2022 16:18:44 -0800 Subject: [PATCH 03/25] Switch to persist.wm.debug For use with systemui flag flipping Bug: 219067621 Test: manual Change-Id: I5477b961bfdbe4e9103173c1c7f8daf03ce5eee4 --- quickstep/src/com/android/quickstep/TaskAnimationManager.java | 4 ++-- .../src/com/android/quickstep/AbstractQuickStepTest.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index 5e298f4b8d..fa1eb61e29 100644 --- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java +++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java @@ -49,9 +49,9 @@ import java.util.HashMap; public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener { public static final boolean ENABLE_SHELL_TRANSITIONS = - SystemProperties.getBoolean("persist.debug.shell_transit", false); + SystemProperties.getBoolean("persist.wm.debug.shell_transit", false); public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS - && SystemProperties.getBoolean("persist.debug.shell_transit_rotate", false); + && SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false); private RecentsAnimationController mController; private RecentsAnimationCallbacks mCallbacks; diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java index fb7fda13a4..09f0858618 100644 --- a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java +++ b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java @@ -36,7 +36,7 @@ import org.junit.rules.TestRule; */ public abstract class AbstractQuickStepTest extends AbstractLauncherUiTest { static final boolean ENABLE_SHELL_TRANSITIONS = - SystemProperties.getBoolean("persist.debug.shell_transit", false); + SystemProperties.getBoolean("persist.wm.debug.shell_transit", false); @Override protected TestRule getRulesInsideActivityMonitor() { return RuleChain. From 3e7415df56206e3fae71cff3f521549f84baff6a Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Tue, 25 Jan 2022 00:12:36 +0000 Subject: [PATCH 04/25] Keep reporting > 0 insets on home screen Technically mControllers.stashedHandleViewController.isStashedHandleVisible() is false on the home screen (since we unstash), so now we also check isInApp(). This ensures that we always return the same insets when on the home screen - and the insets we report are the same as when you launch an app, to ensure seamless transitions. The value itself shouldn't matter to launcher as long as it is static, since launcher overrides the insets due to taskbar anyway, but we need to keep the value static to avoid configuration change. Test: Open a website in Chrome, stash taskbar, then launch Chrome from home screen and ensure content doesn't jump during the transition Fixes: 221238308 Change-Id: I81e320b3a8d32ffe78441be5dd8f15a586d3b842 --- .../taskbar/TaskbarStashController.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index 6bc4c0a019..473be9ea7e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -250,7 +250,11 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba * Returns whether the taskbar is currently visible and in an app. */ public boolean isInAppAndNotStashed() { - return !mIsStashed && (mState & FLAG_IN_APP) != 0; + return !mIsStashed && isInApp(); + } + + private boolean isInApp() { + return hasAnyFlag(FLAG_IN_APP); } /** @@ -266,8 +270,15 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba return mUnstashedHeight; } boolean isAnimating = mAnimator != null && mAnimator.isStarted(); - return mControllers.stashedHandleViewController.isStashedHandleVisible() || isAnimating - ? mStashedHeight : 0; + if (!mControllers.stashedHandleViewController.isStashedHandleVisible() + && isInApp() + && !isAnimating) { + // We are in a settled state where we're not showing the handle even though taskbar + // is stashed. This can happen for example when home button is disabled (see + // StashedHandleViewController#setIsHomeButtonDisabled()). + return 0; + } + return mStashedHeight; } return mUnstashedHeight; } From 789a6a95cc4f087f5fe5e1c8202e0ee03ffcab8c Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Fri, 4 Mar 2022 12:06:09 -0800 Subject: [PATCH 05/25] Add method to pause expensive view updates during the app launch aimation Fixes: 220922269 Test: Manual Change-Id: I39066f575c0ddfc4868ab9e27149e2bd9492b39c --- .../android/launcher3/QuickstepTransitionManager.java | 6 +++--- src/com/android/launcher3/Launcher.java | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index c4a3fb5572..803dee4beb 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -579,8 +579,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } } - // Pause page indicator animations as they lead to layer trashing. - mLauncher.getWorkspace().getPageIndicator().pauseAnimations(); + // Pause expensive view updates as they can lead to layer thrashing and skipped frames. + mLauncher.pauseExpensiveViewUpdates(); endListener = () -> { viewsToAnimate.forEach(view -> { @@ -590,7 +590,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener if (scrimEnabled) { mLauncher.getScrimView().setBackgroundColor(Color.TRANSPARENT); } - mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd(); + mLauncher.resumeExpensiveViewUpdates(); }; } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 8fb2f53ec6..5c5aee579d 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -3192,4 +3192,15 @@ public class Launcher extends StatefulActivity implements Launche public ArrowPopup getOptionsPopup() { return findViewById(R.id.popup_container); } + + /** Pauses view updates that should not be run during the app launch animation. */ + public void pauseExpensiveViewUpdates() { + // Pause page indicator animations as they lead to layer trashing. + getWorkspace().getPageIndicator().pauseAnimations(); + } + + /** Resumes view updates at the end of the app launch animation. */ + public void resumeExpensiveViewUpdates() { + getWorkspace().getPageIndicator().skipAnimationsToEnd(); + } } From 8dd3e6e7ce351c0f1cc2b5d22cf7cb5b857a9f1e Mon Sep 17 00:00:00 2001 From: Xiang Wang Date: Wed, 16 Feb 2022 14:22:10 -0800 Subject: [PATCH 06/25] Move the BitmapUtil to com.android.internal package Bug: 219992742 Test: N/A Change-Id: I1fa145c18799904a63613ab861a7635cc74dcfce --- .../src/com/android/quickstep/util/ImageActionUtils.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java index 51a9915523..63d5b0dd50 100644 --- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java +++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java @@ -48,10 +48,10 @@ import androidx.annotation.WorkerThread; import androidx.core.content.FileProvider; import com.android.internal.app.ChooserActivity; +import com.android.internal.util.ScreenshotHelper; import com.android.launcher3.BuildConfig; import com.android.quickstep.SystemUiProxy; import com.android.systemui.shared.recents.model.Task; -import com.android.systemui.shared.recents.utilities.BitmapUtil; import java.io.File; import java.io.FileOutputStream; @@ -77,7 +77,8 @@ public class ImageActionUtils { public static void saveScreenshot(SystemUiProxy systemUiProxy, Bitmap screenshot, Rect screenshotBounds, Insets visibleInsets, Task.TaskKey task) { - systemUiProxy.handleImageBundleAsScreenshot(BitmapUtil.hardwareBitmapToBundle(screenshot), + systemUiProxy.handleImageBundleAsScreenshot( + ScreenshotHelper.HardwareBitmapBundler.hardwareBitmapToBundle(screenshot), screenshotBounds, visibleInsets, task); } From a0fb57dc43128e9087a146a33f3300404e41060d Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Mon, 7 Mar 2022 14:41:32 -0800 Subject: [PATCH 07/25] Let BubbleTextHolder extend IconLabelDotView This properly hides just the icon/dot and leaves the text for All Apps views during the app launch/exit animation. Bug: 213306709 Test: open/close apps on workspace, open/swipe back apps in all apps Change-Id: I327ce3e41298e50e34b8809491fc6d97a89f9f96 --- .../android/launcher3/views/BubbleTextHolder.java | 12 +++++++++++- .../android/launcher3/views/FloatingIconView.java | 14 +++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/com/android/launcher3/views/BubbleTextHolder.java b/src/com/android/launcher3/views/BubbleTextHolder.java index 1cb27e1649..76c465cbc8 100644 --- a/src/com/android/launcher3/views/BubbleTextHolder.java +++ b/src/com/android/launcher3/views/BubbleTextHolder.java @@ -22,7 +22,7 @@ import com.android.launcher3.model.data.ItemInfoWithIcon; /** * Views that contain {@link BubbleTextView} should implement this interface. */ -public interface BubbleTextHolder { +public interface BubbleTextHolder extends IconLabelDotView { BubbleTextView getBubbleText(); /** @@ -32,4 +32,14 @@ public interface BubbleTextHolder { */ default void onItemInfoUpdated(ItemInfoWithIcon itemInfo) { } + + @Override + default void setIconVisible(boolean visible) { + getBubbleText().setIconVisible(visible); + } + + @Override + default void setForceHideDot(boolean hide) { + getBubbleText().setForceHideDot(hide); + } } diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 0f69530ec0..1c4ca8c6a4 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -587,7 +587,7 @@ public class FloatingIconView extends FrameLayout implements view.matchPositionOf(launcher, originalView, isOpening, positionOut); // We need to add it to the overlay, but keep it invisible until animation starts.. - view.setVisibility(INVISIBLE); + setIconAndDotVisible(view, false); parent.addView(view); dragLayer.addView(view.mListenerView); view.mListenerView.setListener(view::fastFinish); @@ -596,16 +596,8 @@ public class FloatingIconView extends FrameLayout implements view.mEndRunnable = null; if (hideOriginal) { - if (isOpening) { - setIconAndDotVisible(originalView, true); - view.finish(dragLayer); - } else { - originalView.setVisibility(VISIBLE); - if (originalView instanceof IconLabelDotView) { - setIconAndDotVisible(originalView, true); - } - view.finish(dragLayer); - } + setIconAndDotVisible(originalView, true); + view.finish(dragLayer); } else { view.finish(dragLayer); } From 64b3497301e049a0f9b4d4e513ad560f00beb239 Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Mon, 7 Mar 2022 12:46:36 -0800 Subject: [PATCH 08/25] Fixing crashloop where LauncherActivityInfo can be null during icon query. The previous icon loading path checked that the LauncherActivityInfo was not null and did nothing otherwise. Updating bulk icon loading path to do the same. Fixes: 223219500 Test: manual Change-Id: I79b7f15c65183f42ed6a23fec05558c250150cb6 --- src/com/android/launcher3/icons/IconCache.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java index 308a8f2e5d..5ece4a62dd 100644 --- a/src/com/android/launcher3/icons/IconCache.java +++ b/src/com/android/launcher3/icons/IconCache.java @@ -440,7 +440,7 @@ public class IconCache extends BaseIconCache { cn, sectionKey.first); } - if (loadFallbackTitle && TextUtils.isEmpty(entry.title)) { + if (loadFallbackTitle && TextUtils.isEmpty(entry.title) && lai != null) { loadFallbackTitle( lai, entry, From 04db7091c7d144a3cd0c4578a9f45eaf031f1bb9 Mon Sep 17 00:00:00 2001 From: Brian Isganitis Date: Mon, 7 Mar 2022 18:05:55 -0800 Subject: [PATCH 09/25] Close Taskbar AFVs when locking screen. Fix: 223288975 Test: Manual Change-Id: Ib810b495064922d87391aa0b61b79efe10ada40c --- .../launcher3/taskbar/TaskbarKeyguardController.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java index 991bcec376..56648eac38 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarKeyguardController.java @@ -1,5 +1,6 @@ package com.android.launcher3.taskbar; +import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING; @@ -14,6 +15,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import com.android.launcher3.AbstractFloatingView; import com.android.systemui.shared.system.QuickStepContract; import java.io.PrintWriter; @@ -39,6 +41,7 @@ public class TaskbarKeyguardController implements TaskbarControllers.LoggableTas @Override public void onReceive(Context context, Intent intent) { mIsScreenOff = true; + AbstractFloatingView.closeOpenViews(mContext, false, TYPE_ALL); } }; @@ -71,6 +74,10 @@ public class TaskbarKeyguardController implements TaskbarControllers.LoggableTas mNavbarButtonsViewController.setKeyguardVisible(keyguardShowing || dozing, keyguardOccluded); updateIconsForBouncer(); + + if (keyguardShowing) { + AbstractFloatingView.closeOpenViews(mContext, true, TYPE_ALL); + } } public boolean isScreenOff() { From 39334f4013fbdc51ccca758a52118c011c2b7cc4 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Mon, 7 Mar 2022 18:15:06 -0800 Subject: [PATCH 10/25] Allow icons to take up full width in all cases where width > height. Previously we only let the icons take up the max width if the device was in vertical bar layout. For tablets this meant that the icons would be smaller than the actual window crop. We want the full width in any cases where the profile width is greater than the height, so created a new method to check for that. Bug: 203157974 Test: phone/tablet in portrait/landscape Change-Id: I467f142bac87ec7c3b369c01f8d9c96ddf74fc76 --- .../SwipeUpGestureTutorialController.java | 3 +-- .../android/launcher3/views/ClipIconView.java | 27 +++++++++---------- .../launcher3/views/FloatingIconView.java | 11 ++++---- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java index 9a101b9651..2db4c20d02 100644 --- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java @@ -336,8 +336,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { 1f - SHAPE_PROGRESS_DURATION /* shapeProgressStart */, radius, 255, false, /* isOpening */ - mFakeIconView, mDp, - false /* isVerticalBarLayout */); + mFakeIconView, mDp); mFakeIconView.setAlpha(1); mFakeTaskView.setAlpha(getWindowAlpha(progress)); mFakePreviousTaskView.setAlpha(getWindowAlpha(progress)); diff --git a/src/com/android/launcher3/views/ClipIconView.java b/src/com/android/launcher3/views/ClipIconView.java index a66b3f942e..d1f90e987f 100644 --- a/src/com/android/launcher3/views/ClipIconView.java +++ b/src/com/android/launcher3/views/ClipIconView.java @@ -147,8 +147,7 @@ public class ClipIconView extends View implements ClipPathView { * Update the icon UI to match the provided parameters during an animation frame */ public void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius, - int fgIconAlpha, boolean isOpening, View container, DeviceProfile dp, - boolean isVerticalBarLayout) { + int fgIconAlpha, boolean isOpening, View container, DeviceProfile dp) { MarginLayoutParams lp = (MarginLayoutParams) container.getLayoutParams(); float dX = mIsRtl @@ -169,7 +168,7 @@ public class ClipIconView extends View implements ClipPathView { } update(rect, progress, shapeProgressStart, cornerRadius, fgIconAlpha, isOpening, scale, - minSize, lp, isVerticalBarLayout, dp); + minSize, lp, dp); container.setPivotX(0); container.setPivotY(0); @@ -181,7 +180,7 @@ public class ClipIconView extends View implements ClipPathView { private void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius, int fgIconAlpha, boolean isOpening, float scale, float minSize, - MarginLayoutParams parentLp, boolean isVerticalBarLayout, DeviceProfile dp) { + MarginLayoutParams parentLp, DeviceProfile dp) { float dX = mIsRtl ? rect.left - (dp.widthPx - parentLp.getMarginStart() - parentLp.width) : rect.left - parentLp.getMarginStart(); @@ -193,7 +192,7 @@ public class ClipIconView extends View implements ClipPathView { float shapeRevealProgress = boundToRange(mapToRange(max(shapeProgressStart, progress), shapeProgressStart, 1f, 0, toMax, LINEAR), 0, 1); - if (isVerticalBarLayout) { + if (dp.isLandscape) { mOutline.right = (int) (rect.width() / scale); } else { mOutline.bottom = (int) (rect.height() / scale); @@ -218,16 +217,16 @@ public class ClipIconView extends View implements ClipPathView { mRevealAnimator.setCurrentFraction(shapeRevealProgress); } - float drawableScale = (isVerticalBarLayout ? mOutline.width() : mOutline.height()) + float drawableScale = (dp.isLandscape ? mOutline.width() : mOutline.height()) / minSize; - setBackgroundDrawableBounds(drawableScale, isVerticalBarLayout); + setBackgroundDrawableBounds(drawableScale, dp.isLandscape); if (isOpening) { // Center align foreground int height = mFinalDrawableBounds.height(); int width = mFinalDrawableBounds.width(); - int diffY = isVerticalBarLayout ? 0 + int diffY = dp.isLandscape ? 0 : (int) (((height * drawableScale) - height) / 2); - int diffX = isVerticalBarLayout ? (int) (((width * drawableScale) - width) / 2) + int diffX = dp.isLandscape ? (int) (((width * drawableScale) - width) / 2) : 0; sTmpRect.set(mFinalDrawableBounds); sTmpRect.offset(diffX, diffY); @@ -247,11 +246,11 @@ public class ClipIconView extends View implements ClipPathView { invalidateOutline(); } - private void setBackgroundDrawableBounds(float scale, boolean isVerticalBarLayout) { + private void setBackgroundDrawableBounds(float scale, boolean isLandscape) { sTmpRect.set(mFinalDrawableBounds); Utilities.scaleRectAboutCenter(sTmpRect, scale); // Since the drawable is at the top of the view, we need to offset to keep it centered. - if (isVerticalBarLayout) { + if (isLandscape) { sTmpRect.offsetTo((int) (mFinalDrawableBounds.left * scale), sTmpRect.top); } else { sTmpRect.offsetTo(sTmpRect.left, (int) (mFinalDrawableBounds.top * scale)); @@ -269,7 +268,7 @@ public class ClipIconView extends View implements ClipPathView { * Sets the icon for this view as part of initial setup */ public void setIcon(@Nullable Drawable drawable, int iconOffset, MarginLayoutParams lp, - boolean isOpening, boolean isVerticalBarLayout, DeviceProfile dp) { + boolean isOpening, DeviceProfile dp) { mIsAdaptiveIcon = drawable instanceof AdaptiveIconDrawable; if (mIsAdaptiveIcon) { boolean isFolderIcon = drawable instanceof FolderAdaptiveIcon; @@ -304,7 +303,7 @@ public class ClipIconView extends View implements ClipPathView { Utilities.scaleRectAboutCenter(mStartRevealRect, IconShape.getNormalizationScale()); } - if (isVerticalBarLayout) { + if (dp.isLandscape) { lp.width = (int) Math.max(lp.width, lp.height * dp.aspectRatio); } else { lp.height = (int) Math.max(lp.height, lp.width * dp.aspectRatio); @@ -325,7 +324,7 @@ public class ClipIconView extends View implements ClipPathView { bgDrawableStartScale = scale; mOutline.set(0, 0, lp.width, lp.height); } - setBackgroundDrawableBounds(bgDrawableStartScale, isVerticalBarLayout); + setBackgroundDrawableBounds(bgDrawableStartScale, dp.isLandscape); mEndRevealRect.set(0, 0, lp.width, lp.height); setOutlineProvider(new ViewOutlineProvider() { @Override diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 0f69530ec0..e6bc598766 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -43,6 +43,7 @@ import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; import com.android.launcher3.BubbleTextView; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.Launcher; import com.android.launcher3.R; @@ -82,7 +83,6 @@ public class FloatingIconView extends FrameLayout implements private final Launcher mLauncher; private final boolean mIsRtl; - private boolean mIsVerticalBarLayout = false; private boolean mIsOpening; private IconLoadResult mIconLoadResult; @@ -150,7 +150,7 @@ public class FloatingIconView extends FrameLayout implements float shapeProgressStart, float cornerRadius, boolean isOpening) { setAlpha(alpha); mClipIconView.update(rect, progress, shapeProgressStart, cornerRadius, fgIconAlpha, - isOpening, this, mLauncher.getDeviceProfile(), mIsVerticalBarLayout); + isOpening, this, mLauncher.getDeviceProfile()); } @Override @@ -320,11 +320,11 @@ public class FloatingIconView extends FrameLayout implements @UiThread private void setIcon(@Nullable Drawable drawable, @Nullable Drawable badge, @Nullable Drawable btvIcon, int iconOffset) { + final DeviceProfile dp = mLauncher.getDeviceProfile(); final InsettableFrameLayout.LayoutParams lp = (InsettableFrameLayout.LayoutParams) getLayoutParams(); mBadge = badge; - mClipIconView.setIcon(drawable, iconOffset, lp, mIsOpening, mIsVerticalBarLayout, - mLauncher.getDeviceProfile()); + mClipIconView.setIcon(drawable, iconOffset, lp, mIsOpening, dp); if (drawable instanceof AdaptiveIconDrawable) { final int originalHeight = lp.height; final int originalWidth = lp.width; @@ -332,7 +332,7 @@ public class FloatingIconView extends FrameLayout implements mFinalDrawableBounds.set(0, 0, originalWidth, originalHeight); float aspectRatio = mLauncher.getDeviceProfile().aspectRatio; - if (mIsVerticalBarLayout) { + if (dp.isLandscape) { lp.width = (int) Math.max(lp.width, lp.height * aspectRatio); } else { lp.height = (int) Math.max(lp.height, lp.width * aspectRatio); @@ -565,7 +565,6 @@ public class FloatingIconView extends FrameLayout implements view.recycle(); // Init properties before getting the drawable. - view.mIsVerticalBarLayout = launcher.getDeviceProfile().isVerticalBarLayout(); view.mIsOpening = isOpening; view.mOriginalIcon = originalView; view.mPositionOut = positionOut; From fe9ec740cc0c04565cfb5ccbd7a0786dd266d1d3 Mon Sep 17 00:00:00 2001 From: Nick Chameyev Date: Fri, 4 Mar 2022 16:27:54 +0000 Subject: [PATCH 11/25] Do not run unfold taskbar animation when in portrait Limits taskbar icons translation animation only when the display is in natural orientation. Bug: 219958588 Test: fold/unfold in portrait and landscape Change-Id: I33e26829ae37f1df39e8c7234f98d20eb7993b93 --- .../taskbar/TaskbarActivityContext.java | 5 ++-- .../launcher3/taskbar/TaskbarManager.java | 7 +++-- .../TaskbarUnfoldAnimationController.java | 25 +++++++++++----- .../taskbar/TaskbarViewController.java | 19 ++++-------- ...copedUnfoldTransitionProgressProvider.java | 30 +++++++++++++++++++ 5 files changed, 60 insertions(+), 26 deletions(-) create mode 100644 quickstep/src/com/android/launcher3/taskbar/unfold/NonDestroyableScopedUnfoldTransitionProgressProvider.java diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 8f0b9345d7..46640e25f1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -47,6 +47,7 @@ import android.view.Gravity; import android.view.RoundedCorner; import android.view.View; import android.view.WindowManager; +import android.view.WindowManagerGlobal; import android.widget.FrameLayout; import android.widget.Toast; @@ -178,8 +179,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { new TaskbarDragLayerController(this, mDragLayer), new TaskbarViewController(this, taskbarView), new TaskbarScrimViewController(this, taskbarScrimView), - new TaskbarUnfoldAnimationController(unfoldTransitionProgressProvider, - mWindowManager), + new TaskbarUnfoldAnimationController(this, unfoldTransitionProgressProvider, + mWindowManager, WindowManagerGlobal.getWindowManagerService()), new TaskbarKeyguardController(this), new StashedHandleViewController(this, stashedHandleView), new TaskbarStashController(this), diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java index 0f7abda714..16cf883fa4 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java @@ -40,8 +40,8 @@ import androidx.annotation.Nullable; import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.statemanager.StatefulActivity; +import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.Info; import com.android.launcher3.util.SettingsCache; @@ -78,8 +78,11 @@ public class TaskbarManager implements DisplayController.DisplayInfoChangeListen private final SimpleBroadcastReceiver mShutdownReceiver; // The source for this provider is set when Launcher is available + // We use 'non-destroyable' version here so the original provider won't be destroyed + // as it is tied to the activity lifecycle, not the taskbar lifecycle. + // It's destruction/creation will be managed by the activity. private final ScopedUnfoldTransitionProgressProvider mUnfoldProgressProvider = - new ScopedUnfoldTransitionProgressProvider(); + new NonDestroyableScopedUnfoldTransitionProgressProvider(); private TaskbarActivityContext mTaskbarActivityContext; private StatefulActivity mActivity; diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java index d5ea570613..64a4fa79df 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUnfoldAnimationController.java @@ -15,12 +15,14 @@ */ package com.android.launcher3.taskbar; +import android.view.IWindowManager; import android.view.View; import android.view.WindowManager; import com.android.quickstep.util.LauncherViewsMoveFromCenterTranslationApplier; import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator; import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener; +import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider; import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider; import java.io.PrintWriter; @@ -31,14 +33,18 @@ import java.io.PrintWriter; public class TaskbarUnfoldAnimationController implements TaskbarControllers.LoggableTaskbarController { - private final ScopedUnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider; + private final ScopedUnfoldTransitionProgressProvider mScopedUnfoldTransitionProgressProvider; + private final NaturalRotationUnfoldProgressProvider mNaturalUnfoldTransitionProgressProvider; private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimator; private final TransitionListener mTransitionListener = new TransitionListener(); private TaskbarViewController mTaskbarViewController; - public TaskbarUnfoldAnimationController(ScopedUnfoldTransitionProgressProvider - unfoldTransitionProgressProvider, WindowManager windowManager) { - mUnfoldTransitionProgressProvider = unfoldTransitionProgressProvider; + public TaskbarUnfoldAnimationController(BaseTaskbarContext context, + ScopedUnfoldTransitionProgressProvider source, + WindowManager windowManager, IWindowManager iWindowManager) { + mScopedUnfoldTransitionProgressProvider = source; + mNaturalUnfoldTransitionProgressProvider = + new NaturalRotationUnfoldProgressProvider(context, iWindowManager, source); mMoveFromCenterAnimator = new UnfoldMoveFromCenterAnimator(windowManager, new LauncherViewsMoveFromCenterTranslationApplier()); } @@ -48,18 +54,21 @@ public class TaskbarUnfoldAnimationController implements * @param taskbarControllers references to all other taskbar controllers */ public void init(TaskbarControllers taskbarControllers) { + mNaturalUnfoldTransitionProgressProvider.init(); mTaskbarViewController = taskbarControllers.taskbarViewController; mTaskbarViewController.addOneTimePreDrawListener(() -> - mUnfoldTransitionProgressProvider.setReadyToHandleTransition(true)); - mUnfoldTransitionProgressProvider.addCallback(mTransitionListener); + mScopedUnfoldTransitionProgressProvider.setReadyToHandleTransition(true)); + mNaturalUnfoldTransitionProgressProvider.addCallback(mTransitionListener); } /** * Destroys the controller */ public void onDestroy() { - mUnfoldTransitionProgressProvider.setReadyToHandleTransition(false); - mUnfoldTransitionProgressProvider.removeCallback(mTransitionListener); + mScopedUnfoldTransitionProgressProvider.setReadyToHandleTransition(false); + mNaturalUnfoldTransitionProgressProvider.removeCallback(mTransitionListener); + mNaturalUnfoldTransitionProgressProvider.destroy(); + mTaskbarViewController = null; } @Override diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 153ed140b4..3575dc151b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -20,13 +20,14 @@ import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.quickstep.AnimatedFloat.VALUE; +import android.annotation.NonNull; import android.graphics.Rect; import android.util.FloatProperty; import android.util.Log; import android.view.MotionEvent; import android.view.View; -import android.view.ViewTreeObserver; -import android.view.ViewTreeObserver.OnPreDrawListener; + +import androidx.core.view.OneShotPreDrawListener; import com.android.launcher3.BubbleTextView; import com.android.launcher3.DeviceProfile; @@ -142,18 +143,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar * drawing a frame and invoked only once * @param listener callback that will be invoked before drawing the next frame */ - public void addOneTimePreDrawListener(Runnable listener) { - mTaskbarView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() { - @Override - public boolean onPreDraw() { - final ViewTreeObserver viewTreeObserver = mTaskbarView.getViewTreeObserver(); - if (viewTreeObserver.isAlive()) { - listener.run(); - viewTreeObserver.removeOnPreDrawListener(this); - } - return true; - } - }); + public void addOneTimePreDrawListener(@NonNull Runnable listener) { + OneShotPreDrawListener.add(mTaskbarView, listener); } public Rect getIconLayoutBounds() { diff --git a/quickstep/src/com/android/launcher3/taskbar/unfold/NonDestroyableScopedUnfoldTransitionProgressProvider.java b/quickstep/src/com/android/launcher3/taskbar/unfold/NonDestroyableScopedUnfoldTransitionProgressProvider.java new file mode 100644 index 0000000000..f9da4e4e74 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/unfold/NonDestroyableScopedUnfoldTransitionProgressProvider.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2022 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.taskbar.unfold; + +import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider; + +/** + * ScopedUnfoldTransitionProgressProvider that doesn't propagate destroy method + */ +public class NonDestroyableScopedUnfoldTransitionProgressProvider extends + ScopedUnfoldTransitionProgressProvider { + + @Override + public void destroy() { + // no-op + } +} From 4937d79c03fa8d5188a2b237c672c5e3e12cdda6 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Mon, 7 Mar 2022 14:50:28 -0800 Subject: [PATCH 12/25] Sync hotseat/taskbar handoff Bug: 202507555 Test: open/close apps in hotseat/taskbar Change-Id: Ia5ecff8438f0cf237b39a54b7a78c423c53f9023 --- Android.bp | 3 ++ .../TaskbarLauncherStateController.java | 34 +++++++++++++++++++ .../taskbar/TaskbarViewController.java | 14 ++++++-- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/Android.bp b/Android.bp index f5a38b4e6f..c980a2e50f 100644 --- a/Android.bp +++ b/Android.bp @@ -31,6 +31,7 @@ android_library { "androidx.test.uiautomator_uiautomator", "androidx.preference_preference", "SystemUISharedLib", + "SystemUIAnimationLib", ], srcs: [ "tests/tapl/**/*.java", @@ -196,6 +197,7 @@ android_library { "lottie", "SystemUISharedLib", "SystemUI-statsd", + "SystemUIAnimationLib", ], manifest: "quickstep/AndroidManifest.xml", min_sdk_version: "current", @@ -287,6 +289,7 @@ android_library { "SystemUISharedLib", "Launcher3CommonDepsLib", "QuickstepResLib", + "SystemUIAnimationLib", ], manifest: "quickstep/AndroidManifest.xml", platform_apis: true, diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index ebe6a04611..ce468d0cee 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -29,6 +29,7 @@ import androidx.annotation.NonNull; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BaseQuickstepLauncher; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherState; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.util.MultiValueAlpha; @@ -36,6 +37,7 @@ import com.android.quickstep.AnimatedFloat; import com.android.quickstep.RecentsAnimationCallbacks; import com.android.quickstep.RecentsAnimationController; import com.android.quickstep.views.RecentsView; +import com.android.systemui.animation.ViewRootSync; import com.android.systemui.shared.recents.model.ThumbnailData; import java.util.HashMap; @@ -76,6 +78,9 @@ import java.util.function.Supplier; private boolean mShouldDelayLauncherStateAnim; + // We skip any view synchronizations during init/destroy. + private boolean mCanSyncViews; + private final StateManager.StateListener mStateListener = new StateManager.StateListener() { @@ -102,6 +107,8 @@ import java.util.function.Supplier; }; public void init(TaskbarControllers controllers, BaseQuickstepLauncher launcher) { + mCanSyncViews = false; + mControllers = controllers; mLauncher = launcher; @@ -121,9 +128,13 @@ import java.util.function.Supplier; updateStateForFlag(FLAG_RESUMED, launcher.hasBeenResumed()); mLauncherState = launcher.getStateManager().getState(); applyState(0); + + mCanSyncViews = true; } public void onDestroy() { + mCanSyncViews = false; + mIconAlignmentForResumedState.finishAnimation(); mIconAlignmentForGestureState.finishAnimation(); mIconAlignmentForLauncherState.finishAnimation(); @@ -131,6 +142,8 @@ import java.util.function.Supplier; mIconAlphaForHome.setConsumer(null); mLauncher.getHotseat().setIconsAlpha(1f); mLauncher.getStateManager().removeStateListener(mStateListener); + + mCanSyncViews = true; } public Animator createAnimToLauncher(@NonNull LauncherState toState, @@ -380,6 +393,27 @@ import java.util.function.Supplier; return; } float alignment = alignmentSupplier.get(); + float currentValue = mIconAlphaForHome.getValue(); + boolean taskbarWillBeVisible = alignment < 1; + boolean firstFrameVisChanged = (taskbarWillBeVisible && Float.compare(currentValue, 1) != 0) + || (!taskbarWillBeVisible && Float.compare(currentValue, 0) != 0); + + // Sync the first frame where we swap taskbar and hotseat. + if (firstFrameVisChanged && mCanSyncViews) { + DeviceProfile dp = mLauncher.getDeviceProfile(); + + // Do all the heavy work before the sync. + mControllers.taskbarViewController.createIconAlignmentControllerIfNotExists(dp); + + ViewRootSync.synchronizeNextDraw(mLauncher.getHotseat(), + mControllers.taskbarActivityContext.getDragLayer(), + () -> updateIconAlignment(alignment)); + } else { + updateIconAlignment(alignment); + } + } + + private void updateIconAlignment(float alignment) { mControllers.taskbarViewController.setLauncherIconAlignment( alignment, mLauncher.getDeviceProfile()); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 6e34ee03b4..f354b2c2a8 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -191,6 +191,16 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar + mTaskbarIconTranslationYForStash.value); } + /** + * Creates the icon alignment controller if it does not already exist. + * @param launcherDp Launcher device profile. + */ + public void createIconAlignmentControllerIfNotExists(DeviceProfile launcherDp) { + if (mIconAlignControllerLazy == null) { + mIconAlignControllerLazy = createIconAlignmentController(launcherDp); + } + } + /** * Sets the taskbar icon alignment relative to Launcher hotseat icons * @param alignmentRatio [0, 1] @@ -198,9 +208,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar * 1 => fully aligned */ public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) { - if (mIconAlignControllerLazy == null) { - mIconAlignControllerLazy = createIconAlignmentController(launcherDp); - } + createIconAlignmentControllerIfNotExists(launcherDp); mIconAlignControllerLazy.setPlayFraction(alignmentRatio); if (alignmentRatio <= 0 || alignmentRatio >= 1) { // Cleanup lazy controller so that it is created again in next animation From 8e6c9bbb3e685a0f5237c5d4c14399615d2032f6 Mon Sep 17 00:00:00 2001 From: Zak Cohen Date: Tue, 8 Mar 2022 11:14:16 -0800 Subject: [PATCH 13/25] Widgets - Filter work widgets when Work Profile is paused. Test: local Bug: 188227318 Change-Id: Icbe6f69de9f3776c88df8c56468531940b54f239 --- .../widget/picker/WidgetsFullSheet.java | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index daa8fb7889..6e97774b82 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -31,6 +31,7 @@ import android.content.res.Resources; import android.graphics.Rect; import android.os.Process; import android.os.UserHandle; +import android.os.UserManager; import android.util.AttributeSet; import android.util.Pair; import android.util.SparseArray; @@ -55,7 +56,9 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.compat.AccessibilityManagerCompat; +import com.android.launcher3.model.UserManagerState; import com.android.launcher3.model.WidgetItem; +import com.android.launcher3.pm.UserCache; import com.android.launcher3.views.ArrowTipView; import com.android.launcher3.views.RecyclerViewFastScroller; import com.android.launcher3.views.SpringRelativeLayout; @@ -94,13 +97,17 @@ public class WidgetsFullSheet extends BaseWidgetSheet "launcher.widgets_education_dialog_seen"; private final Rect mInsets = new Rect(); + + private final UserManagerState mUserManagerState = new UserManagerState(); + private final boolean mHasWorkProfile; private final SparseArray mAdapters = new SparseArray(); private final UserHandle mCurrentUser = Process.myUserHandle(); private final Predicate mPrimaryWidgetsFilter = entry -> mCurrentUser.equals(entry.mPkgItem.user); private final Predicate mWorkWidgetsFilter = - mPrimaryWidgetsFilter.negate(); + entry -> !mCurrentUser.equals(entry.mPkgItem.user) + && !mUserManagerState.isUserQuiet(entry.mPkgItem.user); @Nullable private ArrowTipView mLatestEducationalTip; private final OnLayoutChangeListener mLayoutChangeListenerToShowTips = new OnLayoutChangeListener() { @@ -171,6 +178,9 @@ public class WidgetsFullSheet extends BaseWidgetSheet : 0; mWidgetSheetContentHorizontalPadding = 2 * resources.getDimensionPixelSize( R.dimen.widget_cell_horizontal_padding); + + mUserManagerState.init(UserCache.INSTANCE.get(context), + context.getSystemService(UserManager.class)); } public WidgetsFullSheet(Context context, AttributeSet attrs) { @@ -259,10 +269,15 @@ public class WidgetsFullSheet extends BaseWidgetSheet boolean isWidgetAvailable = adapterHolder.mWidgetsListAdapter.hasVisibleEntries(); adapterHolder.mWidgetsRecyclerView.setVisibility(isWidgetAvailable ? VISIBLE : GONE); - mNoWidgetsView.setText( - adapterHolder.mAdapterType == AdapterHolder.SEARCH - ? R.string.no_search_results - : R.string.no_widgets_available); + if (adapterHolder.mAdapterType == AdapterHolder.SEARCH) { + mNoWidgetsView.setText(R.string.no_search_results); + } else if (adapterHolder.mAdapterType == AdapterHolder.WORK + && mUserManagerState.isAnyProfileQuietModeEnabled() + && mActivityContext.getStringCache() != null) { + mNoWidgetsView.setText(mActivityContext.getStringCache().workProfilePausedTitle); + } else { + mNoWidgetsView.setText(R.string.no_widgets_available); + } mNoWidgetsView.setVisibility(isWidgetAvailable ? GONE : VISIBLE); } From 38c59cfe4ab5689b88c7ac7e0d5cd4a3e4f8e0af Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Thu, 3 Feb 2022 16:07:04 -0800 Subject: [PATCH 14/25] Update gesture nav tutorial with updated phone landscape layouts - Updated layout files to support landscape mode on phones - Updated All Set page to say "tablet" rather than "phone" on tablets - Hiding feedback view during gestures for better visibility - Renamed files and resources to say "tablet" rather than "foldable" - Added custom layout logic for the mock hotseat on foldables - Updated feedback view margins Test: manual Fixes: 215063763 Fixes: 206895841 Fixes: 219251891 Change-Id: I56f7f33dd0617bdeeca4863f7d5de0143376c8bf --- .../res/drawable/bg_sandbox_feedback.xml | 3 +- .../gesture_tutorial_mock_hotseat.xml | 80 +++ .../gesture_tutorial_tablet_mock_hotseat.xml | 123 ++++ quickstep/res/layout/activity_allset.xml | 7 +- ...gesture_tutorial_foldable_mock_hotseat.xml | 18 +- .../res/layout/gesture_tutorial_fragment.xml | 41 +- .../gesture_tutorial_mock_conversation.xml | 16 +- ...esture_tutorial_mock_conversation_list.xml | 557 +++++++++--------- .../layout/gesture_tutorial_mock_webpage.xml | 24 +- ...ure_tutorial_tablet_mock_conversation.xml} | 24 +- ...utorial_tablet_mock_conversation_list.xml} | 34 +- .../gesture_tutorial_tablet_mock_hotseat.xml | 122 ++++ ... gesture_tutorial_tablet_mock_taskbar.xml} | 0 ... gesture_tutorial_tablet_mock_webpage.xml} | 0 quickstep/res/values-land/dimens.xml | 57 ++ quickstep/res/values/dimens.xml | 50 +- quickstep/res/values/strings.xml | 4 +- quickstep/res/values/styles.xml | 15 +- .../quickstep/interaction/AllSetActivity.java | 7 + .../BackGestureTutorialController.java | 4 +- .../HomeGestureTutorialController.java | 2 +- .../OverviewGestureTutorialController.java | 2 +- .../SwipeUpGestureTutorialController.java | 9 + .../interaction/TutorialController.java | 61 +- .../interaction/TutorialFragment.java | 29 +- .../interaction/TutorialStepIndicator.java | 10 +- .../quickstep/util/RecentsOrientedState.java | 9 +- 27 files changed, 910 insertions(+), 398 deletions(-) create mode 100644 quickstep/res/layout-land/gesture_tutorial_mock_hotseat.xml create mode 100644 quickstep/res/layout-land/gesture_tutorial_tablet_mock_hotseat.xml rename quickstep/res/layout/{gesture_tutorial_foldable_mock_conversation.xml => gesture_tutorial_tablet_mock_conversation.xml} (93%) rename quickstep/res/layout/{gesture_tutorial_foldable_mock_conversation_list.xml => gesture_tutorial_tablet_mock_conversation_list.xml} (95%) create mode 100644 quickstep/res/layout/gesture_tutorial_tablet_mock_hotseat.xml rename quickstep/res/layout/{gesture_tutorial_foldable_mock_taskbar.xml => gesture_tutorial_tablet_mock_taskbar.xml} (100%) rename quickstep/res/layout/{gesture_tutorial_foldable_mock_webpage.xml => gesture_tutorial_tablet_mock_webpage.xml} (100%) diff --git a/quickstep/res/drawable/bg_sandbox_feedback.xml b/quickstep/res/drawable/bg_sandbox_feedback.xml index 83a3deada8..83d7e4396c 100644 --- a/quickstep/res/drawable/bg_sandbox_feedback.xml +++ b/quickstep/res/drawable/bg_sandbox_feedback.xml @@ -14,7 +14,8 @@ limitations under the License. --> - + diff --git a/quickstep/res/layout-land/gesture_tutorial_mock_hotseat.xml b/quickstep/res/layout-land/gesture_tutorial_mock_hotseat.xml new file mode 100644 index 0000000000..20d2ecc59f --- /dev/null +++ b/quickstep/res/layout-land/gesture_tutorial_mock_hotseat.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/quickstep/res/layout-land/gesture_tutorial_tablet_mock_hotseat.xml b/quickstep/res/layout-land/gesture_tutorial_tablet_mock_hotseat.xml new file mode 100644 index 0000000000..6877b89716 --- /dev/null +++ b/quickstep/res/layout-land/gesture_tutorial_tablet_mock_hotseat.xml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml index 9ad10dc54e..06dfa37263 100644 --- a/quickstep/res/layout/activity_allset.xml +++ b/quickstep/res/layout/activity_allset.xml @@ -60,7 +60,7 @@ + android:gravity="start"/> + + android:paddingStart="@dimen/gesture_tutorial_hotseat_padding_start_end" + android:paddingEnd="@dimen/gesture_tutorial_hotseat_padding_start_end"> + android:layout_width="@dimen/gesture_tutorial_hotseat_width" + android:layout_height="@dimen/gesture_tutorial_hotseat_height"/> @@ -102,7 +100,7 @@ android:background="@drawable/gesture_tutorial_ripple"/> - - + + \ No newline at end of file diff --git a/quickstep/res/layout/gesture_tutorial_mock_conversation.xml b/quickstep/res/layout/gesture_tutorial_mock_conversation.xml index e8d5d79a58..5550389e25 100644 --- a/quickstep/res/layout/gesture_tutorial_mock_conversation.xml +++ b/quickstep/res/layout/gesture_tutorial_mock_conversation.xml @@ -34,8 +34,8 @@ android:layout_height="0dp" android:layout_marginTop="43dp" android:layout_marginBottom="22dp" - android:layout_marginStart="34dp" - android:layout_marginEnd="211dp" + android:layout_marginStart="@dimen/gesture_tutorial_top_bar_margin_start" + android:layout_marginEnd="@dimen/gesture_tutorial_top_bar_margin_end" app:cardElevation="0dp" app:cardCornerRadius="4dp" @@ -84,7 +84,7 @@ android:layout_width="match_parent" android:layout_height="0dp" android:background="@color/mock_conversation_background" - android:paddingBottom="66dp" + android:paddingBottom="@dimen/gesture_tutorial_conversation_bottom_padding" app:layout_constraintTop_toBottomOf="@id/top_bar" app:layout_constraintBottom_toBottomOf="parent" @@ -108,6 +108,7 @@ android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom" android:layout_marginStart="124dp" android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end" + android:visibility="@integer/gesture_tutorial_extra_messages_visibility" app:cardElevation="0dp" app:cardCornerRadius="18dp" @@ -122,6 +123,7 @@ android:layout_height="@dimen/gesture_tutorial_message_icon_size" android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom" android:layout_marginStart="@dimen/gesture_tutorial_message_padding_start" + android:visibility="@integer/gesture_tutorial_extra_messages_visibility" app:cardElevation="0dp" app:cardCornerRadius="@dimen/gesture_tutorial_message_icon_corner_radius" @@ -135,6 +137,7 @@ android:layout_height="36dp" android:layout_marginStart="17dp" android:layout_marginEnd="112dp" + android:visibility="@integer/gesture_tutorial_extra_messages_visibility" app:cardElevation="0dp" app:cardCornerRadius="18dp" @@ -151,6 +154,7 @@ android:layout_marginBottom="@dimen/gesture_tutorial_message_small_margin_bottom" android:layout_marginStart="280dp" android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end" + android:visibility="@integer/gesture_tutorial_extra_messages_visibility" app:cardElevation="0dp" app:cardCornerRadius="18dp" @@ -164,7 +168,7 @@ android:layout_width="0dp" android:layout_height="74dp" android:layout_marginBottom="@dimen/gesture_tutorial_message_large_margin_bottom" - android:layout_marginStart="124dp" + android:layout_marginStart="@dimen/gesture_tutorial_message_margin_start" android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end" app:cardElevation="0dp" @@ -192,7 +196,7 @@ android:layout_width="0dp" android:layout_height="36dp" android:layout_marginStart="17dp" - android:layout_marginEnd="144dp" + android:layout_marginEnd="@dimen/gesture_tutorial_reply_margin_end" app:cardElevation="0dp" app:cardCornerRadius="18dp" @@ -206,7 +210,7 @@ android:id="@+id/message_4" android:layout_width="0dp" android:layout_height="74dp" - android:layout_marginStart="124dp" + android:layout_marginStart="@dimen/gesture_tutorial_message_margin_start" android:layout_marginEnd="@dimen/gesture_tutorial_message_padding_end" app:cardElevation="0dp" diff --git a/quickstep/res/layout/gesture_tutorial_mock_conversation_list.xml b/quickstep/res/layout/gesture_tutorial_mock_conversation_list.xml index 364ad6d17d..a172ad3712 100644 --- a/quickstep/res/layout/gesture_tutorial_mock_conversation_list.xml +++ b/quickstep/res/layout/gesture_tutorial_mock_conversation_list.xml @@ -35,7 +35,7 @@ android:layout_marginTop="43dp" android:layout_marginBottom="22dp" android:layout_marginStart="34dp" - android:layout_marginEnd="35dp" + android:layout_marginEnd="34dp" app:cardElevation="0dp" app:cardCornerRadius="4dp" @@ -51,337 +51,336 @@ android:layout_width="match_parent" android:layout_height="0dp" android:background="@color/mock_list_background" - android:paddingBottom="66dp" + android:paddingTop="@dimen/gesture_tutorial_conversation_list_padding_top" + android:paddingStart="26dp" app:layout_constraintTop_toBottomOf="@id/top_bar" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"> - + app:layout_constraintStart_toStartOf="parent"/> - + app:cardElevation="0dp" + app:cardCornerRadius="4dp" + app:cardBackgroundColor="@color/mock_list_preview_message" + app:layout_constraintVertical_chainStyle="packed" + app:layout_constraintTop_toTopOf="@id/conversation_icon_1" + app:layout_constraintStart_toEndOf="@id/conversation_icon_1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toTopOf="@id/conversation_line_2"/> - + app:cardElevation="0dp" + app:cardCornerRadius="4dp" + app:cardBackgroundColor="@color/mock_list_preview_message" + app:layout_constraintTop_toBottomOf="@id/conversation_line_1" + app:layout_constraintStart_toEndOf="@id/conversation_icon_1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="@id/conversation_icon_1"/> - + app:cardElevation="0dp" + app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius" + app:cardBackgroundColor="@color/mock_list_profile_icon" + app:layout_constraintTop_toBottomOf="@id/conversation_icon_1" + app:layout_constraintStart_toStartOf="parent"/> - + app:cardElevation="0dp" + app:cardCornerRadius="4dp" + app:cardBackgroundColor="@color/mock_list_preview_message" + app:layout_constraintVertical_chainStyle="packed" + app:layout_constraintTop_toTopOf="@id/conversation_icon_2" + app:layout_constraintStart_toEndOf="@id/conversation_icon_2" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toTopOf="@id/conversation_line_4"/> - + app:cardElevation="0dp" + app:cardCornerRadius="4dp" + app:cardBackgroundColor="@color/mock_list_preview_message" + app:layout_constraintTop_toBottomOf="@id/conversation_line_3" + app:layout_constraintStart_toEndOf="@id/conversation_icon_2" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="@id/conversation_icon_2"/> - + app:cardElevation="0dp" + app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius" + app:cardBackgroundColor="@color/mock_list_profile_icon" + app:layout_constraintTop_toBottomOf="@id/conversation_icon_2" + app:layout_constraintStart_toStartOf="parent"/> - + app:cardElevation="0dp" + app:cardCornerRadius="4dp" + app:cardBackgroundColor="@color/mock_list_preview_message" + app:layout_constraintVertical_chainStyle="packed" + app:layout_constraintTop_toTopOf="@id/conversation_icon_3" + app:layout_constraintStart_toEndOf="@id/conversation_icon_3" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toTopOf="@id/conversation_line_6"/> - + app:cardElevation="0dp" + app:cardCornerRadius="4dp" + app:cardBackgroundColor="@color/mock_list_preview_message" + app:layout_constraintTop_toBottomOf="@id/conversation_line_5" + app:layout_constraintStart_toEndOf="@id/conversation_icon_3" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="@id/conversation_icon_3"/> - + app:cardElevation="0dp" + app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius" + app:cardBackgroundColor="@color/mock_list_profile_icon" + app:layout_constraintTop_toBottomOf="@id/conversation_icon_3" + app:layout_constraintStart_toStartOf="parent"/> - + app:cardElevation="0dp" + app:cardCornerRadius="4dp" + app:cardBackgroundColor="@color/mock_list_preview_message" + app:layout_constraintVertical_chainStyle="packed" + app:layout_constraintTop_toTopOf="@id/conversation_icon_4" + app:layout_constraintStart_toEndOf="@id/conversation_icon_4" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toTopOf="@id/conversation_line_8"/> - + app:cardElevation="0dp" + app:cardCornerRadius="4dp" + app:cardBackgroundColor="@color/mock_list_preview_message" + app:layout_constraintTop_toBottomOf="@id/conversation_line_7" + app:layout_constraintStart_toEndOf="@id/conversation_icon_4" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="@id/conversation_icon_4"/> - + app:cardElevation="0dp" + app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius" + app:cardBackgroundColor="@color/mock_list_profile_icon" + app:layout_constraintTop_toBottomOf="@id/conversation_icon_4" + app:layout_constraintStart_toStartOf="parent"/> - + app:cardElevation="0dp" + app:cardCornerRadius="4dp" + app:cardBackgroundColor="@color/mock_list_preview_message" + app:layout_constraintVertical_chainStyle="packed" + app:layout_constraintTop_toTopOf="@id/conversation_icon_5" + app:layout_constraintStart_toEndOf="@id/conversation_icon_5" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toTopOf="@id/conversation_line_10"/> - + app:cardElevation="0dp" + app:cardCornerRadius="4dp" + app:cardBackgroundColor="@color/mock_list_preview_message" + app:layout_constraintTop_toBottomOf="@id/conversation_line_9" + app:layout_constraintStart_toEndOf="@id/conversation_icon_5" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="@id/conversation_icon_5"/> - + app:cardElevation="0dp" + app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius" + app:cardBackgroundColor="@color/mock_list_profile_icon" + app:layout_constraintTop_toBottomOf="@id/conversation_icon_5" + app:layout_constraintStart_toStartOf="parent"/> - + app:cardElevation="0dp" + app:cardCornerRadius="4dp" + app:cardBackgroundColor="@color/mock_list_preview_message" + app:layout_constraintVertical_chainStyle="packed" + app:layout_constraintTop_toTopOf="@id/conversation_icon_6" + app:layout_constraintStart_toEndOf="@id/conversation_icon_6" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toTopOf="@id/conversation_line_12"/> - + app:cardElevation="0dp" + app:cardCornerRadius="4dp" + app:cardBackgroundColor="@color/mock_list_preview_message" + app:layout_constraintTop_toBottomOf="@id/conversation_line_11" + app:layout_constraintStart_toEndOf="@id/conversation_icon_6" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="@id/conversation_icon_6"/> - + app:cardElevation="0dp" + app:cardCornerRadius="@dimen/gesture_tutorial_conversation_icon_corner_radius" + app:cardBackgroundColor="@color/mock_list_profile_icon" + app:layout_constraintTop_toBottomOf="@id/conversation_icon_6" + app:layout_constraintStart_toStartOf="parent"/> - + app:cardElevation="0dp" + app:cardCornerRadius="4dp" + app:cardBackgroundColor="@color/mock_list_preview_message" + app:layout_constraintVertical_chainStyle="packed" + app:layout_constraintTop_toTopOf="@id/conversation_icon_7" + app:layout_constraintStart_toEndOf="@id/conversation_icon_7" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toTopOf="@id/conversation_line_14"/> - - - - - + app:cardElevation="0dp" + app:cardCornerRadius="4dp" + app:cardBackgroundColor="@color/mock_list_preview_message" + app:layout_constraintTop_toBottomOf="@id/conversation_line_13" + app:layout_constraintStart_toEndOf="@id/conversation_icon_7" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="@id/conversation_icon_7"/> + app:layout_constraintEnd_toEndOf="parent"> + + - - \ No newline at end of file diff --git a/quickstep/res/layout/gesture_tutorial_tablet_mock_hotseat.xml b/quickstep/res/layout/gesture_tutorial_tablet_mock_hotseat.xml new file mode 100644 index 0000000000..027e4a05b0 --- /dev/null +++ b/quickstep/res/layout/gesture_tutorial_tablet_mock_hotseat.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/quickstep/res/layout/gesture_tutorial_foldable_mock_taskbar.xml b/quickstep/res/layout/gesture_tutorial_tablet_mock_taskbar.xml similarity index 100% rename from quickstep/res/layout/gesture_tutorial_foldable_mock_taskbar.xml rename to quickstep/res/layout/gesture_tutorial_tablet_mock_taskbar.xml diff --git a/quickstep/res/layout/gesture_tutorial_foldable_mock_webpage.xml b/quickstep/res/layout/gesture_tutorial_tablet_mock_webpage.xml similarity index 100% rename from quickstep/res/layout/gesture_tutorial_foldable_mock_webpage.xml rename to quickstep/res/layout/gesture_tutorial_tablet_mock_webpage.xml diff --git a/quickstep/res/values-land/dimens.xml b/quickstep/res/values-land/dimens.xml index 668aea2c9e..f233bde73a 100644 --- a/quickstep/res/values-land/dimens.xml +++ b/quickstep/res/values-land/dimens.xml @@ -16,4 +16,61 @@ --> 8dp + + + 126dp + 24dp + + + 42dp + 60dp + 42dp + 683dp + 42dp + 35dp + 2 + 505dp + 462dp + 103dp + 103dp + 345dp + 341dp + 501dp + 345dp + 373dp + + + 607dp + 460dp + 554dp + 517dp + 570dp + 336dp + 523dp + 500dp + 15dp + 72dp + 111dp + 2 + 34dp + 42dp + + + -2px + -1px + 170dp + + + 24dp + 48dp + 121dp + 355dp + 355dp + 208dp + 439dp + 311dp + 2 + + + 218dp \ No newline at end of file diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 926e10c8b7..de6768909c 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -110,8 +110,10 @@ 136dp - 24dp - 140dp + 8dp + 140dp + 16dp + 24dp 72dp 18dp 80dp @@ -124,15 +126,46 @@ 4dp 26dp 18dp - 126dp + 34dp + 211dp + 24dp + 66dp + 0 + 124dp + 144dp + 34dp + 24dp + 126dp + 245dp + 241dp + 401dp + 245dp + 273dp 56dp 100dp 28dp 20dp + 217dp + 142dp + 190dp + 171dp + 198dp + 79dp + 174dp + 117dp + 65dp + 132dp + 161dp + 0 + 24dp + 66dp + -1px + -2px + 26dp 60dp 100dp 50dp @@ -148,11 +181,20 @@ 4dp 36dp 22dp + 16dp + 24dp + 97dp + 97dp + 126dp + 64dp + 151dp + 24dp + 0 44dp 100dp - 218dp + 52dp 40dp diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml index 3ee2af0bed..f80deeba45 100644 --- a/quickstep/res/values/strings.xml +++ b/quickstep/res/values/strings.xml @@ -182,8 +182,10 @@ All set! Swipe up to go Home - + You\u2019re ready to start using your phone + + You\u2019re ready to start using your tablet System navigation settings diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml index 2efe72e651..6aa488375b 100644 --- a/quickstep/res/values/styles.xml +++ b/quickstep/res/values/styles.xml @@ -47,6 +47,12 @@ 44sp + + + + @@ -76,9 +88,8 @@