| /* |
| * 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 android.content.Context; |
| import android.graphics.Bitmap; |
| import android.graphics.Rect; |
| import android.os.Process; |
| import android.support.annotation.NonNull; |
| import android.support.annotation.Nullable; |
| import android.support.v4.view.PagerAdapter; |
| import android.support.v4.view.ViewPager; |
| import android.support.v7.widget.LinearLayoutManager; |
| import android.support.v7.widget.RecyclerView; |
| import android.text.Selection; |
| import android.text.SpannableStringBuilder; |
| import android.util.AttributeSet; |
| import android.view.KeyEvent; |
| import android.view.LayoutInflater; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.RelativeLayout; |
| |
| import com.android.launcher3.AppInfo; |
| import com.android.launcher3.BubbleTextView; |
| import com.android.launcher3.ClickShadowView; |
| import com.android.launcher3.DeviceProfile; |
| import com.android.launcher3.DragSource; |
| import com.android.launcher3.DropTarget; |
| import com.android.launcher3.DropTarget.DragObject; |
| import com.android.launcher3.Insettable; |
| import com.android.launcher3.ItemInfo; |
| import com.android.launcher3.Launcher; |
| import com.android.launcher3.LauncherState; |
| import com.android.launcher3.PromiseAppInfo; |
| import com.android.launcher3.R; |
| import com.android.launcher3.anim.SpringAnimationHandler; |
| import com.android.launcher3.config.FeatureFlags; |
| import com.android.launcher3.dragndrop.DragController; |
| import com.android.launcher3.dragndrop.DragOptions; |
| import com.android.launcher3.keyboard.FocusedItemDecorator; |
| import com.android.launcher3.userevent.nano.LauncherLogProto.Target; |
| import com.android.launcher3.util.ComponentKey; |
| import com.android.launcher3.util.ComponentKeyMapper; |
| import com.android.launcher3.util.ItemInfoMatcher; |
| import com.android.launcher3.util.PackageUserKey; |
| import com.android.launcher3.util.TransformingTouchDelegate; |
| import com.android.launcher3.views.SlidingTabStrip; |
| |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * The all apps view container. |
| */ |
| public class AllAppsContainerView extends RelativeLayout implements DragSource, |
| View.OnLongClickListener, Insettable, DeviceProfile.LauncherLayoutChangeListener, |
| BubbleTextView.BubbleTextShadowHandler { |
| |
| protected final Rect mBasePadding = new Rect(); |
| |
| private final Launcher mLauncher; |
| private final AdapterHolder[] mAH; |
| private final ClickShadowView mTouchFeedbackView; |
| private final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(Process.myUserHandle()); |
| private final ItemInfoMatcher mWorkMatcher = ItemInfoMatcher.not(mPersonalMatcher); |
| |
| private SearchUiManager mSearchUiManager; |
| private View mSearchContainer; |
| private InterceptingViewPager mViewPager; |
| private ViewGroup mHeader; |
| private FloatingHeaderHandler mFloatingHeaderHandler; |
| |
| private SpannableStringBuilder mSearchQueryBuilder = null; |
| |
| private int mNumAppsPerRow; |
| private int mNumPredictedAppsPerRow; |
| |
| private TransformingTouchDelegate mTouchDelegate; |
| private boolean mUsingTabs; |
| |
| private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>(); |
| |
| public AllAppsContainerView(Context context) { |
| this(context, null); |
| } |
| |
| public AllAppsContainerView(Context context, AttributeSet attrs) { |
| this(context, attrs, 0); |
| } |
| |
| public AllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) { |
| super(context, attrs, defStyleAttr); |
| |
| mLauncher = Launcher.getLauncher(context); |
| |
| mSearchQueryBuilder = new SpannableStringBuilder(); |
| |
| Selection.setSelection(mSearchQueryBuilder, 0); |
| |
| mTouchFeedbackView = new ClickShadowView(context); |
| // Make the feedback view large enough to hold the blur bitmap. |
| int size = mLauncher.getDeviceProfile().allAppsIconSizePx |
| + mTouchFeedbackView.getExtraSize(); |
| addView(mTouchFeedbackView, size, size); |
| |
| mAH = new AdapterHolder[2]; |
| mAH[AdapterHolder.MAIN] = new AdapterHolder(); |
| mAH[AdapterHolder.WORK] = new AdapterHolder(); |
| } |
| |
| @Override |
| protected void onAttachedToWindow() { |
| super.onAttachedToWindow(); |
| |
| DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile(); |
| grid.addLauncherLayoutChangedListener(this); |
| |
| applyTouchDelegate(); |
| } |
| |
| private void applyTouchDelegate() { |
| RecyclerView rv = getActiveRecyclerView(); |
| mTouchDelegate = new TransformingTouchDelegate(rv); |
| mTouchDelegate.setBounds( |
| rv.getLeft() - mBasePadding.left, |
| rv.getTop() - mBasePadding.top, |
| rv.getRight() + mBasePadding.right, |
| rv.getBottom() + mBasePadding.bottom); |
| setTouchDelegate(mTouchDelegate); |
| } |
| |
| @Override |
| protected void onDetachedFromWindow() { |
| super.onDetachedFromWindow(); |
| |
| DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile(); |
| grid.removeLauncherLayoutChangedListener(this); |
| } |
| |
| /** |
| * Calculate the background padding as it can change due to insets/content padding change. |
| */ |
| @Override |
| public void onLauncherLayoutChanged() { |
| DeviceProfile grid = mLauncher.getDeviceProfile(); |
| if (!grid.isVerticalBarLayout()) { |
| return; |
| } |
| |
| int[] padding = grid.getContainerPadding(); |
| int paddingLeft = padding[0]; |
| int paddingRight = padding[1]; |
| mBasePadding.set(paddingLeft, 0, paddingRight, 0); |
| setPadding(paddingLeft, 0, paddingRight, 0); |
| } |
| |
| @Override |
| protected void onLayout(boolean changed, int left, int top, int right, int bottom) { |
| super.onLayout(changed, left, top, right, bottom); |
| applyTouchDelegate(); |
| } |
| |
| @Override |
| public void setPressedIcon(BubbleTextView icon, Bitmap background) { |
| mTouchFeedbackView.setPressedIcon(icon, background); |
| } |
| |
| /** |
| * Sets the current set of apps. |
| */ |
| public void setApps(List<AppInfo> apps) { |
| boolean hasWorkProfileApp = hasWorkProfileApp(apps); |
| if (mUsingTabs != hasWorkProfileApp) { |
| rebindAdapters(hasWorkProfileApp); |
| } |
| mComponentToAppMap.clear(); |
| addOrUpdateApps(apps); |
| } |
| |
| /** |
| * Adds or updates existing apps in the list |
| */ |
| public void addOrUpdateApps(List<AppInfo> apps) { |
| for (AppInfo app : apps) { |
| mComponentToAppMap.put(app.toComponentKey(), app); |
| } |
| onAppsUpdated(); |
| mSearchUiManager.refreshSearchResult(); |
| } |
| |
| /** |
| * Removes some apps from the list. |
| */ |
| public void removeApps(List<AppInfo> apps) { |
| for (AppInfo app : apps) { |
| mComponentToAppMap.remove(app.toComponentKey()); |
| } |
| onAppsUpdated(); |
| mSearchUiManager.refreshSearchResult(); |
| } |
| |
| private void onAppsUpdated() { |
| for (int i = 0; i < getNumOfAdapters(); i++) { |
| mAH[i].appsList.onAppsUpdated(); |
| } |
| } |
| |
| private int getNumOfAdapters() { |
| return mUsingTabs ? mAH.length : 1; |
| } |
| |
| public void updatePromiseAppProgress(PromiseAppInfo app) { |
| for (int i = 0; i < mAH.length; i++) { |
| updatePromiseAppProgress(app, mAH[i].recyclerView); |
| } |
| if (mFloatingHeaderHandler != null) { |
| updatePromiseAppProgress(app, mFloatingHeaderHandler.getContentView()); |
| } |
| } |
| |
| private void updatePromiseAppProgress(PromiseAppInfo app, ViewGroup parent) { |
| if (parent == null) { |
| return; |
| } |
| int childCount = parent.getChildCount(); |
| for (int i = 0; i < childCount; i++) { |
| View child = parent.getChildAt(i); |
| if (child instanceof BubbleTextView && child.getTag() == app) { |
| BubbleTextView bubbleTextView = (BubbleTextView) child; |
| bubbleTextView.applyProgressLevel(app.level); |
| } |
| } |
| } |
| |
| /** |
| * Returns whether the view itself will handle the touch event or not. |
| */ |
| public boolean shouldContainerScroll(MotionEvent ev) { |
| // IF the MotionEvent is inside the search box, and the container keeps on receiving |
| // touch input, container should move down. |
| if (mLauncher.getDragLayer().isEventOverView(mSearchContainer, ev)) { |
| return true; |
| } |
| if (mUsingTabs && mLauncher.getDragLayer().isEventOverView(mHeader, ev)) { |
| return true; |
| } |
| AllAppsRecyclerView rv = getActiveRecyclerView(); |
| return rv == null || rv.shouldContainerScroll(ev, mLauncher.getDragLayer()); |
| } |
| |
| public AllAppsRecyclerView getActiveRecyclerView() { |
| if (!mUsingTabs || mViewPager.getCurrentItem() == 0) { |
| return mAH[AdapterHolder.MAIN].recyclerView; |
| } else { |
| return mAH[AdapterHolder.WORK].recyclerView; |
| } |
| } |
| |
| /** |
| * Resets the state of AllApps. |
| */ |
| public void reset() { |
| for (int i = 0; i < mAH.length; i++) { |
| if (mAH[i].recyclerView != null) { |
| mAH[i].recyclerView.scrollToTop(); |
| } |
| } |
| if (mFloatingHeaderHandler != null) { |
| mFloatingHeaderHandler.reset(); |
| } |
| // Reset the search bar and base recycler view after transitioning home |
| mSearchUiManager.reset(); |
| } |
| |
| @Override |
| protected void onFinishInflate() { |
| super.onFinishInflate(); |
| |
| // 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(new View.OnFocusChangeListener() { |
| @Override |
| public void onFocusChange(View v, boolean hasFocus) { |
| if (hasFocus && getActiveRecyclerView() != null) { |
| getActiveRecyclerView().requestFocus(); |
| } |
| } |
| }); |
| |
| mHeader = findViewById(R.id.all_apps_header); |
| mFloatingHeaderHandler = new FloatingHeaderHandler(mHeader); |
| rebindAdapters(mUsingTabs); |
| |
| mSearchContainer = findViewById(R.id.search_container_all_apps); |
| mSearchUiManager = (SearchUiManager) mSearchContainer; |
| mSearchUiManager.initialize(this); |
| |
| onLauncherLayoutChanged(); |
| } |
| |
| public SearchUiManager getSearchUiManager() { |
| return mSearchUiManager; |
| } |
| |
| @Override |
| protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
| DeviceProfile grid = mLauncher.getDeviceProfile(); |
| // Update the number of items in the grid before we measure the view |
| grid.updateAppsViewNumCols(); |
| |
| if (mNumAppsPerRow != grid.inv.numColumns || |
| mNumPredictedAppsPerRow != grid.inv.numColumns) { |
| mNumAppsPerRow = grid.inv.numColumns; |
| mNumPredictedAppsPerRow = grid.inv.numColumns; |
| for (int i = 0; i < mAH.length; i++) { |
| mAH[i].applyNumsPerRow(); |
| } |
| } |
| super.onMeasure(widthMeasureSpec, heightMeasureSpec); |
| } |
| |
| @Override |
| public boolean dispatchKeyEvent(KeyEvent event) { |
| mSearchUiManager.preDispatchKeyEvent(event); |
| return super.dispatchKeyEvent(event); |
| } |
| |
| @Override |
| public boolean onLongClick(final View v) { |
| // When we have exited all apps or are in transition, disregard long clicks |
| if (!mLauncher.isInState(LauncherState.ALL_APPS) || |
| mLauncher.getWorkspace().isSwitchingState()) return false; |
| // Return if global dragging is not enabled or we are already dragging |
| if (!mLauncher.isDraggingEnabled()) return false; |
| if (mLauncher.getDragController().isDragging()) return false; |
| |
| // Start the drag |
| final DragController dragController = mLauncher.getDragController(); |
| dragController.addDragListener(new DragController.DragListener() { |
| @Override |
| public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { |
| v.setVisibility(INVISIBLE); |
| } |
| |
| @Override |
| public void onDragEnd() { |
| v.setVisibility(VISIBLE); |
| dragController.removeDragListener(this); |
| } |
| }); |
| |
| DeviceProfile grid = mLauncher.getDeviceProfile(); |
| DragOptions options = new DragOptions(); |
| options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx; |
| mLauncher.getWorkspace().beginDragShared(v, this, options); |
| return false; |
| } |
| |
| @Override |
| public void onDropCompleted(View target, DragObject d, boolean success) { } |
| |
| @Override |
| public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) { |
| // This is filled in {@link AllAppsRecyclerView} |
| } |
| |
| @Override |
| public void setInsets(Rect insets) { |
| DeviceProfile grid = mLauncher.getDeviceProfile(); |
| for (int i = 0; i < mAH.length; i++) { |
| mAH[i].padding.bottom = insets.bottom; |
| mAH[i].applyPadding(); |
| } |
| if (grid.isVerticalBarLayout()) { |
| ViewGroup.MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams(); |
| mlp.leftMargin = insets.left; |
| mlp.topMargin = insets.top; |
| mlp.rightMargin = insets.right; |
| setLayoutParams(mlp); |
| } else { |
| View navBarBg = findViewById(R.id.nav_bar_bg); |
| ViewGroup.LayoutParams navBarBgLp = navBarBg.getLayoutParams(); |
| navBarBgLp.height = insets.bottom; |
| navBarBg.setLayoutParams(navBarBgLp); |
| } |
| } |
| |
| public void updateIconBadges(Set<PackageUserKey> updatedBadges) { |
| final PackageUserKey packageUserKey = new PackageUserKey(null, null); |
| for (int j = 0; j < mAH.length; j++) { |
| if (mAH[j].recyclerView != null) { |
| final int n = mAH[j].recyclerView.getChildCount(); |
| for (int i = 0; i < n; i++) { |
| View child = mAH[j].recyclerView.getChildAt(i); |
| if (!(child instanceof BubbleTextView) || !(child.getTag() instanceof ItemInfo)) { |
| continue; |
| } |
| ItemInfo info = (ItemInfo) child.getTag(); |
| if (packageUserKey.updateFromItemInfo(info) && updatedBadges.contains(packageUserKey)) { |
| ((BubbleTextView) child).applyBadgeState(info, true /* animate */); |
| } |
| } |
| } |
| } |
| } |
| |
| public SpringAnimationHandler getSpringAnimationHandler() { |
| return mUsingTabs ? null : mAH[AdapterHolder.MAIN].animationHandler; |
| } |
| |
| private void rebindAdapters(boolean showTabs) { |
| if (showTabs != mUsingTabs) { |
| replaceRVContainer(showTabs); |
| } |
| mUsingTabs = showTabs; |
| |
| if (mUsingTabs) { |
| mAH[AdapterHolder.MAIN].setup(mViewPager.getChildAt(0), mPersonalMatcher); |
| mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher); |
| setupWorkProfileTabs(); |
| setupHeader(); |
| } else { |
| mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null); |
| if (FeatureFlags.ALL_APPS_PREDICTION_ROW_VIEW) { |
| setupHeader(); |
| } else { |
| mFloatingHeaderHandler = null; |
| mHeader.setVisibility(View.GONE); |
| } |
| } |
| |
| applyTouchDelegate(); |
| } |
| |
| private boolean hasWorkProfileApp(List<AppInfo> apps) { |
| if (FeatureFlags.ALL_APPS_TABS_ENABLED) { |
| for (AppInfo app : apps) { |
| if (mWorkMatcher.matches(app, null)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| private void replaceRVContainer(boolean showTabs) { |
| for (int i = 0; i < mAH.length; i++) { |
| if (mAH[i].recyclerView != null) { |
| mAH[i].recyclerView.setLayoutManager(null); |
| } |
| } |
| View oldView = getRecyclerViewContainer(); |
| int index = indexOfChild(oldView); |
| removeView(oldView); |
| int layout = showTabs ? R.layout.all_apps_tabs : R.layout.all_apps_rv_layout; |
| View newView = LayoutInflater.from(getContext()).inflate(layout, this, false); |
| addView(newView, index); |
| mViewPager = showTabs ? (InterceptingViewPager) newView : null; |
| } |
| |
| public View getRecyclerViewContainer() { |
| return mViewPager != null ? mViewPager : findViewById(R.id.apps_list_view); |
| } |
| |
| private void setupWorkProfileTabs() { |
| final SlidingTabStrip tabs = findViewById(R.id.tabs); |
| mViewPager.setAdapter(new TabsPagerAdapter()); |
| mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { |
| |
| boolean mVisible = true; |
| |
| @Override |
| public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { |
| tabs.updateIndicatorPosition(position, positionOffset); |
| if (positionOffset == 0 && !mVisible || positionOffset > 0 && mVisible) { |
| mVisible = positionOffset == 0; |
| for (int i = 0; i < mAH.length; i++) { |
| if (mAH[i].recyclerView != null) { |
| mAH[i].recyclerView.getScrollbar().setAlpha(mVisible ? 1 : 0); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void onPageSelected(int pos) { |
| tabs.updateTabTextColor(pos); |
| mFloatingHeaderHandler.setMainActive(pos == 0); |
| applyTouchDelegate(); |
| if (mAH[pos].recyclerView != null) { |
| mAH[pos].recyclerView.bindFastScrollbar(); |
| } |
| } |
| |
| @Override |
| public void onPageScrollStateChanged(int state) { |
| } |
| }); |
| |
| findViewById(R.id.tab_personal) |
| .setOnClickListener((View view) -> mViewPager.setCurrentItem(0)); |
| findViewById(R.id.tab_work) |
| .setOnClickListener((View view) -> mViewPager.setCurrentItem(1)); |
| } |
| |
| public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) { |
| if (mFloatingHeaderHandler != null) { |
| mFloatingHeaderHandler.getContentView().setPredictedApps(apps); |
| } |
| mAH[AdapterHolder.MAIN].appsList.setPredictedApps(apps); |
| } |
| |
| public AppInfo findApp(ComponentKeyMapper<AppInfo> mapper) { |
| return mapper.getItem(mComponentToAppMap); |
| } |
| |
| public AlphabeticalAppsList getApps() { |
| return mAH[AdapterHolder.MAIN].appsList; |
| } |
| |
| public boolean isUsingTabs() { |
| return mUsingTabs; |
| } |
| |
| public FloatingHeaderHandler getFloatingHeaderHandler() { |
| return mFloatingHeaderHandler; |
| } |
| |
| private void setupHeader() { |
| mHeader.setVisibility(View.VISIBLE); |
| int contentHeight = mLauncher.getDeviceProfile().allAppsCellHeightPx; |
| if (!mUsingTabs) { |
| contentHeight += getResources() |
| .getDimensionPixelSize(R.dimen.all_apps_prediction_row_divider_height); |
| } |
| RecyclerView mainRV = mAH[AdapterHolder.MAIN].recyclerView; |
| RecyclerView workRV = mAH[AdapterHolder.WORK].recyclerView; |
| mFloatingHeaderHandler.setup(mainRV, workRV, contentHeight); |
| mFloatingHeaderHandler.getContentView().setup(mAH[AdapterHolder.MAIN].adapter, |
| mComponentToAppMap, mNumPredictedAppsPerRow); |
| |
| int padding = contentHeight; |
| if (!mUsingTabs) { |
| padding += mHeader.getPaddingTop() + mHeader.getPaddingBottom(); |
| } |
| for (int i = 0; i < mAH.length; i++) { |
| mAH[i].paddingTopForTabs = padding; |
| mAH[i].applyPadding(); |
| } |
| } |
| |
| public void setLastSearchQuery(String query) { |
| for (int i = 0; i < mAH.length; i++) { |
| mAH[i].adapter.setLastSearchQuery(query); |
| } |
| } |
| |
| public void onSearchResultsChanged() { |
| for (int i = 0; i < mAH.length; i++) { |
| if (mAH[i].recyclerView != null) { |
| mAH[i].recyclerView.onSearchResultsChanged(); |
| } |
| } |
| } |
| |
| public void setRecyclerViewPaddingTop(int top) { |
| for (int i = 0; i < mAH.length; i++) { |
| mAH[i].padding.top = top; |
| mAH[i].applyPadding(); |
| } |
| } |
| |
| public void setRecyclerViewSidePadding(int left, int right) { |
| for (int i = 0; i < mAH.length; i++) { |
| mAH[i].padding.left = left; |
| mAH[i].padding.right = right; |
| mAH[i].applyPadding(); |
| } |
| } |
| |
| public void setRecyclerViewVerticalFadingEdgeEnabled(boolean enabled) { |
| for (int i = 0; i < mAH.length; i++) { |
| mAH[i].applyVerticalFadingEdgeEnabled(enabled); |
| } |
| } |
| |
| public void addElevationController(RecyclerView.OnScrollListener scrollListener) { |
| if (!mUsingTabs) { |
| mAH[AdapterHolder.MAIN].recyclerView.addOnScrollListener(scrollListener); |
| } |
| } |
| |
| public List<AppInfo> getPredictedApps() { |
| if (mUsingTabs) { |
| return mFloatingHeaderHandler.getContentView().getPredictedApps(); |
| } else { |
| return mAH[AdapterHolder.MAIN].appsList.getPredictedApps(); |
| } |
| } |
| |
| public class AdapterHolder { |
| public static final int MAIN = 0; |
| public static final int WORK = 1; |
| |
| final AllAppsGridAdapter adapter; |
| final LinearLayoutManager layoutManager; |
| final SpringAnimationHandler animationHandler; |
| final AlphabeticalAppsList appsList; |
| final Rect padding = new Rect(); |
| int paddingTopForTabs; |
| AllAppsRecyclerView recyclerView; |
| boolean verticalFadingEdge; |
| |
| AdapterHolder() { |
| appsList = new AlphabeticalAppsList(mLauncher, mComponentToAppMap); |
| adapter = new AllAppsGridAdapter(mLauncher, appsList, mLauncher, |
| AllAppsContainerView.this, true); |
| appsList.setAdapter(adapter); |
| animationHandler = adapter.getSpringAnimationHandler(); |
| layoutManager = adapter.getLayoutManager(); |
| } |
| |
| void setup(@NonNull View rv, @Nullable ItemInfoMatcher matcher) { |
| appsList.updateItemFilter(matcher); |
| recyclerView = (AllAppsRecyclerView) rv; |
| recyclerView.setApps(appsList, mUsingTabs); |
| recyclerView.setLayoutManager(layoutManager); |
| recyclerView.setAdapter(adapter); |
| recyclerView.setHasFixedSize(true); |
| // No animations will occur when changes occur to the items in this RecyclerView. |
| recyclerView.setItemAnimator(null); |
| if (FeatureFlags.LAUNCHER3_PHYSICS && animationHandler != null) { |
| recyclerView.setSpringAnimationHandler(animationHandler); |
| } |
| FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(recyclerView); |
| recyclerView.addItemDecoration(focusedItemDecorator); |
| recyclerView.preMeasureViews(adapter); |
| adapter.setIconFocusListener(focusedItemDecorator.getFocusListener()); |
| applyVerticalFadingEdgeEnabled(verticalFadingEdge); |
| applyPadding(); |
| applyNumsPerRow(); |
| } |
| |
| void applyPadding() { |
| if (recyclerView != null) { |
| int paddingTop = mUsingTabs || FeatureFlags.ALL_APPS_PREDICTION_ROW_VIEW |
| ? paddingTopForTabs : padding.top; |
| recyclerView.setPadding(padding.left, paddingTop, padding.right, padding.bottom); |
| } |
| if (mFloatingHeaderHandler != null) { |
| mFloatingHeaderHandler.getContentView() |
| .setPadding(padding.left, 0 , padding.right, 0); |
| } |
| } |
| |
| void applyNumsPerRow() { |
| if (mNumAppsPerRow > 0) { |
| if (recyclerView != null) { |
| recyclerView.setNumAppsPerRow(mLauncher.getDeviceProfile(), mNumAppsPerRow); |
| } |
| adapter.setNumAppsPerRow(mNumAppsPerRow); |
| appsList.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow); |
| if (mFloatingHeaderHandler != null) { |
| mFloatingHeaderHandler.getContentView() |
| .setNumAppsPerRow(mNumPredictedAppsPerRow); |
| } |
| } |
| } |
| |
| public void applyVerticalFadingEdgeEnabled(boolean enabled) { |
| verticalFadingEdge = enabled; |
| mAH[AdapterHolder.MAIN].recyclerView.setVerticalFadingEdgeEnabled(!mUsingTabs |
| && verticalFadingEdge); |
| } |
| } |
| |
| private class TabsPagerAdapter extends PagerAdapter { |
| @Override |
| public int getCount() { |
| return 2; |
| } |
| |
| @Override |
| public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { |
| return view == object; |
| } |
| |
| @NonNull |
| @Override |
| public Object instantiateItem(@NonNull ViewGroup container, int position) { |
| if (position == 0) { |
| return mAH[AdapterHolder.MAIN].recyclerView; |
| } else { |
| return mAH[AdapterHolder.WORK].recyclerView; |
| } |
| } |
| |
| @Nullable |
| @Override |
| public CharSequence getPageTitle(int position) { |
| if (position == 0) { |
| return getResources().getString(R.string.all_apps_personal_tab); |
| } else { |
| return getResources().getString(R.string.all_apps_work_tab); |
| } |
| } |
| } |
| |
| } |