diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index de5419eb7b..5b21fb8260 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -132,7 +132,6 @@ import com.android.launcher3.allapps.ActivityAllAppsContainerView; 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 @@ public class Launcher extends StatefulActivity 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 2511cada78..8a02a5452e 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 @@ import java.util.ArrayList; * @param Type of context inflating all apps. */ public class ActivityAllAppsContainerView - extends BaseAllAppsContainerView { + 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 mAH; + protected final Predicate 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 class ActivityAllAppsContainerView 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 @@ public class ActivityAllAppsContainerView }); } - @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 @@ public class ActivityAllAppsContainerView 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 @@ public class ActivityAllAppsContainerView // 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); + } + + protected void rebindAdapters() { + rebindAdapters(false /* force */); } - @Override 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 @@ public class ActivityAllAppsContainerView 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 @@ public class ActivityAllAppsContainerView } } - @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 @@ public class ActivityAllAppsContainerView 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 @@ public class ActivityAllAppsContainerView layoutParams.removeRule(RelativeLayout.ALIGN_PARENT_TOP); } - @Override protected BaseAllAppsAdapter createAdapter(AlphabeticalAppsList appsList, BaseAdapterProvider[] adapterProviders) { return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), appsList, @@ -339,9 +648,558 @@ public class ActivityAllAppsContainerView : 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 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 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 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 mAdapter; + final RecyclerView.LayoutManager mLayoutManager; + final AlphabeticalAppsList 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 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 8cb31fa2e1..7789191370 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -299,7 +299,7 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView { 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 9bb825084b..0000000000 --- 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 Type of context inflating all apps. - */ -public abstract class BaseAllAppsContainerView - 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 mAH; - protected final Predicate 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 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 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 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 createAdapter(AlphabeticalAppsList 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 mAdapter; - final RecyclerView.LayoutManager mLayoutManager; - final AlphabeticalAppsList 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 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 c18f9e1884..b3ea3ab225 100644 --- a/src/com/android/launcher3/allapps/FloatingHeaderView.java +++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java @@ -32,7 +32,7 @@ import androidx.recyclerview.widget.RecyclerView; 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 @@ public class FloatingHeaderView extends LinearLayout implements moved(current); applyVerticalMove(); if (headerCollapsed != mHeaderCollapsed) { - BaseAllAppsContainerView parent = - (BaseAllAppsContainerView) getParent(); + ActivityAllAppsContainerView parent = + (ActivityAllAppsContainerView) getParent(); parent.invalidateHeader(); } } @@ -191,7 +191,7 @@ public class FloatingHeaderView extends LinearLayout implements 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 fa03905515..f66ea34d7c 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.ArrayList; 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 class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP public @interface WorkProfileState { } private final UserManager mUserManager; - private final BaseAllAppsContainerView mAllApps; + private final ActivityAllAppsContainerView mAllApps; private final Predicate mMatcher; private final StatsLogManager mStatsLogManager; @@ -89,7 +89,7 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP private int mCurrentState; public WorkProfileManager( - UserManager userManager, BaseAllAppsContainerView allApps, + UserManager userManager, ActivityAllAppsContainerView allApps, StatsLogManager statsLogManager) { mUserManager = userManager; mAllApps = allApps; @@ -152,7 +152,7 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP } /** - * 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 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP 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 @@ public class WorkProfileManager implements PersonalWorkSlidingTabStrip.OnActiveP 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 3890741d8e..4f7f9afe39 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; @@ -37,12 +36,6 @@ public abstract class SearchAdapterProvider extends B mLauncher = launcher; } - /** - * 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