Merging unnecessary subclass of ActivityAllAppsContainerView
Bug: 266605714
Test: Presubmit
Change-Id: I87272ee827055f58f91a4ad9fb25d5351d453cdb
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index de5419e..5b21fb8 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -132,7 +132,6 @@
import com.android.launcher3.allapps.AllAppsRecyclerView;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.allapps.BaseAllAppsContainerView;
import com.android.launcher3.allapps.BaseSearchConfig;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.PropertyListBuilder;
@@ -1642,7 +1641,7 @@
private void showAllAppsWorkTabFromIntent(boolean alreadyOnHome) {
showAllAppsFromIntent(alreadyOnHome);
- mAppsView.switchToTab(BaseAllAppsContainerView.AdapterHolder.WORK);
+ mAppsView.switchToTab(ActivityAllAppsContainerView.AdapterHolder.WORK);
}
/**
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 2511cada..8a02a54 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -15,25 +15,74 @@
*/
package com.android.launcher3.allapps;
-import static com.android.launcher3.allapps.BaseAllAppsContainerView.AdapterHolder.SEARCH;
+import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_COUNT;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Path.Direction;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.Process;
+import android.os.UserManager;
import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.TypedValue;
import android.view.KeyEvent;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.WindowInsets;
+import android.widget.Button;
import android.widget.RelativeLayout;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
+import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
+import com.android.launcher3.allapps.search.SearchAdapterProvider;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.keyboard.FocusedItemDecorator;
+import com.android.launcher3.model.StringCache;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.views.RecyclerViewFastScroller;
+import com.android.launcher3.views.ScrimView;
+import com.android.launcher3.views.SpringRelativeLayout;
+import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
/**
* All apps container view with search support for use in a dragging activity.
@@ -41,16 +90,67 @@
* @param <T> Type of context inflating all apps.
*/
public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
- extends BaseAllAppsContainerView<T> {
+ extends SpringRelativeLayout implements DragSource, Insettable,
+ OnDeviceProfileChangeListener, PersonalWorkSlidingTabStrip.OnActivePageChangedListener,
+ ScrimView.ScrimDrawingController {
+ public static final float PULL_MULTIPLIER = .02f;
+ public static final float FLING_VELOCITY_MULTIPLIER = 1200f;
+ protected static final String BUNDLE_KEY_CURRENT_PAGE = "launcher.allapps.current_page";
private static final long DEFAULT_SEARCH_TRANSITION_DURATION_MS = 300;
+ // Render the header protection at all times to debug clipping issues.
+ private static final boolean DEBUG_HEADER_PROTECTION = false;
+ /** Context of an activity or window that is inflating this container. */
+
+ protected final T mActivityContext;
+ protected final List<AdapterHolder> mAH;
+ protected final Predicate<ItemInfo> mPersonalMatcher = ItemInfoMatcher.ofUser(
+ Process.myUserHandle());
+ protected final WorkProfileManager mWorkManager;
+ protected final Point mFastScrollerOffset = new Point();
+ protected final int mScrimColor;
+ protected final float mHeaderThreshold;
// Used to animate Search results out and A-Z apps in, or vice-versa.
private final SearchTransitionController mSearchTransitionController;
+ private final Paint mHeaderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final Rect mInsets = new Rect();
+ private final AllAppsStore mAllAppsStore = new AllAppsStore();
+ private final RecyclerView.OnScrollListener mScrollListener =
+ new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+ updateHeaderScroll(recyclerView.computeVerticalScrollOffset());
+ }
+ };
+ private final Paint mNavBarScrimPaint;
+ private final int mHeaderProtectionColor;
+ private final Path mTmpPath = new Path();
+ private final RectF mTmpRectF = new RectF();
+ protected AllAppsPagedView mViewPager;
+ protected FloatingHeaderView mHeader;
+ protected View mBottomSheetBackground;
+ /**
+ * View that defines the search box. Result is rendered inside {@link #mSearchRecyclerView}.
+ */
+ protected View mSearchContainer;
+ protected SearchUiManager mSearchUiManager;
+ protected boolean mUsingTabs;
+ protected RecyclerViewFastScroller mTouchHandler;
/** {@code true} when rendered view is in search state instead of the scroll state. */
private boolean mIsSearching;
private boolean mRebindAdaptersAfterSearchAnimation;
+ private int mNavBarScrimHeight = 0;
+ private SearchRecyclerView mSearchRecyclerView;
+ private SearchAdapterProvider<?> mMainAdapterProvider;
+ private View mBottomSheetHandleArea;
+ private boolean mHasWorkApps;
+ private float[] mBottomSheetCornerRadii;
+ private ScrimView mScrimView;
+ private int mHeaderColor;
+ private int mBottomSheetBackgroundColor;
+ private int mTabsProtectionAlpha;
public ActivityAllAppsContainerView(Context context) {
this(context, null);
@@ -62,13 +162,84 @@
public ActivityAllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
+ mActivityContext = ActivityContext.lookupContext(context);
+
+ mScrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
+ mHeaderThreshold = getResources().getDimensionPixelSize(
+ R.dimen.dynamic_grid_cell_border_spacing);
+ mHeaderProtectionColor = Themes.getAttrColor(context, R.attr.allappsHeaderProtectionColor);
+
+ mWorkManager = new WorkProfileManager(
+ mActivityContext.getSystemService(UserManager.class),
+ this, mActivityContext.getStatsLogManager());
+ mAH = Arrays.asList(null, null, null);
+ mNavBarScrimPaint = new Paint();
+ mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
+
+ mAllAppsStore.addUpdateListener(this::onAppsUpdated);
+ mActivityContext.addOnDeviceProfileChangeListener(this);
+
+ // This is a focus listener that proxies focus from a view into the list view. This is to
+ // work around the search box from getting first focus and showing the cursor.
+ setOnFocusChangeListener((v, hasFocus) -> {
+ if (hasFocus && getActiveRecyclerView() != null) {
+ getActiveRecyclerView().requestFocus();
+ }
+ });
+ initContent();
mSearchTransitionController = new SearchTransitionController(this);
}
+ /**
+ * Initializes the view hierarchy and internal variables. Any initialization which actually uses
+ * these members should be done in {@link #onFinishInflate()}.
+ * In terms of subclass initialization, the following would be parallel order for activity:
+ * initContent -> onPreCreate
+ * constructor/init -> onCreate
+ * onFinishInflate -> onPostCreate
+ */
+ protected void initContent() {
+ mMainAdapterProvider = createMainAdapterProvider();
+
+ mAH.set(AdapterHolder.MAIN, new AdapterHolder(AdapterHolder.MAIN));
+ mAH.set(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
+ mAH.set(SEARCH, new AdapterHolder(SEARCH));
+
+ getLayoutInflater().inflate(R.layout.all_apps_content, this);
+ mHeader = findViewById(R.id.all_apps_header);
+ mBottomSheetBackground = findViewById(R.id.bottom_sheet_background);
+ mBottomSheetHandleArea = findViewById(R.id.bottom_sheet_handle_area);
+ mSearchRecyclerView = findViewById(R.id.search_results_list_view);
+
+ // Add the search box next to the header
+ mSearchContainer = inflateSearchBox();
+ addView(mSearchContainer, indexOfChild(mHeader) + 1);
+ mSearchUiManager = (SearchUiManager) mSearchContainer;
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+
+ mAH.get(SEARCH).setup(mSearchRecyclerView,
+ /* Filter out A-Z apps */ itemInfo -> false);
+ rebindAdapters(true /* force */);
+ float cornerRadius = Themes.getDialogCornerRadius(getContext());
+ mBottomSheetCornerRadii = new float[]{
+ cornerRadius,
+ cornerRadius, // Top left radius in px
+ cornerRadius,
+ cornerRadius, // Top right radius in px
+ 0,
+ 0, // Bottom right
+ 0,
+ 0 // Bottom left
+ };
+ final TypedValue value = new TypedValue();
+ getContext().getTheme().resolveAttribute(android.R.attr.colorBackground, value, true);
+ mBottomSheetBackgroundColor = value.data;
+ updateBackground(mActivityContext.getDeviceProfile());
mSearchUiManager.initializeSearch(this);
}
@@ -141,19 +312,43 @@
});
}
- @Override
public boolean shouldContainerScroll(MotionEvent ev) {
+ BaseDragLayer dragLayer = mActivityContext.getDragLayer();
// IF the MotionEvent is inside the search box, and the container keeps on receiving
// touch input, container should move down.
- if (mActivityContext.getDragLayer().isEventOverView(mSearchContainer, ev)) {
+ if (dragLayer.isEventOverView(mSearchContainer, ev)) {
return true;
}
- return super.shouldContainerScroll(ev);
+ // Scroll if not within the container view (e.g. over large-screen scrim).
+ if (!dragLayer.isEventOverView(getVisibleContainerView(), ev)) {
+ return true;
+ }
+ if (dragLayer.isEventOverView(mBottomSheetHandleArea, ev)) {
+ return true;
+ }
+ AllAppsRecyclerView rv = getActiveRecyclerView();
+ if (rv == null) {
+ return true;
+ }
+ if (rv.getScrollbar() != null
+ && rv.getScrollbar().getThumbOffsetY() >= 0
+ && dragLayer.isEventOverView(rv.getScrollbar(), ev)) {
+ return false;
+ }
+ return rv.shouldContainerScroll(ev, dragLayer);
}
- @Override
public void reset(boolean animate) {
- super.reset(animate);
+ for (int i = 0; i < mAH.size(); i++) {
+ if (mAH.get(i).mRecyclerView != null) {
+ mAH.get(i).mRecyclerView.scrollToTop();
+ }
+ }
+ if (isHeaderVisible()) {
+ mHeader.reset(animate);
+ }
+ // Reset the base recycler view after transitioning home.
+ updateHeaderScroll(0);
// Reset the search bar after transitioning home.
mSearchUiManager.resetSearch();
// Animate to A-Z with 0 time to reset the animation with proper state management.
@@ -166,16 +361,26 @@
return super.dispatchKeyEvent(event);
}
- @Override
public String getDescription() {
if (!mUsingTabs && isSearching()) {
return getContext().getString(R.string.all_apps_search_results);
} else {
- return super.getDescription();
+ StringCache cache = mActivityContext.getStringCache();
+ if (mUsingTabs) {
+ if (cache != null) {
+ return isPersonalTab()
+ ? cache.allAppsPersonalTabAccessibility
+ : cache.allAppsWorkTabAccessibility;
+ } else {
+ return isPersonalTab()
+ ? getContext().getString(R.string.all_apps_button_personal_label)
+ : getContext().getString(R.string.all_apps_button_work_label);
+ }
+ }
+ return getContext().getString(R.string.all_apps_button_label);
}
}
- @Override
public boolean isSearching() {
return mIsSearching;
}
@@ -186,30 +391,112 @@
// Will be called at the end of the animation.
return;
}
- super.onActivePageChanged(currentActivePage);
+ if (mAH.get(currentActivePage).mRecyclerView != null) {
+ mAH.get(currentActivePage).mRecyclerView.bindFastScrollbar();
+ }
+ // Header keeps track of active recycler view to properly render header protection.
+ mHeader.setActiveRV(currentActivePage);
+ reset(true /* animate */);
+
+ mWorkManager.onActivePageChanged(currentActivePage);
}
- @Override
+ protected void rebindAdapters() {
+ rebindAdapters(false /* force */);
+ }
+
protected void rebindAdapters(boolean force) {
if (mSearchTransitionController.isRunning()) {
mRebindAdaptersAfterSearchAnimation = true;
return;
}
- super.rebindAdapters(force);
- if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()
- || getMainAdapterProvider().getDecorator() == null
- || getSearchRecyclerView() == null) {
+ updateSearchResultsVisibility();
+
+ boolean showTabs = shouldShowTabs();
+ if (showTabs == mUsingTabs && !force) {
return;
}
- RecyclerView.ItemDecoration decoration = getMainAdapterProvider().getDecorator();
- getSearchRecyclerView().removeItemDecoration(decoration); // In case it is already added.
- getSearchRecyclerView().addItemDecoration(decoration);
+ if (isSearching()) {
+ mUsingTabs = showTabs;
+ mWorkManager.detachWorkModeSwitch();
+ return;
+ }
+
+ // replaceAppsRVcontainer() needs to use both mUsingTabs value to remove the old view AND
+ // showTabs value to create new view. Hence the mUsingTabs new value assignment MUST happen
+ // after this call.
+ replaceAppsRVContainer(showTabs);
+ mUsingTabs = showTabs;
+
+ mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView);
+ mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
+ mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.SEARCH).mRecyclerView);
+
+ if (mUsingTabs) {
+ mAH.get(AdapterHolder.MAIN).setup(mViewPager.getChildAt(0), mPersonalMatcher);
+ mAH.get(AdapterHolder.WORK).setup(mViewPager.getChildAt(1), mWorkManager.getMatcher());
+ mAH.get(AdapterHolder.WORK).mRecyclerView.setId(R.id.apps_list_view_work);
+ if (FeatureFlags.ENABLE_EXPANDING_PAUSE_WORK_BUTTON.get()) {
+ mAH.get(AdapterHolder.WORK).mRecyclerView.addOnScrollListener(
+ mWorkManager.newScrollListener());
+ }
+ mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.MAIN);
+ findViewById(R.id.tab_personal)
+ .setOnClickListener((View view) -> {
+ if (mViewPager.snapToPage(AdapterHolder.MAIN)) {
+ mActivityContext.getStatsLogManager().logger()
+ .log(LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB);
+ }
+ mActivityContext.hideKeyboard();
+ });
+ findViewById(R.id.tab_work)
+ .setOnClickListener((View view) -> {
+ if (mViewPager.snapToPage(AdapterHolder.WORK)) {
+ mActivityContext.getStatsLogManager().logger()
+ .log(LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB);
+ }
+ mActivityContext.hideKeyboard();
+ });
+ setDeviceManagementResources();
+ onActivePageChanged(mViewPager.getNextPage());
+ } else {
+ mAH.get(AdapterHolder.MAIN).setup(findViewById(R.id.apps_list_view), null);
+ mAH.get(AdapterHolder.WORK).mRecyclerView = null;
+ }
+ setupHeader();
+
+ mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView);
+ mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
+ mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.SEARCH).mRecyclerView);
}
- @Override
protected View replaceAppsRVContainer(boolean showTabs) {
- View rvContainer = super.replaceAppsRVContainer(showTabs);
+ for (int i = AdapterHolder.MAIN; i <= AdapterHolder.WORK; i++) {
+ AdapterHolder adapterHolder = mAH.get(i);
+ if (adapterHolder.mRecyclerView != null) {
+ adapterHolder.mRecyclerView.setLayoutManager(null);
+ adapterHolder.mRecyclerView.setAdapter(null);
+ }
+ }
+ View oldView = getAppsRecyclerViewContainer();
+ int index = indexOfChild(oldView);
+ removeView(oldView);
+ int layout = showTabs ? R.layout.all_apps_tabs : R.layout.all_apps_rv_layout;
+ final View rvContainer = getLayoutInflater().inflate(layout, this, false);
+ addView(rvContainer, index);
+ if (showTabs) {
+ mViewPager = (AllAppsPagedView) rvContainer;
+ mViewPager.initParentViews(this);
+ mViewPager.getPageIndicator().setOnActivePageChangedListener(this);
+
+ mWorkManager.reset();
+ post(() -> mAH.get(AdapterHolder.WORK).applyPadding());
+
+ } else {
+ mWorkManager.detachWorkModeSwitch();
+ mViewPager = null;
+ }
removeCustomRules(rvContainer);
removeCustomRules(getSearchRecyclerView());
@@ -228,9 +515,24 @@
return rvContainer;
}
- @Override
void setupHeader() {
- super.setupHeader();
+ mHeader.setVisibility(View.VISIBLE);
+ boolean tabsHidden = !mUsingTabs;
+ mHeader.setup(
+ mAH.get(AdapterHolder.MAIN).mRecyclerView,
+ mAH.get(AdapterHolder.WORK).mRecyclerView,
+ (SearchRecyclerView) mAH.get(SEARCH).mRecyclerView,
+ getCurrentPage(),
+ tabsHidden);
+
+ int padding = mHeader.getMaxTranslation();
+ mAH.forEach(adapterHolder -> {
+ adapterHolder.mPadding.top = padding;
+ adapterHolder.applyPadding();
+ if (adapterHolder.mRecyclerView != null) {
+ adapterHolder.mRecyclerView.scrollToTop();
+ }
+ });
removeCustomRules(mHeader);
if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
@@ -240,9 +542,18 @@
}
}
- @Override
protected void updateHeaderScroll(int scrolledOffset) {
- super.updateHeaderScroll(scrolledOffset);
+ float prog1 = Utilities.boundToRange((float) scrolledOffset / mHeaderThreshold, 0f, 1f);
+ int headerColor = getHeaderColor(prog1);
+ int tabsAlpha = mHeader.getPeripheralProtectionHeight() == 0 ? 0
+ : (int) (Utilities.boundToRange(
+ (scrolledOffset + mHeader.mSnappedScrolledY) / mHeaderThreshold, 0f, 1f)
+ * 255);
+ if (headerColor != mHeaderColor || mTabsProtectionAlpha != tabsAlpha) {
+ mHeaderColor = headerColor;
+ mTabsProtectionAlpha = tabsAlpha;
+ invalidateHeader();
+ }
if (mSearchUiManager.getEditText() == null) {
return;
}
@@ -257,10 +568,9 @@
mSearchUiManager.setBackgroundVisibility(bgVisible, 1 - prog);
}
- @Override
protected int getHeaderColor(float blendRatio) {
return ColorUtils.setAlphaComponent(
- super.getHeaderColor(blendRatio),
+ ColorUtils.blendARGB(mScrimColor, mHeaderProtectionColor, blendRatio),
(int) (mSearchContainer.getAlpha() * 255));
}
@@ -315,7 +625,6 @@
layoutParams.removeRule(RelativeLayout.ALIGN_PARENT_TOP);
}
- @Override
protected BaseAllAppsAdapter<T> createAdapter(AlphabeticalAppsList<T> appsList,
BaseAdapterProvider[] adapterProviders) {
return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), appsList,
@@ -339,9 +648,558 @@
: R.dimen.all_apps_header_top_margin);
}
- @Override
public boolean isInAllApps() {
// TODO: Make this abstract
return true;
}
+
+ /**
+ * Inflates the search box
+ */
+ protected View inflateSearchBox() {
+ return getLayoutInflater().inflate(R.layout.search_container_all_apps, this, false);
+ }
+
+ /** Creates the adapter provider for the main section. */
+ protected SearchAdapterProvider<?> createMainAdapterProvider() {
+ return new DefaultSearchAdapterProvider(mActivityContext);
+ }
+
+ /** The adapter provider for the main section. */
+ public final SearchAdapterProvider<?> getMainAdapterProvider() {
+ return mMainAdapterProvider;
+ }
+
+ @Override
+ protected void dispatchRestoreInstanceState(SparseArray<Parcelable> sparseArray) {
+ try {
+ // Many slice view id is not properly assigned, and hence throws null
+ // pointer exception in the underneath method. Catching the exception
+ // simply doesn't restore these slice views. This doesn't have any
+ // user visible effect because because we query them again.
+ super.dispatchRestoreInstanceState(sparseArray);
+ } catch (Exception e) {
+ Log.e("AllAppsContainerView", "restoreInstanceState viewId = 0", e);
+ }
+
+ Bundle state = (Bundle) sparseArray.get(R.id.work_tab_state_id, null);
+ if (state != null) {
+ int currentPage = state.getInt(BUNDLE_KEY_CURRENT_PAGE, 0);
+ if (currentPage == AdapterHolder.WORK && mViewPager != null) {
+ mViewPager.setCurrentPage(currentPage);
+ rebindAdapters();
+ } else {
+ reset(true);
+ }
+ }
+ }
+
+ @Override
+ protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
+ super.dispatchSaveInstanceState(container);
+ Bundle state = new Bundle();
+ state.putInt(BUNDLE_KEY_CURRENT_PAGE, getCurrentPage());
+ container.put(R.id.work_tab_state_id, state);
+ }
+
+ /**
+ * Sets the long click listener for icons
+ */
+ public void setOnIconLongClickListener(OnLongClickListener listener) {
+ for (AdapterHolder holder : mAH) {
+ holder.mAdapter.setOnIconLongClickListener(listener);
+ }
+ }
+
+ public AllAppsStore getAppsStore() {
+ return mAllAppsStore;
+ }
+
+ public WorkProfileManager getWorkManager() {
+ return mWorkManager;
+ }
+
+ @Override
+ public void onDeviceProfileChanged(DeviceProfile dp) {
+ for (AdapterHolder holder : mAH) {
+ holder.mAdapter.setAppsPerRow(dp.numShownAllAppsColumns);
+ if (holder.mRecyclerView != null) {
+ // Remove all views and clear the pool, while keeping the data same. After this
+ // call, all the viewHolders will be recreated.
+ holder.mRecyclerView.swapAdapter(holder.mRecyclerView.getAdapter(), true);
+ holder.mRecyclerView.getRecycledViewPool().clear();
+ }
+ }
+ updateBackground(dp);
+ }
+
+ protected void updateBackground(DeviceProfile deviceProfile) {
+ mBottomSheetBackground.setVisibility(deviceProfile.isTablet ? View.VISIBLE : View.GONE);
+ // Note: For tablets, the opaque background and header protection are added in drawOnScrim.
+ // For the taskbar entrypoint, the scrim is drawn differently, so a static background is
+ // added in TaskbarAllAppsContainerView and header protection is not yet supported.
+ }
+
+ private void onAppsUpdated() {
+ mHasWorkApps = Stream.of(mAllAppsStore.getApps()).anyMatch(mWorkManager.getMatcher());
+ if (!isSearching()) {
+ rebindAdapters();
+ if (mHasWorkApps) {
+ mWorkManager.reset();
+ }
+ }
+
+ mActivityContext.getStatsLogManager().logger()
+ .withCardinality(mAllAppsStore.getApps().length)
+ .log(LAUNCHER_ALLAPPS_COUNT);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ // The AllAppsContainerView houses the QSB and is hence visible from the Workspace
+ // Overview states. We shouldn't intercept for the scrubber in these cases.
+ if (!isInAllApps()) {
+ mTouchHandler = null;
+ return false;
+ }
+
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ AllAppsRecyclerView rv = getActiveRecyclerView();
+ if (rv != null && rv.getScrollbar() != null
+ && rv.getScrollbar().isHitInParent(ev.getX(), ev.getY(), mFastScrollerOffset)) {
+ mTouchHandler = rv.getScrollbar();
+ } else {
+ mTouchHandler = null;
+ }
+ }
+ if (mTouchHandler != null) {
+ return mTouchHandler.handleTouchEvent(ev, mFastScrollerOffset);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (!isInAllApps()) {
+ return false;
+ }
+
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ AllAppsRecyclerView rv = getActiveRecyclerView();
+ if (rv != null && rv.getScrollbar() != null
+ && rv.getScrollbar().isHitInParent(ev.getX(), ev.getY(), mFastScrollerOffset)) {
+ mTouchHandler = rv.getScrollbar();
+ } else {
+ mTouchHandler = null;
+
+ }
+ }
+ if (mTouchHandler != null) {
+ mTouchHandler.handleTouchEvent(ev, mFastScrollerOffset);
+ return true;
+ }
+ if (isSearching()
+ && mActivityContext.getDragLayer().isEventOverView(getVisibleContainerView(), ev)) {
+ // if in search state, consume touch event.
+ return true;
+ }
+ return false;
+ }
+
+ /** The current active recycler view (A-Z list from one of the profiles, or search results). */
+ public AllAppsRecyclerView getActiveRecyclerView() {
+ if (isSearching()) {
+ return getSearchRecyclerView();
+ }
+ return getActiveAppsRecyclerView();
+ }
+
+ /** The current apps recycler view in the container. */
+ private AllAppsRecyclerView getActiveAppsRecyclerView() {
+ if (!mUsingTabs || isPersonalTab()) {
+ return mAH.get(AdapterHolder.MAIN).mRecyclerView;
+ } else {
+ return mAH.get(AdapterHolder.WORK).mRecyclerView;
+ }
+ }
+
+ /**
+ * The container for A-Z apps (the ViewPager for main+work tabs, or main RV). This is currently
+ * hidden while searching.
+ **/
+ protected View getAppsRecyclerViewContainer() {
+ return mViewPager != null ? mViewPager : findViewById(R.id.apps_list_view);
+ }
+
+ /** The RV for search results, which is hidden while A-Z apps are visible. */
+ public SearchRecyclerView getSearchRecyclerView() {
+ return mSearchRecyclerView;
+ }
+
+ protected boolean isPersonalTab() {
+ return mViewPager == null || mViewPager.getNextPage() == 0;
+ }
+
+ /**
+ * Switches the current page to the provided {@code tab} if tabs are supported, otherwise does
+ * nothing.
+ */
+ public void switchToTab(int tab) {
+ if (mUsingTabs) {
+ mViewPager.setCurrentPage(tab);
+ }
+ }
+
+ public LayoutInflater getLayoutInflater() {
+ return LayoutInflater.from(getContext());
+ }
+
+ @Override
+ public void onDropCompleted(View target, DragObject d, boolean success) {}
+
+ @Override
+ public void setInsets(Rect insets) {
+ mInsets.set(insets);
+ DeviceProfile grid = mActivityContext.getDeviceProfile();
+
+ applyAdapterSideAndBottomPaddings(grid);
+
+ MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
+ mlp.leftMargin = insets.left;
+ mlp.rightMargin = insets.right;
+ setLayoutParams(mlp);
+
+ if (grid.isVerticalBarLayout()) {
+ setPadding(grid.workspacePadding.left, 0, grid.workspacePadding.right, 0);
+ } else {
+ int topPadding = grid.allAppsTopPadding;
+ if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() && !grid.isTablet) {
+ topPadding += getResources().getDimensionPixelSize(
+ R.dimen.all_apps_additional_top_padding_floating_search);
+ }
+ setPadding(grid.allAppsLeftRightMargin, topPadding, grid.allAppsLeftRightMargin, 0);
+ }
+
+ InsettableFrameLayout.dispatchInsets(this, insets);
+ }
+
+ /**
+ * Returns a padding in case a scrim is shown on the bottom of the view and a padding is needed.
+ */
+ protected int computeNavBarScrimHeight(WindowInsets insets) {
+ return 0;
+ }
+
+ /**
+ * Returns the current height of nav bar scrim
+ */
+ public int getNavBarScrimHeight() {
+ return mNavBarScrimHeight;
+ }
+
+ @Override
+ public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
+ mNavBarScrimHeight = computeNavBarScrimHeight(insets);
+ applyAdapterSideAndBottomPaddings(mActivityContext.getDeviceProfile());
+ return super.dispatchApplyWindowInsets(insets);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+
+ if (mNavBarScrimHeight > 0) {
+ canvas.drawRect(0, getHeight() - mNavBarScrimHeight, getWidth(), getHeight(),
+ mNavBarScrimPaint);
+ }
+ }
+
+ protected void updateSearchResultsVisibility() {
+ if (isSearching()) {
+ getSearchRecyclerView().setVisibility(VISIBLE);
+ getAppsRecyclerViewContainer().setVisibility(GONE);
+ mHeader.setVisibility(GONE);
+ } else {
+ getSearchRecyclerView().setVisibility(GONE);
+ getAppsRecyclerViewContainer().setVisibility(VISIBLE);
+ mHeader.setVisibility(VISIBLE);
+ }
+ if (mHeader.isSetUp()) {
+ mHeader.setActiveRV(getCurrentPage());
+ }
+ }
+
+ private void applyAdapterSideAndBottomPaddings(DeviceProfile grid) {
+ int bottomPadding = Math.max(mInsets.bottom, mNavBarScrimHeight);
+ mAH.forEach(adapterHolder -> {
+ adapterHolder.mPadding.bottom = bottomPadding;
+ adapterHolder.mPadding.left =
+ adapterHolder.mPadding.right = grid.allAppsLeftRightPadding;
+ adapterHolder.applyPadding();
+ });
+ }
+
+ private void setDeviceManagementResources() {
+ if (mActivityContext.getStringCache() != null) {
+ Button personalTab = findViewById(R.id.tab_personal);
+ personalTab.setText(mActivityContext.getStringCache().allAppsPersonalTab);
+
+ Button workTab = findViewById(R.id.tab_work);
+ workTab.setText(mActivityContext.getStringCache().allAppsWorkTab);
+ }
+ }
+
+ protected boolean shouldShowTabs() {
+ return mHasWorkApps;
+ }
+
+ // Used by tests only
+ private boolean isDescendantViewVisible(int viewId) {
+ final View view = findViewById(viewId);
+ if (view == null) return false;
+
+ if (!view.isShown()) return false;
+
+ return view.getGlobalVisibleRect(new Rect());
+ }
+
+ @VisibleForTesting
+ public boolean isPersonalTabVisible() {
+ return isDescendantViewVisible(R.id.tab_personal);
+ }
+
+ @VisibleForTesting
+ public boolean isWorkTabVisible() {
+ return isDescendantViewVisible(R.id.tab_work);
+ }
+
+ public AlphabeticalAppsList<T> getSearchResultList() {
+ return mAH.get(SEARCH).mAppsList;
+ }
+
+ public FloatingHeaderView getFloatingHeaderView() {
+ return mHeader;
+ }
+
+ @VisibleForTesting
+ public View getContentView() {
+ return isSearching() ? getSearchRecyclerView() : getAppsRecyclerViewContainer();
+ }
+
+ /** The current page visible in all apps. */
+ public int getCurrentPage() {
+ return isSearching()
+ ? SEARCH
+ : mViewPager == null ? AdapterHolder.MAIN : mViewPager.getNextPage();
+ }
+
+ /** The scroll bar for the active apps recycler view. */
+ public RecyclerViewFastScroller getScrollBar() {
+ AllAppsRecyclerView rv = getActiveAppsRecyclerView();
+ return rv == null ? null : rv.getScrollbar();
+ }
+
+ public boolean isHeaderVisible() {
+ return mHeader != null && mHeader.getVisibility() == View.VISIBLE;
+ }
+
+ /**
+ * Adds an update listener to animator that adds springs to the animation.
+ */
+ public void addSpringFromFlingUpdateListener(ValueAnimator animator,
+ float velocity /* release velocity */,
+ float progress /* portion of the distance to travel*/) {
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animator) {
+ float distance = (1 - progress) * getHeight(); // px
+ float settleVelocity = Math.min(0, distance
+ / (AllAppsTransitionController.INTERP_COEFF * animator.getDuration())
+ + velocity);
+ absorbSwipeUpVelocity(Math.max(1000, Math.abs(
+ Math.round(settleVelocity * FLING_VELOCITY_MULTIPLIER))));
+ }
+ });
+ }
+
+ /** Invoked when the container is pulled. */
+ public void onPull(float deltaDistance, float displacement) {
+ absorbPullDeltaDistance(PULL_MULTIPLIER * deltaDistance, PULL_MULTIPLIER * displacement);
+ // Current motion spec is to actually push and not pull
+ // on this surface. However, until EdgeEffect.onPush (b/190612804) is
+ // implemented at view level, we will simply pull
+ }
+
+ @Override
+ public void getDrawingRect(Rect outRect) {
+ super.getDrawingRect(outRect);
+ outRect.offset(0, (int) getTranslationY());
+ }
+
+ @Override
+ public void setTranslationY(float translationY) {
+ super.setTranslationY(translationY);
+ invalidateHeader();
+ }
+
+ public void setScrimView(ScrimView scrimView) {
+ mScrimView = scrimView;
+ }
+
+ @Override
+ public void drawOnScrimWithScale(Canvas canvas, float scale) {
+ boolean isTablet = mActivityContext.getDeviceProfile().isTablet;
+
+ // Draw full background panel for tablets.
+ if (isTablet) {
+ mHeaderPaint.setColor(mBottomSheetBackgroundColor);
+ View panel = (View) mBottomSheetBackground;
+ float translationY = ((View) panel.getParent()).getTranslationY();
+ mTmpRectF.set(panel.getLeft(), panel.getTop() + translationY,
+ panel.getRight(), panel.getBottom());
+ mTmpPath.reset();
+ mTmpPath.addRoundRect(mTmpRectF, mBottomSheetCornerRadii, Direction.CW);
+ canvas.drawPath(mTmpPath, mHeaderPaint);
+ }
+
+ if (DEBUG_HEADER_PROTECTION) {
+ mHeaderPaint.setColor(Color.MAGENTA);
+ mHeaderPaint.setAlpha(255);
+ } else {
+ mHeaderPaint.setColor(mHeaderColor);
+ mHeaderPaint.setAlpha((int) (getAlpha() * Color.alpha(mHeaderColor)));
+ }
+ if (mHeaderPaint.getColor() == mScrimColor || mHeaderPaint.getColor() == 0) {
+ return;
+ }
+ final float offset = (getVisibleContainerView().getHeight() * (1 - scale) / 2);
+ final float bottom =
+ scale * (getHeaderBottom() + getVisibleContainerView().getPaddingTop()) + offset;
+ FloatingHeaderView headerView = getFloatingHeaderView();
+ if (isTablet) {
+ // Start adding header protection if search bar or tabs will attach to the top.
+ if (!FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() || mUsingTabs) {
+ View panel = (View) mBottomSheetBackground;
+ float translationY = ((View) panel.getParent()).getTranslationY();
+ mTmpRectF.set(panel.getLeft(), panel.getTop() + translationY, panel.getRight(),
+ bottom);
+ mTmpPath.reset();
+ mTmpPath.addRoundRect(mTmpRectF, mBottomSheetCornerRadii, Direction.CW);
+ canvas.drawPath(mTmpPath, mHeaderPaint);
+ }
+ } else {
+ canvas.drawRect(0, 0, canvas.getWidth(), bottom, mHeaderPaint);
+ }
+ int tabsHeight = headerView.getPeripheralProtectionHeight();
+ if (mTabsProtectionAlpha > 0 && tabsHeight != 0) {
+ if (DEBUG_HEADER_PROTECTION) {
+ mHeaderPaint.setColor(Color.BLUE);
+ mHeaderPaint.setAlpha(255);
+ } else {
+ mHeaderPaint.setAlpha((int) (getAlpha() * mTabsProtectionAlpha));
+ }
+ int left = 0;
+ int right = canvas.getWidth();
+ if (isTablet) {
+ left = mBottomSheetBackground.getLeft();
+ right = mBottomSheetBackground.getRight();
+ }
+ canvas.drawRect(left, bottom, right, bottom + tabsHeight, mHeaderPaint);
+ }
+ }
+
+ /**
+ * redraws header protection
+ */
+ public void invalidateHeader() {
+ if (mScrimView != null) {
+ mScrimView.invalidate();
+ }
+ }
+
+ /** Returns the position of the bottom edge of the header */
+ public int getHeaderBottom() {
+ int bottom = (int) getTranslationY() + mHeader.getClipTop();
+ if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
+ if (mActivityContext.getDeviceProfile().isTablet) {
+ return bottom + mBottomSheetBackground.getTop();
+ }
+ return bottom;
+ }
+ return bottom + mHeader.getTop();
+ }
+
+ /**
+ * Returns a view that denotes the visible part of all apps container view.
+ */
+ public View getVisibleContainerView() {
+ return mActivityContext.getDeviceProfile().isTablet ? mBottomSheetBackground : this;
+ }
+
+ protected void onInitializeRecyclerView(RecyclerView rv) {
+ rv.addOnScrollListener(mScrollListener);
+ }
+
+ /** Holds a {@link BaseAllAppsAdapter} and related fields. */
+ public class AdapterHolder {
+ public static final int MAIN = 0;
+ public static final int WORK = 1;
+ public static final int SEARCH = 2;
+
+ private final int mType;
+ public final BaseAllAppsAdapter<T> mAdapter;
+ final RecyclerView.LayoutManager mLayoutManager;
+ final AlphabeticalAppsList<T> mAppsList;
+ final Rect mPadding = new Rect();
+ AllAppsRecyclerView mRecyclerView;
+
+ AdapterHolder(int type) {
+ mType = type;
+ mAppsList = new AlphabeticalAppsList<>(mActivityContext,
+ isSearch() ? null : mAllAppsStore,
+ isWork() ? mWorkManager : null);
+ BaseAdapterProvider[] adapterProviders =
+ new BaseAdapterProvider[]{mMainAdapterProvider};
+
+ mAdapter = createAdapter(mAppsList, adapterProviders);
+ mAppsList.setAdapter(mAdapter);
+ mLayoutManager = mAdapter.getLayoutManager();
+ }
+
+ void setup(@NonNull View rv, @Nullable Predicate<ItemInfo> matcher) {
+ mAppsList.updateItemFilter(matcher);
+ mRecyclerView = (AllAppsRecyclerView) rv;
+ mRecyclerView.setEdgeEffectFactory(createEdgeEffectFactory());
+ mRecyclerView.setApps(mAppsList);
+ mRecyclerView.setLayoutManager(mLayoutManager);
+ mRecyclerView.setAdapter(mAdapter);
+ mRecyclerView.setHasFixedSize(true);
+ // No animations will occur when changes occur to the items in this RecyclerView.
+ mRecyclerView.setItemAnimator(null);
+ onInitializeRecyclerView(mRecyclerView);
+ FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mRecyclerView);
+ mRecyclerView.addItemDecoration(focusedItemDecorator);
+ mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
+ applyPadding();
+ }
+
+ void applyPadding() {
+ if (mRecyclerView != null) {
+ int bottomOffset = 0;
+ if (isWork() && mWorkManager.getWorkModeSwitch() != null) {
+ bottomOffset = mInsets.bottom + mWorkManager.getWorkModeSwitch().getHeight();
+ }
+ mRecyclerView.setPadding(mPadding.left, mPadding.top, mPadding.right,
+ mPadding.bottom + bottomOffset);
+ }
+ }
+
+ private boolean isWork() {
+ return mType == WORK;
+ }
+
+ private boolean isSearch() {
+ return mType == SEARCH;
+ }
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 8cb31fa..7789191 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -299,7 +299,7 @@
return;
} else if (appsView.mViewPager != null) {
int currentPage = appsView.mViewPager.getCurrentPage();
- if (currentPage == BaseAllAppsContainerView.AdapterHolder.WORK) {
+ if (currentPage == ActivityAllAppsContainerView.AdapterHolder.WORK) {
// In work A-Z list
mgr.logger().withContainerInfo(containerInfo).log((mCumulativeVerticalScroll > 0)
? LAUNCHER_WORK_FAB_BUTTON_COLLAPSE
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
deleted file mode 100644
index 9bb8250..0000000
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ /dev/null
@@ -1,996 +0,0 @@
-/*
- * Copyright (C) 2015 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.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_COUNT;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Path.Direction;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.os.Process;
-import android.os.UserManager;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.WindowInsets;
-import android.widget.Button;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import androidx.core.graphics.ColorUtils;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
-import com.android.launcher3.DragSource;
-import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.Insettable;
-import com.android.launcher3.InsettableFrameLayout;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
-import com.android.launcher3.allapps.search.SearchAdapterProvider;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.keyboard.FocusedItemDecorator;
-import com.android.launcher3.model.StringCache;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.Themes;
-import com.android.launcher3.views.ActivityContext;
-import com.android.launcher3.views.BaseDragLayer;
-import com.android.launcher3.views.RecyclerViewFastScroller;
-import com.android.launcher3.views.ScrimView;
-import com.android.launcher3.views.SpringRelativeLayout;
-import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.function.Predicate;
-import java.util.stream.Stream;
-
-/**
- * Base all apps view container.
- *
- * @param <T> Type of context inflating all apps.
- */
-public abstract class BaseAllAppsContainerView<T extends Context & ActivityContext>
- extends SpringRelativeLayout implements DragSource, Insettable,
- OnDeviceProfileChangeListener, OnActivePageChangedListener,
- ScrimView.ScrimDrawingController {
-
- protected static final String BUNDLE_KEY_CURRENT_PAGE = "launcher.allapps.current_page";
-
- public static final float PULL_MULTIPLIER = .02f;
- public static final float FLING_VELOCITY_MULTIPLIER = 1200f;
-
- // Render the header protection at all times to debug clipping issues.
- private static final boolean DEBUG_HEADER_PROTECTION = false;
-
- private final Paint mHeaderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- private final Rect mInsets = new Rect();
-
- /** Context of an activity or window that is inflating this container. */
- protected final T mActivityContext;
- protected final List<AdapterHolder> mAH;
- protected final Predicate<ItemInfo> mPersonalMatcher = ItemInfoMatcher.ofUser(
- Process.myUserHandle());
- private final AllAppsStore mAllAppsStore = new AllAppsStore();
-
- private final RecyclerView.OnScrollListener mScrollListener =
- new RecyclerView.OnScrollListener() {
- @Override
- public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
- updateHeaderScroll(recyclerView.computeVerticalScrollOffset());
- }
- };
-
- protected final WorkProfileManager mWorkManager;
-
- private final Paint mNavBarScrimPaint;
- private int mNavBarScrimHeight = 0;
-
- protected AllAppsPagedView mViewPager;
- private SearchRecyclerView mSearchRecyclerView;
- private SearchAdapterProvider<?> mMainAdapterProvider;
-
- protected FloatingHeaderView mHeader;
- protected View mBottomSheetBackground;
- private View mBottomSheetHandleArea;
-
- /**
- * View that defines the search box. Result is rendered inside {@link #mSearchRecyclerView}.
- */
- protected View mSearchContainer;
- protected SearchUiManager mSearchUiManager;
-
- protected boolean mUsingTabs;
- private boolean mHasWorkApps;
-
- protected RecyclerViewFastScroller mTouchHandler;
- protected final Point mFastScrollerOffset = new Point();
-
- protected final int mScrimColor;
- private final int mHeaderProtectionColor;
- protected final float mHeaderThreshold;
- private final Path mTmpPath = new Path();
- private final RectF mTmpRectF = new RectF();
- private float[] mBottomSheetCornerRadii;
- private ScrimView mScrimView;
- private int mHeaderColor;
- private int mBottomSheetBackgroundColor;
- private int mTabsProtectionAlpha;
-
- protected BaseAllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- mActivityContext = ActivityContext.lookupContext(context);
-
- mScrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
- mHeaderThreshold = getResources().getDimensionPixelSize(
- R.dimen.dynamic_grid_cell_border_spacing);
- mHeaderProtectionColor = Themes.getAttrColor(context, R.attr.allappsHeaderProtectionColor);
-
- mWorkManager = new WorkProfileManager(mActivityContext.getSystemService(UserManager.class),
- this, mActivityContext.getStatsLogManager());
- mAH = Arrays.asList(null, null, null);
- mNavBarScrimPaint = new Paint();
- mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
-
- mAllAppsStore.addUpdateListener(this::onAppsUpdated);
- mActivityContext.addOnDeviceProfileChangeListener(this);
-
- // This is a focus listener that proxies focus from a view into the list view. This is to
- // work around the search box from getting first focus and showing the cursor.
- setOnFocusChangeListener((v, hasFocus) -> {
- if (hasFocus && getActiveRecyclerView() != null) {
- getActiveRecyclerView().requestFocus();
- }
- });
- initContent();
- }
-
- /**
- * Initializes the view hierarchy and internal variables. Any initialization which actually uses
- * these members should be done in {@link #onFinishInflate()}.
- * In terms of subclass initialization, the following would be parallel order for activity:
- * initContent -> onPreCreate
- * constructor/init -> onCreate
- * onFinishInflate -> onPostCreate
- */
- protected void initContent() {
- mMainAdapterProvider = createMainAdapterProvider();
-
- mAH.set(AdapterHolder.MAIN, new AdapterHolder(AdapterHolder.MAIN));
- mAH.set(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
- mAH.set(AdapterHolder.SEARCH, new AdapterHolder(AdapterHolder.SEARCH));
-
- getLayoutInflater().inflate(R.layout.all_apps_content, this);
- mHeader = findViewById(R.id.all_apps_header);
- mBottomSheetBackground = findViewById(R.id.bottom_sheet_background);
- mBottomSheetHandleArea = findViewById(R.id.bottom_sheet_handle_area);
- mSearchRecyclerView = findViewById(R.id.search_results_list_view);
-
- // Add the search box next to the header
- mSearchContainer = inflateSearchBox();
- addView(mSearchContainer, indexOfChild(mHeader) + 1);
- mSearchUiManager = (SearchUiManager) mSearchContainer;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mAH.get(AdapterHolder.SEARCH).setup(mSearchRecyclerView,
- /* Filter out A-Z apps */ itemInfo -> false);
- rebindAdapters(true /* force */);
- float cornerRadius = Themes.getDialogCornerRadius(getContext());
- mBottomSheetCornerRadii = new float[]{
- cornerRadius,
- cornerRadius, // Top left radius in px
- cornerRadius,
- cornerRadius, // Top right radius in px
- 0,
- 0, // Bottom right
- 0,
- 0 // Bottom left
- };
- final TypedValue value = new TypedValue();
- getContext().getTheme().resolveAttribute(android.R.attr.colorBackground, value, true);
- mBottomSheetBackgroundColor = value.data;
- updateBackground(mActivityContext.getDeviceProfile());
- }
-
- /**
- * Inflates the search box
- */
- protected View inflateSearchBox() {
- return getLayoutInflater().inflate(R.layout.search_container_all_apps, this, false);
- }
-
- /** Creates the adapter provider for the main section. */
- protected SearchAdapterProvider<?> createMainAdapterProvider() {
- return new DefaultSearchAdapterProvider(mActivityContext);
- }
-
- /** The adapter provider for the main section. */
- public final SearchAdapterProvider<?> getMainAdapterProvider() {
- return mMainAdapterProvider;
- }
-
- @Override
- protected void dispatchRestoreInstanceState(SparseArray<Parcelable> sparseArray) {
- try {
- // Many slice view id is not properly assigned, and hence throws null
- // pointer exception in the underneath method. Catching the exception
- // simply doesn't restore these slice views. This doesn't have any
- // user visible effect because because we query them again.
- super.dispatchRestoreInstanceState(sparseArray);
- } catch (Exception e) {
- Log.e("AllAppsContainerView", "restoreInstanceState viewId = 0", e);
- }
-
- Bundle state = (Bundle) sparseArray.get(R.id.work_tab_state_id, null);
- if (state != null) {
- int currentPage = state.getInt(BUNDLE_KEY_CURRENT_PAGE, 0);
- if (currentPage == AdapterHolder.WORK && mViewPager != null) {
- mViewPager.setCurrentPage(currentPage);
- rebindAdapters();
- } else {
- reset(true);
- }
- }
- }
-
- @Override
- protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
- super.dispatchSaveInstanceState(container);
- Bundle state = new Bundle();
- state.putInt(BUNDLE_KEY_CURRENT_PAGE, getCurrentPage());
- container.put(R.id.work_tab_state_id, state);
- }
-
- /**
- * Sets the long click listener for icons
- */
- public void setOnIconLongClickListener(OnLongClickListener listener) {
- for (AdapterHolder holder : mAH) {
- holder.mAdapter.setOnIconLongClickListener(listener);
- }
- }
-
- public AllAppsStore getAppsStore() {
- return mAllAppsStore;
- }
-
- public WorkProfileManager getWorkManager() {
- return mWorkManager;
- }
-
- @Override
- public void onDeviceProfileChanged(DeviceProfile dp) {
- for (AdapterHolder holder : mAH) {
- holder.mAdapter.setAppsPerRow(dp.numShownAllAppsColumns);
- if (holder.mRecyclerView != null) {
- // Remove all views and clear the pool, while keeping the data same. After this
- // call, all the viewHolders will be recreated.
- holder.mRecyclerView.swapAdapter(holder.mRecyclerView.getAdapter(), true);
- holder.mRecyclerView.getRecycledViewPool().clear();
- }
- }
- updateBackground(dp);
- }
-
- protected void updateBackground(DeviceProfile deviceProfile) {
- mBottomSheetBackground.setVisibility(deviceProfile.isTablet ? View.VISIBLE : View.GONE);
- // Note: For tablets, the opaque background and header protection are added in drawOnScrim.
- // For the taskbar entrypoint, the scrim is drawn differently, so a static background is
- // added in TaskbarAllAppsContainerView and header protection is not yet supported.
- }
-
- private void onAppsUpdated() {
- mHasWorkApps = Stream.of(mAllAppsStore.getApps()).anyMatch(mWorkManager.getMatcher());
- if (!isSearching()) {
- rebindAdapters();
- if (mHasWorkApps) {
- mWorkManager.reset();
- }
- }
-
- mActivityContext.getStatsLogManager().logger()
- .withCardinality(mAllAppsStore.getApps().length)
- .log(LAUNCHER_ALLAPPS_COUNT);
- }
-
- /**
- * Returns whether the view itself will handle the touch event or not.
- */
- public boolean shouldContainerScroll(MotionEvent ev) {
- BaseDragLayer dragLayer = mActivityContext.getDragLayer();
- // Scroll if not within the container view (e.g. over large-screen scrim).
- if (!dragLayer.isEventOverView(getVisibleContainerView(), ev)) {
- return true;
- }
- if (dragLayer.isEventOverView(mBottomSheetHandleArea, ev)) {
- return true;
- }
- AllAppsRecyclerView rv = getActiveRecyclerView();
- if (rv == null) {
- return true;
- }
- if (rv.getScrollbar() != null
- && rv.getScrollbar().getThumbOffsetY() >= 0
- && dragLayer.isEventOverView(rv.getScrollbar(), ev)) {
- return false;
- }
- return rv.shouldContainerScroll(ev, dragLayer);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- // The AllAppsContainerView houses the QSB and is hence visible from the Workspace
- // Overview states. We shouldn't intercept for the scrubber in these cases.
- if (!isInAllApps()) {
- mTouchHandler = null;
- return false;
- }
-
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- AllAppsRecyclerView rv = getActiveRecyclerView();
- if (rv != null && rv.getScrollbar() != null
- && rv.getScrollbar().isHitInParent(ev.getX(), ev.getY(), mFastScrollerOffset)) {
- mTouchHandler = rv.getScrollbar();
- } else {
- mTouchHandler = null;
- }
- }
- if (mTouchHandler != null) {
- return mTouchHandler.handleTouchEvent(ev, mFastScrollerOffset);
- }
- return false;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- if (!isInAllApps()) {
- return false;
- }
-
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- AllAppsRecyclerView rv = getActiveRecyclerView();
- if (rv != null && rv.getScrollbar() != null
- && rv.getScrollbar().isHitInParent(ev.getX(), ev.getY(), mFastScrollerOffset)) {
- mTouchHandler = rv.getScrollbar();
- } else {
- mTouchHandler = null;
-
- }
- }
- if (mTouchHandler != null) {
- mTouchHandler.handleTouchEvent(ev, mFastScrollerOffset);
- return true;
- }
- if (isSearching()
- && mActivityContext.getDragLayer().isEventOverView(getVisibleContainerView(), ev)) {
- // if in search state, consume touch event.
- return true;
- }
- return false;
- }
-
- /** Description of the container view based on its current state. */
- public String getDescription() {
- StringCache cache = mActivityContext.getStringCache();
- if (mUsingTabs) {
- if (cache != null) {
- return isPersonalTab()
- ? cache.allAppsPersonalTabAccessibility
- : cache.allAppsWorkTabAccessibility;
- } else {
- return isPersonalTab()
- ? getContext().getString(R.string.all_apps_button_personal_label)
- : getContext().getString(R.string.all_apps_button_work_label);
- }
- }
- return getContext().getString(R.string.all_apps_button_label);
- }
-
- /** The current active recycler view (A-Z list from one of the profiles, or search results). */
- public AllAppsRecyclerView getActiveRecyclerView() {
- if (isSearching()) {
- return getSearchRecyclerView();
- }
- return getActiveAppsRecyclerView();
- }
-
- /** The current apps recycler view in the container. */
- private AllAppsRecyclerView getActiveAppsRecyclerView() {
- if (!mUsingTabs || isPersonalTab()) {
- return mAH.get(AdapterHolder.MAIN).mRecyclerView;
- } else {
- return mAH.get(AdapterHolder.WORK).mRecyclerView;
- }
- }
-
- /**
- * The container for A-Z apps (the ViewPager for main+work tabs, or main RV). This is currently
- * hidden while searching.
- **/
- protected View getAppsRecyclerViewContainer() {
- return mViewPager != null ? mViewPager : findViewById(R.id.apps_list_view);
- }
-
- /** The RV for search results, which is hidden while A-Z apps are visible. */
- public SearchRecyclerView getSearchRecyclerView() {
- return mSearchRecyclerView;
- }
-
- protected boolean isPersonalTab() {
- return mViewPager == null || mViewPager.getNextPage() == 0;
- }
-
- /**
- * Switches the current page to the provided {@code tab} if tabs are supported, otherwise does
- * nothing.
- */
- public void switchToTab(int tab) {
- if (mUsingTabs) {
- mViewPager.setCurrentPage(tab);
- }
- }
-
- public LayoutInflater getLayoutInflater() {
- return LayoutInflater.from(getContext());
- }
-
- /**
- * Resets the state of AllApps.
- */
- public void reset(boolean animate) {
- for (int i = 0; i < mAH.size(); i++) {
- if (mAH.get(i).mRecyclerView != null) {
- mAH.get(i).mRecyclerView.scrollToTop();
- }
- }
- if (isHeaderVisible()) {
- mHeader.reset(animate);
- }
- // Reset the base recycler view after transitioning home.
- updateHeaderScroll(0);
- }
-
- @Override
- public void onDropCompleted(View target, DragObject d, boolean success) {}
-
- @Override
- public void setInsets(Rect insets) {
- mInsets.set(insets);
- DeviceProfile grid = mActivityContext.getDeviceProfile();
-
- applyAdapterSideAndBottomPaddings(grid);
-
- MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
- mlp.leftMargin = insets.left;
- mlp.rightMargin = insets.right;
- setLayoutParams(mlp);
-
- if (grid.isVerticalBarLayout()) {
- setPadding(grid.workspacePadding.left, 0, grid.workspacePadding.right, 0);
- } else {
- int topPadding = grid.allAppsTopPadding;
- if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() && !grid.isTablet) {
- topPadding += getResources().getDimensionPixelSize(
- R.dimen.all_apps_additional_top_padding_floating_search);
- }
- setPadding(grid.allAppsLeftRightMargin, topPadding, grid.allAppsLeftRightMargin, 0);
- }
-
- InsettableFrameLayout.dispatchInsets(this, insets);
- }
-
- /**
- * Returns a padding in case a scrim is shown on the bottom of the view and a padding is needed.
- */
- protected int computeNavBarScrimHeight(WindowInsets insets) {
- return 0;
- }
-
- /**
- * Returns the current height of nav bar scrim
- */
- public int getNavBarScrimHeight() {
- return mNavBarScrimHeight;
- }
-
- @Override
- public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
- mNavBarScrimHeight = computeNavBarScrimHeight(insets);
- applyAdapterSideAndBottomPaddings(mActivityContext.getDeviceProfile());
- return super.dispatchApplyWindowInsets(insets);
- }
-
- @Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
-
- if (mNavBarScrimHeight > 0) {
- canvas.drawRect(0, getHeight() - mNavBarScrimHeight, getWidth(), getHeight(),
- mNavBarScrimPaint);
- }
- }
-
- protected void rebindAdapters() {
- rebindAdapters(false /* force */);
- }
-
- protected void rebindAdapters(boolean force) {
- updateSearchResultsVisibility();
-
- boolean showTabs = shouldShowTabs();
- if (showTabs == mUsingTabs && !force) {
- return;
- }
-
- if (isSearching()) {
- mUsingTabs = showTabs;
- mWorkManager.detachWorkModeSwitch();
- return;
- }
-
- // replaceAppsRVcontainer() needs to use both mUsingTabs value to remove the old view AND
- // showTabs value to create new view. Hence the mUsingTabs new value assignment MUST happen
- // after this call.
- replaceAppsRVContainer(showTabs);
- mUsingTabs = showTabs;
-
- mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView);
- mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
- mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.SEARCH).mRecyclerView);
-
- if (mUsingTabs) {
- mAH.get(AdapterHolder.MAIN).setup(mViewPager.getChildAt(0), mPersonalMatcher);
- mAH.get(AdapterHolder.WORK).setup(mViewPager.getChildAt(1), mWorkManager.getMatcher());
- mAH.get(AdapterHolder.WORK).mRecyclerView.setId(R.id.apps_list_view_work);
- if (FeatureFlags.ENABLE_EXPANDING_PAUSE_WORK_BUTTON.get()) {
- mAH.get(AdapterHolder.WORK).mRecyclerView.addOnScrollListener(
- mWorkManager.newScrollListener());
- }
- mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.MAIN);
- findViewById(R.id.tab_personal)
- .setOnClickListener((View view) -> {
- if (mViewPager.snapToPage(AdapterHolder.MAIN)) {
- mActivityContext.getStatsLogManager().logger()
- .log(LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB);
- }
- mActivityContext.hideKeyboard();
- });
- findViewById(R.id.tab_work)
- .setOnClickListener((View view) -> {
- if (mViewPager.snapToPage(AdapterHolder.WORK)) {
- mActivityContext.getStatsLogManager().logger()
- .log(LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB);
- }
- mActivityContext.hideKeyboard();
- });
- setDeviceManagementResources();
- onActivePageChanged(mViewPager.getNextPage());
- } else {
- mAH.get(AdapterHolder.MAIN).setup(findViewById(R.id.apps_list_view), null);
- mAH.get(AdapterHolder.WORK).mRecyclerView = null;
- }
- setupHeader();
-
- mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView);
- mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
- mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.SEARCH).mRecyclerView);
- }
-
- protected void updateSearchResultsVisibility() {
- if (isSearching()) {
- getSearchRecyclerView().setVisibility(VISIBLE);
- getAppsRecyclerViewContainer().setVisibility(GONE);
- mHeader.setVisibility(GONE);
- } else {
- getSearchRecyclerView().setVisibility(GONE);
- getAppsRecyclerViewContainer().setVisibility(VISIBLE);
- mHeader.setVisibility(VISIBLE);
- }
- if (mHeader.isSetUp()) {
- mHeader.setActiveRV(getCurrentPage());
- }
- }
-
- private void applyAdapterSideAndBottomPaddings(DeviceProfile grid) {
- int bottomPadding = Math.max(mInsets.bottom, mNavBarScrimHeight);
- mAH.forEach(adapterHolder -> {
- adapterHolder.mPadding.bottom = bottomPadding;
- adapterHolder.mPadding.left =
- adapterHolder.mPadding.right = grid.allAppsLeftRightPadding;
- adapterHolder.applyPadding();
- });
- }
-
- private void setDeviceManagementResources() {
- if (mActivityContext.getStringCache() != null) {
- Button personalTab = findViewById(R.id.tab_personal);
- personalTab.setText(mActivityContext.getStringCache().allAppsPersonalTab);
-
- Button workTab = findViewById(R.id.tab_work);
- workTab.setText(mActivityContext.getStringCache().allAppsWorkTab);
- }
- }
-
- protected boolean shouldShowTabs() {
- return mHasWorkApps;
- }
-
- protected boolean isSearching() {
- return false;
- }
-
- protected View replaceAppsRVContainer(boolean showTabs) {
- for (int i = AdapterHolder.MAIN; i <= AdapterHolder.WORK; i++) {
- AdapterHolder adapterHolder = mAH.get(i);
- if (adapterHolder.mRecyclerView != null) {
- adapterHolder.mRecyclerView.setLayoutManager(null);
- adapterHolder.mRecyclerView.setAdapter(null);
- }
- }
- View oldView = getAppsRecyclerViewContainer();
- int index = indexOfChild(oldView);
- removeView(oldView);
- int layout = showTabs ? R.layout.all_apps_tabs : R.layout.all_apps_rv_layout;
- View newView = getLayoutInflater().inflate(layout, this, false);
- addView(newView, index);
- if (showTabs) {
- mViewPager = (AllAppsPagedView) newView;
- mViewPager.initParentViews(this);
- mViewPager.getPageIndicator().setOnActivePageChangedListener(this);
-
- mWorkManager.reset();
- post(() -> mAH.get(AdapterHolder.WORK).applyPadding());
-
- } else {
- mWorkManager.detachWorkModeSwitch();
- mViewPager = null;
- }
- return newView;
- }
-
- @Override
- public void onActivePageChanged(int currentActivePage) {
- if (mAH.get(currentActivePage).mRecyclerView != null) {
- mAH.get(currentActivePage).mRecyclerView.bindFastScrollbar();
- }
- // Header keeps track of active recycler view to properly render header protection.
- mHeader.setActiveRV(currentActivePage);
- reset(true /* animate */);
-
- mWorkManager.onActivePageChanged(currentActivePage);
- }
-
- // Used by tests only
- private boolean isDescendantViewVisible(int viewId) {
- final View view = findViewById(viewId);
- if (view == null) return false;
-
- if (!view.isShown()) return false;
-
- return view.getGlobalVisibleRect(new Rect());
- }
-
- @VisibleForTesting
- public boolean isPersonalTabVisible() {
- return isDescendantViewVisible(R.id.tab_personal);
- }
-
- @VisibleForTesting
- public boolean isWorkTabVisible() {
- return isDescendantViewVisible(R.id.tab_work);
- }
-
- public AlphabeticalAppsList<T> getSearchResultList() {
- return mAH.get(AdapterHolder.SEARCH).mAppsList;
- }
-
- public FloatingHeaderView getFloatingHeaderView() {
- return mHeader;
- }
-
- @VisibleForTesting
- public View getContentView() {
- return isSearching() ? getSearchRecyclerView() : getAppsRecyclerViewContainer();
- }
-
- /** The current page visible in all apps. */
- public int getCurrentPage() {
- return isSearching()
- ? AdapterHolder.SEARCH
- : mViewPager == null ? AdapterHolder.MAIN : mViewPager.getNextPage();
- }
-
- /** The scroll bar for the active apps recycler view. */
- public RecyclerViewFastScroller getScrollBar() {
- AllAppsRecyclerView rv = getActiveAppsRecyclerView();
- return rv == null ? null : rv.getScrollbar();
- }
-
- void setupHeader() {
- mHeader.setVisibility(View.VISIBLE);
- boolean tabsHidden = !mUsingTabs;
- mHeader.setup(
- mAH.get(AdapterHolder.MAIN).mRecyclerView,
- mAH.get(AdapterHolder.WORK).mRecyclerView,
- (SearchRecyclerView) mAH.get(AdapterHolder.SEARCH).mRecyclerView,
- getCurrentPage(),
- tabsHidden);
-
- int padding = mHeader.getMaxTranslation();
- mAH.forEach(adapterHolder -> {
- adapterHolder.mPadding.top = padding;
- adapterHolder.applyPadding();
- if (adapterHolder.mRecyclerView != null) {
- adapterHolder.mRecyclerView.scrollToTop();
- }
- });
- }
-
- public boolean isHeaderVisible() {
- return mHeader != null && mHeader.getVisibility() == View.VISIBLE;
- }
-
- /**
- * Adds an update listener to animator that adds springs to the animation.
- */
- public void addSpringFromFlingUpdateListener(ValueAnimator animator,
- float velocity /* release velocity */,
- float progress /* portion of the distance to travel*/) {
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animator) {
- float distance = (1 - progress) * getHeight(); // px
- float settleVelocity = Math.min(0, distance
- / (AllAppsTransitionController.INTERP_COEFF * animator.getDuration())
- + velocity);
- absorbSwipeUpVelocity(Math.max(1000, Math.abs(
- Math.round(settleVelocity * FLING_VELOCITY_MULTIPLIER))));
- }
- });
- }
-
- /** Invoked when the container is pulled. */
- public void onPull(float deltaDistance, float displacement) {
- absorbPullDeltaDistance(PULL_MULTIPLIER * deltaDistance, PULL_MULTIPLIER * displacement);
- // Current motion spec is to actually push and not pull
- // on this surface. However, until EdgeEffect.onPush (b/190612804) is
- // implemented at view level, we will simply pull
- }
-
- @Override
- public void getDrawingRect(Rect outRect) {
- super.getDrawingRect(outRect);
- outRect.offset(0, (int) getTranslationY());
- }
-
- @Override
- public void setTranslationY(float translationY) {
- super.setTranslationY(translationY);
- invalidateHeader();
- }
-
- public void setScrimView(ScrimView scrimView) {
- mScrimView = scrimView;
- }
-
- @Override
- public void drawOnScrimWithScale(Canvas canvas, float scale) {
- boolean isTablet = mActivityContext.getDeviceProfile().isTablet;
-
- // Draw full background panel for tablets.
- if (isTablet) {
- mHeaderPaint.setColor(mBottomSheetBackgroundColor);
- View panel = (View) mBottomSheetBackground;
- float translationY = ((View) panel.getParent()).getTranslationY();
- mTmpRectF.set(panel.getLeft(), panel.getTop() + translationY,
- panel.getRight(), panel.getBottom());
- mTmpPath.reset();
- mTmpPath.addRoundRect(mTmpRectF, mBottomSheetCornerRadii, Direction.CW);
- canvas.drawPath(mTmpPath, mHeaderPaint);
- }
-
- if (DEBUG_HEADER_PROTECTION) {
- mHeaderPaint.setColor(Color.MAGENTA);
- mHeaderPaint.setAlpha(255);
- } else {
- mHeaderPaint.setColor(mHeaderColor);
- mHeaderPaint.setAlpha((int) (getAlpha() * Color.alpha(mHeaderColor)));
- }
- if (mHeaderPaint.getColor() == mScrimColor || mHeaderPaint.getColor() == 0) {
- return;
- }
- final float offset = (getVisibleContainerView().getHeight() * (1 - scale) / 2);
- final float bottom =
- scale * (getHeaderBottom() + getVisibleContainerView().getPaddingTop()) + offset;
- FloatingHeaderView headerView = getFloatingHeaderView();
- if (isTablet) {
- // Start adding header protection if search bar or tabs will attach to the top.
- if (!FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() || mUsingTabs) {
- View panel = (View) mBottomSheetBackground;
- float translationY = ((View) panel.getParent()).getTranslationY();
- mTmpRectF.set(panel.getLeft(), panel.getTop() + translationY, panel.getRight(),
- bottom);
- mTmpPath.reset();
- mTmpPath.addRoundRect(mTmpRectF, mBottomSheetCornerRadii, Direction.CW);
- canvas.drawPath(mTmpPath, mHeaderPaint);
- }
- } else {
- canvas.drawRect(0, 0, canvas.getWidth(), bottom, mHeaderPaint);
- }
- int tabsHeight = headerView.getPeripheralProtectionHeight();
- if (mTabsProtectionAlpha > 0 && tabsHeight != 0) {
- if (DEBUG_HEADER_PROTECTION) {
- mHeaderPaint.setColor(Color.BLUE);
- mHeaderPaint.setAlpha(255);
- } else {
- mHeaderPaint.setAlpha((int) (getAlpha() * mTabsProtectionAlpha));
- }
- int left = 0;
- int right = canvas.getWidth();
- if (isTablet) {
- left = mBottomSheetBackground.getLeft();
- right = mBottomSheetBackground.getRight();
- }
- canvas.drawRect(left, bottom, right, bottom + tabsHeight, mHeaderPaint);
- }
- }
-
- /**
- * redraws header protection
- */
- public void invalidateHeader() {
- if (mScrimView != null) {
- mScrimView.invalidate();
- }
- }
-
- protected void updateHeaderScroll(int scrolledOffset) {
- float prog = Utilities.boundToRange((float) scrolledOffset / mHeaderThreshold, 0f, 1f);
- int headerColor = getHeaderColor(prog);
- int tabsAlpha = mHeader.getPeripheralProtectionHeight() == 0 ? 0
- : (int) (Utilities.boundToRange(
- (scrolledOffset + mHeader.mSnappedScrolledY) / mHeaderThreshold, 0f, 1f)
- * 255);
- if (headerColor != mHeaderColor || mTabsProtectionAlpha != tabsAlpha) {
- mHeaderColor = headerColor;
- mTabsProtectionAlpha = tabsAlpha;
- invalidateHeader();
- }
- }
-
- protected int getHeaderColor(float blendRatio) {
- return ColorUtils.blendARGB(mScrimColor, mHeaderProtectionColor, blendRatio);
- }
-
- protected abstract BaseAllAppsAdapter<T> createAdapter(AlphabeticalAppsList<T> mAppsList,
- BaseAdapterProvider[] adapterProviders);
-
- public int getHeaderBottom() {
- int bottom = (int) getTranslationY() + mHeader.getClipTop();
- if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
- if (mActivityContext.getDeviceProfile().isTablet) {
- return bottom + mBottomSheetBackground.getTop();
- }
- return bottom;
- }
- return bottom + mHeader.getTop();
- }
-
- /**
- * Returns a view that denotes the visible part of all apps container view.
- */
- public View getVisibleContainerView() {
- return mActivityContext.getDeviceProfile().isTablet ? mBottomSheetBackground : this;
- }
-
- protected void onInitializeRecyclerView(RecyclerView rv) {
- rv.addOnScrollListener(mScrollListener);
- }
-
- /**
- * Returns {@code true} the All Apps UI is currently being displayed on the target surface and
- * is interactive.
- */
- public abstract boolean isInAllApps();
-
- /** Holds a {@link BaseAllAppsAdapter} and related fields. */
- public class AdapterHolder {
- public static final int MAIN = 0;
- public static final int WORK = 1;
- public static final int SEARCH = 2;
-
- private final int mType;
- public final BaseAllAppsAdapter<T> mAdapter;
- final RecyclerView.LayoutManager mLayoutManager;
- final AlphabeticalAppsList<T> mAppsList;
- final Rect mPadding = new Rect();
- AllAppsRecyclerView mRecyclerView;
-
- AdapterHolder(int type) {
- mType = type;
- mAppsList = new AlphabeticalAppsList<>(mActivityContext,
- isSearch() ? null : mAllAppsStore,
- isWork() ? mWorkManager : null);
- BaseAdapterProvider[] adapterProviders =
- new BaseAdapterProvider[]{mMainAdapterProvider};
-
- mAdapter = createAdapter(mAppsList, adapterProviders);
- mAppsList.setAdapter(mAdapter);
- mLayoutManager = mAdapter.getLayoutManager();
- }
-
- void setup(@NonNull View rv, @Nullable Predicate<ItemInfo> matcher) {
- mAppsList.updateItemFilter(matcher);
- mRecyclerView = (AllAppsRecyclerView) rv;
- mRecyclerView.setEdgeEffectFactory(createEdgeEffectFactory());
- mRecyclerView.setApps(mAppsList);
- mRecyclerView.setLayoutManager(mLayoutManager);
- mRecyclerView.setAdapter(mAdapter);
- mRecyclerView.setHasFixedSize(true);
- // No animations will occur when changes occur to the items in this RecyclerView.
- mRecyclerView.setItemAnimator(null);
- onInitializeRecyclerView(mRecyclerView);
- FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mRecyclerView);
- mRecyclerView.addItemDecoration(focusedItemDecorator);
- mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
- applyPadding();
- }
-
- void applyPadding() {
- if (mRecyclerView != null) {
- int bottomOffset = 0;
- if (isWork() && mWorkManager.getWorkModeSwitch() != null) {
- bottomOffset = mInsets.bottom + mWorkManager.getWorkModeSwitch().getHeight();
- }
- mRecyclerView.setPadding(mPadding.left, mPadding.top, mPadding.right,
- mPadding.bottom + bottomOffset);
- }
- }
-
- private boolean isWork() {
- return mType == WORK;
- }
-
- private boolean isSearch() {
- return mType == SEARCH;
- }
- }
-}
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index c18f9e1..b3ea3ab 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -32,7 +32,7 @@
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
-import com.android.launcher3.allapps.BaseAllAppsContainerView.AdapterHolder;
+import com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.views.ActivityContext;
@@ -72,8 +72,8 @@
moved(current);
applyVerticalMove();
if (headerCollapsed != mHeaderCollapsed) {
- BaseAllAppsContainerView<?> parent =
- (BaseAllAppsContainerView<?>) getParent();
+ ActivityAllAppsContainerView<?> parent =
+ (ActivityAllAppsContainerView<?>) getParent();
parent.invalidateHeader();
}
}
@@ -191,7 +191,7 @@
updateExpectedHeight();
if (mMaxTranslation != oldMaxHeight || mFloatingRowsCollapsed) {
- BaseAllAppsContainerView<?> parent = (BaseAllAppsContainerView<?>) getParent();
+ ActivityAllAppsContainerView parent = (ActivityAllAppsContainerView) getParent();
if (parent != null) {
parent.setupHeader();
}
diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java
index fa03905..f66ea34 100644
--- a/src/com/android/launcher3/allapps/WorkProfileManager.java
+++ b/src/com/android/launcher3/allapps/WorkProfileManager.java
@@ -16,11 +16,11 @@
package com.android.launcher3.allapps;
import static com.android.launcher3.LauncherPrefs.WORK_EDU_STEP;
+import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN;
+import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
+import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.WORK;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_DISABLED_CARD;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_EDU_CARD;
-import static com.android.launcher3.allapps.BaseAllAppsContainerView.AdapterHolder.MAIN;
-import static com.android.launcher3.allapps.BaseAllAppsContainerView.AdapterHolder.SEARCH;
-import static com.android.launcher3.allapps.BaseAllAppsContainerView.AdapterHolder.WORK;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TURN_OFF_WORK_APPS_TAP;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
@@ -54,7 +54,7 @@
import java.util.function.Predicate;
/**
- * Companion class for {@link BaseAllAppsContainerView} to manage work tab and personal tab
+ * Companion class for {@link ActivityAllAppsContainerView} to manage work tab and personal tab
* related
* logic based on {@link WorkProfileState}?
*/
@@ -79,7 +79,7 @@
public @interface WorkProfileState { }
private final UserManager mUserManager;
- private final BaseAllAppsContainerView<?> mAllApps;
+ private final ActivityAllAppsContainerView<?> mAllApps;
private final Predicate<ItemInfo> mMatcher;
private final StatsLogManager mStatsLogManager;
@@ -89,7 +89,7 @@
private int mCurrentState;
public WorkProfileManager(
- UserManager userManager, BaseAllAppsContainerView<?> allApps,
+ UserManager userManager, ActivityAllAppsContainerView allApps,
StatsLogManager statsLogManager) {
mUserManager = userManager;
mAllApps = allApps;
@@ -152,7 +152,7 @@
}
/**
- * Creates and attaches for profile toggle button to {@link BaseAllAppsContainerView}
+ * Creates and attaches for profile toggle button to {@link ActivityAllAppsContainerView}
*/
public boolean attachWorkModeSwitch() {
if (!mAllApps.getAppsStore().hasModelFlag(
@@ -177,7 +177,7 @@
return true;
}
/**
- * Removes work profile toggle button from {@link BaseAllAppsContainerView}
+ * Removes work profile toggle button from {@link ActivityAllAppsContainerView}
*/
public void detachWorkModeSwitch() {
if (mWorkModeSwitch != null && mWorkModeSwitch.getParent() == mAllApps) {
@@ -195,7 +195,7 @@
return mWorkModeSwitch;
}
- private BaseAllAppsContainerView<?>.AdapterHolder getAH() {
+ private ActivityAllAppsContainerView.AdapterHolder getAH() {
return mAllApps.mAH.get(WORK);
}
diff --git a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
index 3890741..4f7f9af 100644
--- a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
@@ -16,7 +16,6 @@
package com.android.launcher3.allapps.search;
-import android.net.Uri;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
@@ -38,12 +37,6 @@
}
/**
- * Called from LiveSearchManager to notify slice status updates.
- */
- public void onSliceStatusUpdate(Uri sliceUri) {
- }
-
- /**
* Handles selection event on search adapter item. Returns false if provider can not handle
* event
*/