/* * 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.widget; import android.content.Context; import android.graphics.Point; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import com.android.launcher3.BaseRecyclerView; import com.android.launcher3.R; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener; /** * The widgets recycler view. */ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouchListener { private WidgetsListAdapter mAdapter; private final int mScrollbarTop; private final Point mFastScrollerOffset = new Point(); private boolean mTouchDownOnScroller; public WidgetsRecyclerView(Context context) { this(context, null); } public WidgetsRecyclerView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { // API 21 and below only support 3 parameter ctor. super(context, attrs, defStyleAttr); mScrollbarTop = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin); addOnItemTouchListener(this); } @Override protected void onFinishInflate() { super.onFinishInflate(); // create a layout manager with Launcher's context so that scroll position // can be preserved during screen rotation. setLayoutManager(new LinearLayoutManager(getContext())); } @Override public void setAdapter(Adapter adapter) { super.setAdapter(adapter); mAdapter = (WidgetsListAdapter) adapter; } /** * Maps the touch (from 0..1) to the adapter position that should be visible. */ @Override public String scrollToPositionAtProgress(float touchFraction) { // Skip early if widgets are not bound. if (isModelNotReady()) { return ""; } // Stop the scroller if it is scrolling stopScroll(); int rowCount = mAdapter.getItemCount(); float pos = rowCount * touchFraction; int availableScrollHeight = getAvailableScrollHeight(); LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager()); layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction)); int posInt = (int) ((touchFraction == 1)? pos -1 : pos); return mAdapter.getSectionName(posInt); } /** * Updates the bounds for the scrollbar. */ @Override public void onUpdateScrollbar(int dy) { // Skip early if widgets are not bound. if (isModelNotReady()) { return; } // Skip early if, there no child laid out in the container. int scrollY = getCurrentScrollY(); if (scrollY < 0) { mScrollbar.setThumbOffsetY(-1); return; } synchronizeScrollBarThumbOffsetToViewScroll(scrollY, getAvailableScrollHeight()); } @Override public int getCurrentScrollY() { // Skip early if widgets are not bound. if (isModelNotReady() || getChildCount() == 0) { return -1; } View child = getChildAt(0); int rowIndex = getChildPosition(child); int y = (child.getMeasuredHeight() * rowIndex); int offset = getLayoutManager().getDecoratedTop(child); return getPaddingTop() + y - offset; } /** * Returns the available scroll height: * AvailableScrollHeight = Total height of the all items - last page height */ @Override protected int getAvailableScrollHeight() { View child = getChildAt(0); return child.getMeasuredHeight() * mAdapter.getItemCount() - getScrollbarTrackHeight() - mScrollbarTop; } private boolean isModelNotReady() { return mAdapter.getItemCount() == 0; } @Override public int getScrollBarTop() { return mScrollbarTop; } @Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { if (e.getAction() == MotionEvent.ACTION_DOWN) { mTouchDownOnScroller = mScrollbar.isHitInParent(e.getX(), e.getY(), mFastScrollerOffset); } if (mTouchDownOnScroller) { return mScrollbar.handleTouchEvent(e, mFastScrollerOffset); } return false; } @Override public void onTouchEvent(RecyclerView rv, MotionEvent e) { if (mTouchDownOnScroller) { mScrollbar.handleTouchEvent(e, mFastScrollerOffset); } } @Override public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { } }