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