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.model.PackageItemInfo; 27 import com.android.launcher3.model.WidgetsModel; 28 29 /** 30 * The widgets recycler view. 31 */ 32 public class WidgetsRecyclerView extends BaseRecyclerView { 33 34 private static final String TAG = "WidgetsRecyclerView"; 35 private WidgetsModel mWidgets; 36 37 public WidgetsRecyclerView(Context context) { 38 this(context, null); 39 } 40 41 public WidgetsRecyclerView(Context context, AttributeSet attrs) { 42 this(context, attrs, 0); 43 } 44 45 public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { 46 // API 21 and below only support 3 parameter ctor. 47 super(context, attrs, defStyleAttr); 48 } 49 50 public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr, 51 int defStyleRes) { 52 this(context, attrs, defStyleAttr); 53 } 54 55 @Override 56 protected void onFinishInflate() { 57 super.onFinishInflate(); 58 addOnItemTouchListener(this); 59 // create a layout manager with Launcher's context so that scroll position 60 // can be preserved during screen rotation. 61 setLayoutManager(new LinearLayoutManager(getContext())); 62 } 63 64 public int getFastScrollerTrackColor(int defaultTrackColor) { 65 return Color.WHITE; 66 } 67 68 /** 69 * Sets the widget model in this view, used to determine the fast scroll position. 70 */ 71 public void setWidgets(WidgetsModel widgets) { 72 mWidgets = widgets; 73 } 74 75 /** 76 * We need to override the draw to ensure that we don't draw the overscroll effect beyond the 77 * background bounds. 78 */ 79 @Override 80 protected void dispatchDraw(Canvas canvas) { 81 canvas.clipRect(mBackgroundPadding.left, mBackgroundPadding.top, 82 getWidth() - mBackgroundPadding.right, 83 getHeight() - mBackgroundPadding.bottom); 84 super.dispatchDraw(canvas); 85 } 86 87 /** 88 * Maps the touch (from 0..1) to the adapter position that should be visible. 89 */ 90 @Override 91 public String scrollToPositionAtProgress(float touchFraction) { 92 // Skip early if widgets are not bound. 93 if (isModelNotReady()) { 94 return ""; 95 } 96 97 // Stop the scroller if it is scrolling 98 stopScroll(); 99 100 int rowCount = mWidgets.getPackageSize(); 101 float pos = rowCount * touchFraction; 102 int availableScrollHeight = getAvailableScrollHeight(); 103 LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager()); 104 layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction)); 105 106 int posInt = (int) ((touchFraction == 1)? pos -1 : pos); 107 PackageItemInfo p = mWidgets.getPackageItemInfo(posInt); 108 return p.titleSectionName; 109 } 110 111 /** 112 * Updates the bounds for the scrollbar. 113 */ 114 @Override 115 public void onUpdateScrollbar(int dy) { 116 // Skip early if widgets are not bound. 117 if (isModelNotReady()) { 118 return; 119 } 120 121 // Skip early if, there no child laid out in the container. 122 int scrollY = getCurrentScrollY(); 123 if (scrollY < 0) { 124 mScrollbar.setThumbOffset(-1, -1); 125 return; 126 } 127 128 synchronizeScrollBarThumbOffsetToViewScroll(scrollY, getAvailableScrollHeight()); 129 } 130 131 @Override 132 public int getCurrentScrollY() { 133 // Skip early if widgets are not bound. 134 if (isModelNotReady() || getChildCount() == 0) { 135 return -1; 136 } 137 138 View child = getChildAt(0); 139 int rowIndex = getChildPosition(child); 140 int y = (child.getMeasuredHeight() * rowIndex); 141 int offset = getLayoutManager().getDecoratedTop(child); 142 143 return getPaddingTop() + y - offset; 144 } 145 146 /** 147 * Returns the available scroll height: 148 * AvailableScrollHeight = Total height of the all items - last page height 149 */ 150 @Override 151 protected int getAvailableScrollHeight() { 152 View child = getChildAt(0); 153 int height = child.getMeasuredHeight() * mWidgets.getPackageSize(); 154 int totalHeight = getPaddingTop() + height + getPaddingBottom(); 155 int availableScrollHeight = totalHeight - getVisibleHeight(); 156 return availableScrollHeight; 157 } 158 159 private boolean isModelNotReady() { 160 return mWidgets == null || mWidgets.getPackageSize() == 0; 161 } 162 }