1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher3.widget; 18 19 import android.content.Context; 20 import android.graphics.Canvas; 21 import android.graphics.Color; 22 import android.support.v7.widget.LinearLayoutManager; 23 import android.util.AttributeSet; 24 import android.view.View; 25 import com.android.launcher3.BaseRecyclerView; 26 import com.android.launcher3.R; 27 import com.android.launcher3.model.PackageItemInfo; 28 import com.android.launcher3.model.WidgetsModel; 29 30 /** 31 * The widgets recycler view. 32 */ 33 public class WidgetsRecyclerView extends BaseRecyclerView { 34 35 private static final String TAG = "WidgetsRecyclerView"; 36 private WidgetsModel mWidgets; 37 private ScrollPositionState mScrollPosState = new ScrollPositionState(); 38 39 public WidgetsRecyclerView(Context context) { 40 this(context, null); 41 } 42 43 public WidgetsRecyclerView(Context context, AttributeSet attrs) { 44 this(context, attrs, 0); 45 } 46 47 public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { 48 // API 21 and below only support 3 parameter ctor. 49 super(context, attrs, defStyleAttr); 50 } 51 52 public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr, 53 int defStyleRes) { 54 this(context, attrs, defStyleAttr); 55 } 56 57 @Override 58 protected void onFinishInflate() { 59 super.onFinishInflate(); 60 addOnItemTouchListener(this); 61 } 62 63 public int getFastScrollerTrackColor(int defaultTrackColor) { 64 return Color.WHITE; 65 } 66 67 /** 68 * Sets the widget model in this view, used to determine the fast scroll position. 69 */ 70 public void setWidgets(WidgetsModel widgets) { 71 mWidgets = widgets; 72 } 73 74 /** 75 * We need to override the draw to ensure that we don't draw the overscroll effect beyond the 76 * background bounds. 77 */ 78 @Override 79 protected void dispatchDraw(Canvas canvas) { 80 canvas.clipRect(mBackgroundPadding.left, mBackgroundPadding.top, 81 getWidth() - mBackgroundPadding.right, 82 getHeight() - mBackgroundPadding.bottom); 83 super.dispatchDraw(canvas); 84 } 85 86 /** 87 * Maps the touch (from 0..1) to the adapter position that should be visible. 88 */ 89 @Override 90 public String scrollToPositionAtProgress(float touchFraction) { 91 // Skip early if widgets are not bound. 92 if (mWidgets == null) { 93 return ""; 94 } 95 96 // Skip early if there are no widgets. 97 int rowCount = mWidgets.getPackageSize(); 98 if (rowCount == 0) { 99 return ""; 100 } 101 102 // Stop the scroller if it is scrolling 103 stopScroll(); 104 105 getCurScrollState(mScrollPosState, -1); 106 float pos = rowCount * touchFraction; 107 int availableScrollHeight = getAvailableScrollHeight(rowCount); 108 LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager()); 109 layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction)); 110 111 int posInt = (int) ((touchFraction == 1)? pos -1 : pos); 112 PackageItemInfo p = mWidgets.getPackageItemInfo(posInt); 113 return p.titleSectionName; 114 } 115 116 /** 117 * Updates the bounds for the scrollbar. 118 */ 119 @Override 120 public void onUpdateScrollbar(int dy) { 121 // Skip early if widgets are not bound. 122 if (mWidgets == null) { 123 return; 124 } 125 126 // Skip early if there are no widgets. 127 int rowCount = mWidgets.getPackageSize(); 128 if (rowCount == 0) { 129 mScrollbar.setThumbOffset(-1, -1); 130 return; 131 } 132 133 // Skip early if, there no child laid out in the container. 134 getCurScrollState(mScrollPosState, -1); 135 if (mScrollPosState.rowIndex < 0) { 136 mScrollbar.setThumbOffset(-1, -1); 137 return; 138 } 139 140 synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount); 141 } 142 143 /** 144 * Returns the current scroll state. 145 */ 146 protected void getCurScrollState(ScrollPositionState stateOut, int viewTypeMask) { 147 stateOut.rowIndex = -1; 148 stateOut.rowTopOffset = -1; 149 stateOut.itemPos = -1; 150 151 // Skip early if widgets are not bound. 152 if (mWidgets == null) { 153 return; 154 } 155 156 // Return early if there are no items 157 int rowCount = mWidgets.getPackageSize(); 158 if (rowCount == 0) { 159 return; 160 } 161 View child = getChildAt(0); 162 int position = getChildPosition(child); 163 164 stateOut.rowIndex = position; 165 stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child); 166 stateOut.itemPos = position; 167 } 168 169 @Override 170 protected int getTop(int rowIndex) { 171 if (getChildCount() == 0) { 172 return 0; 173 } 174 175 // All the rows are the same height, return any child height 176 View child = getChildAt(0); 177 return child.getMeasuredHeight() * rowIndex; 178 } 179 }