| /* |
| * 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) { } |
| } |