1ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song/* 2ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song * Copyright (C) 2015 The Android Open Source Project 3ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song * 4ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song * Licensed under the Apache License, Version 2.0 (the "License"); 5ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song * you may not use this file except in compliance with the License. 6ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song * You may obtain a copy of the License at 7ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song * 8ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song * http://www.apache.org/licenses/LICENSE-2.0 9ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song * 10ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song * Unless required by applicable law or agreed to in writing, software 11ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song * distributed under the License is distributed on an "AS IS" BASIS, 12ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song * See the License for the specific language governing permissions and 14ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song * limitations under the License. 15ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song */ 16ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song 17ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Songpackage com.android.launcher3.widget; 18ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song 19ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Songimport android.content.Context; 20b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chungimport android.graphics.Color; 21ec84728b270004494dc483c141628ec6417175fbHyunyoung Songimport android.support.v7.widget.LinearLayoutManager; 22ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Songimport android.util.AttributeSet; 23ec84728b270004494dc483c141628ec6417175fbHyunyoung Songimport android.view.View; 24c06af333cbcebb183d13d004cccf5c9d69a70af0Mario Bertschler 255f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chungimport com.android.launcher3.BaseRecyclerView; 2604ac4faab0336e30f0879311432a2377e2631df1Adam Cohen 27ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song/** 285f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung * The widgets recycler view. 29ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song */ 305f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chungpublic class WidgetsRecyclerView extends BaseRecyclerView { 31ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song 32ec84728b270004494dc483c141628ec6417175fbHyunyoung Song private static final String TAG = "WidgetsRecyclerView"; 33d164b7f4abcba6cc965c2264257569f88ad5e4a5Sunny Goyal private WidgetsListAdapter mAdapter; 34ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song 355f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung public WidgetsRecyclerView(Context context) { 36ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song this(context, null); 37ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song } 38ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song 395f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung public WidgetsRecyclerView(Context context, AttributeSet attrs) { 40ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song this(context, attrs, 0); 41ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song } 42ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song 435f4e0fdd2e4edeb9211e2dcd1c99497f175731f8Winson Chung public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { 449e6344212a53cd1ae997cd85da5751585a9de504Hyunyoung Song // API 21 and below only support 3 parameter ctor. 45ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song super(context, attrs, defStyleAttr); 46ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song } 47ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song 489e6344212a53cd1ae997cd85da5751585a9de504Hyunyoung Song public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr, 499e6344212a53cd1ae997cd85da5751585a9de504Hyunyoung Song int defStyleRes) { 509e6344212a53cd1ae997cd85da5751585a9de504Hyunyoung Song this(context, attrs, defStyleAttr); 519e6344212a53cd1ae997cd85da5751585a9de504Hyunyoung Song } 529e6344212a53cd1ae997cd85da5751585a9de504Hyunyoung Song 53ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song @Override 54ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song protected void onFinishInflate() { 55ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song super.onFinishInflate(); 56ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song addOnItemTouchListener(this); 57bc5d623845e8e6e50254064c054c344a7246330bHyunyoung Song // create a layout manager with Launcher's context so that scroll position 58bc5d623845e8e6e50254064c054c344a7246330bHyunyoung Song // can be preserved during screen rotation. 59bc5d623845e8e6e50254064c054c344a7246330bHyunyoung Song setLayoutManager(new LinearLayoutManager(getContext())); 60ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 61ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song 62d164b7f4abcba6cc965c2264257569f88ad5e4a5Sunny Goyal @Override 63d164b7f4abcba6cc965c2264257569f88ad5e4a5Sunny Goyal public void setAdapter(Adapter adapter) { 64d164b7f4abcba6cc965c2264257569f88ad5e4a5Sunny Goyal super.setAdapter(adapter); 65d164b7f4abcba6cc965c2264257569f88ad5e4a5Sunny Goyal mAdapter = (WidgetsListAdapter) adapter; 66ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 67ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song 68ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song /** 69ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song * Maps the touch (from 0..1) to the adapter position that should be visible. 70ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song */ 71ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song @Override 72ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song public String scrollToPositionAtProgress(float touchFraction) { 7323c8f48f4334b0ac0752b320dad2c1fc5ef095f3Winson // Skip early if widgets are not bound. 7481259cd086f8d7a0451f32d6e09cbda26f85ee38Sunny Goyal if (isModelNotReady()) { 75b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung return ""; 76b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung } 77ec84728b270004494dc483c141628ec6417175fbHyunyoung Song 78b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung // Stop the scroller if it is scrolling 79b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung stopScroll(); 80b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung 81d164b7f4abcba6cc965c2264257569f88ad5e4a5Sunny Goyal int rowCount = mAdapter.getItemCount(); 82b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung float pos = rowCount * touchFraction; 83b655b8850064cc4f8e59652b4e3bffc09090769aWinson int availableScrollHeight = getAvailableScrollHeight(); 84ec84728b270004494dc483c141628ec6417175fbHyunyoung Song LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager()); 85b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction)); 86ec84728b270004494dc483c141628ec6417175fbHyunyoung Song 87b1777447d9b9700b48f8060f8b318f2363c43e8dWinson Chung int posInt = (int) ((touchFraction == 1)? pos -1 : pos); 88d164b7f4abcba6cc965c2264257569f88ad5e4a5Sunny Goyal return mAdapter.getSectionName(posInt); 89ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 90ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song 91ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song /** 92ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song * Updates the bounds for the scrollbar. 93ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song */ 94ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song @Override 95d2eb49e4c3bb37d35e72c36d8a308262b690075fWinson public void onUpdateScrollbar(int dy) { 9623c8f48f4334b0ac0752b320dad2c1fc5ef095f3Winson // Skip early if widgets are not bound. 9781259cd086f8d7a0451f32d6e09cbda26f85ee38Sunny Goyal if (isModelNotReady()) { 98ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song return; 99ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 100ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song 101ec84728b270004494dc483c141628ec6417175fbHyunyoung Song // Skip early if, there no child laid out in the container. 102b655b8850064cc4f8e59652b4e3bffc09090769aWinson int scrollY = getCurrentScrollY(); 103b655b8850064cc4f8e59652b4e3bffc09090769aWinson if (scrollY < 0) { 1045d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88Sunny Goyal mScrollbar.setThumbOffsetY(-1); 105ec84728b270004494dc483c141628ec6417175fbHyunyoung Song return; 106ec84728b270004494dc483c141628ec6417175fbHyunyoung Song } 107ec84728b270004494dc483c141628ec6417175fbHyunyoung Song 108b655b8850064cc4f8e59652b4e3bffc09090769aWinson synchronizeScrollBarThumbOffsetToViewScroll(scrollY, getAvailableScrollHeight()); 109ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 110ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song 111b655b8850064cc4f8e59652b4e3bffc09090769aWinson @Override 1122e22b5d2de066245f8c8f52343bb2a8b4e8eb563Peter Schiller public int getCurrentScrollY() { 11323c8f48f4334b0ac0752b320dad2c1fc5ef095f3Winson // Skip early if widgets are not bound. 114b655b8850064cc4f8e59652b4e3bffc09090769aWinson if (isModelNotReady() || getChildCount() == 0) { 115b655b8850064cc4f8e59652b4e3bffc09090769aWinson return -1; 11623c8f48f4334b0ac0752b320dad2c1fc5ef095f3Winson } 117ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song 118ec84728b270004494dc483c141628ec6417175fbHyunyoung Song View child = getChildAt(0); 119b655b8850064cc4f8e59652b4e3bffc09090769aWinson int rowIndex = getChildPosition(child); 120b655b8850064cc4f8e59652b4e3bffc09090769aWinson int y = (child.getMeasuredHeight() * rowIndex); 121b655b8850064cc4f8e59652b4e3bffc09090769aWinson int offset = getLayoutManager().getDecoratedTop(child); 122ec84728b270004494dc483c141628ec6417175fbHyunyoung Song 123b655b8850064cc4f8e59652b4e3bffc09090769aWinson return getPaddingTop() + y - offset; 124c088049113c261331b5685e64050d14a31cd72dfWinson } 125c088049113c261331b5685e64050d14a31cd72dfWinson 126b655b8850064cc4f8e59652b4e3bffc09090769aWinson /** 127b655b8850064cc4f8e59652b4e3bffc09090769aWinson * Returns the available scroll height: 128b655b8850064cc4f8e59652b4e3bffc09090769aWinson * AvailableScrollHeight = Total height of the all items - last page height 129b655b8850064cc4f8e59652b4e3bffc09090769aWinson */ 130c088049113c261331b5685e64050d14a31cd72dfWinson @Override 131b655b8850064cc4f8e59652b4e3bffc09090769aWinson protected int getAvailableScrollHeight() { 132c088049113c261331b5685e64050d14a31cd72dfWinson View child = getChildAt(0); 133d164b7f4abcba6cc965c2264257569f88ad5e4a5Sunny Goyal int height = child.getMeasuredHeight() * mAdapter.getItemCount(); 134b655b8850064cc4f8e59652b4e3bffc09090769aWinson int totalHeight = getPaddingTop() + height + getPaddingBottom(); 13500e106880d33d2fc173c38e6d9da98762ccee478Sunny Goyal int availableScrollHeight = totalHeight - getScrollbarTrackHeight(); 136b655b8850064cc4f8e59652b4e3bffc09090769aWinson return availableScrollHeight; 137ac5f6af1488ec1cf0b73aa0848a675764c2f652bHyunyoung Song } 13881259cd086f8d7a0451f32d6e09cbda26f85ee38Sunny Goyal 13981259cd086f8d7a0451f32d6e09cbda26f85ee38Sunny Goyal private boolean isModelNotReady() { 140d164b7f4abcba6cc965c2264257569f88ad5e4a5Sunny Goyal return mAdapter.getItemCount() == 0; 14181259cd086f8d7a0451f32d6e09cbda26f85ee38Sunny Goyal } 142ebe1734a67dff9ab46f3c6cce328a86b714ce620Hyunyoung Song}