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}