1841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang/*
2841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang * Copyright (C) 2017 The Android Open Source Project
3841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang *
4841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang * Licensed under the Apache License, Version 2.0 (the "License");
5841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang * you may not use this file except in compliance with the License.
6841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang * You may obtain a copy of the License at
7841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang *
8841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang *      http://www.apache.org/licenses/LICENSE-2.0
9841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang *
10841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang * Unless required by applicable law or agreed to in writing, software
11841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang * distributed under the License is distributed on an "AS IS" BASIS,
12841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang * See the License for the specific language governing permissions and
14841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang * limitations under the License.
15841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang */
16841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang
17841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhangpackage com.android.settings.widget;
18841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang
19841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhangimport android.app.ActionBar;
20841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhangimport android.app.Activity;
21e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhangimport android.support.annotation.VisibleForTesting;
22841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhangimport android.support.v7.widget.RecyclerView;
23841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhangimport android.view.View;
24841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang
25841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhangimport com.android.settingslib.core.lifecycle.Lifecycle;
26841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhangimport com.android.settingslib.core.lifecycle.LifecycleObserver;
27841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhangimport com.android.settingslib.core.lifecycle.events.OnStart;
28841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhangimport com.android.settingslib.core.lifecycle.events.OnStop;
29841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang
30e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang/**
31e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang * A controller that adds shadow to actionbar when content view scrolls.
32e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang * <p/>
33e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang * It also works on custom views acting as an actionbar.
34e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang */
35841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhangpublic class ActionBarShadowController implements LifecycleObserver, OnStart, OnStop {
36841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang
37e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang    @VisibleForTesting
38e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang    static final float ELEVATION_HIGH = 8;
39e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang    @VisibleForTesting
40e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang    static final float ELEVATION_LOW = 0;
41e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang
42e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang    @VisibleForTesting
43e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang    ScrollChangeWatcher mScrollChangeWatcher;
44841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    private RecyclerView mRecyclerView;
45841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    private boolean isScrollWatcherAttached;
46841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang
47841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    public static ActionBarShadowController attachToRecyclerView(Activity activity,
48841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang            Lifecycle lifecycle, RecyclerView recyclerView) {
49841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        return new ActionBarShadowController(activity, lifecycle, recyclerView);
50841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    }
51841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang
52e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang    public static ActionBarShadowController attachToRecyclerView(View anchorView,
53e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang            Lifecycle lifecycle, RecyclerView recyclerView) {
54e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang        return new ActionBarShadowController(anchorView, lifecycle, recyclerView);
55e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang    }
56e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang
57841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    private ActionBarShadowController(Activity activity, Lifecycle lifecycle,
58841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang            RecyclerView recyclerView) {
59841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        mScrollChangeWatcher = new ScrollChangeWatcher(activity);
60841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        mRecyclerView = recyclerView;
61841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        attachScrollWatcher();
62841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        lifecycle.addObserver(this);
63841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    }
64841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang
65e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang    private ActionBarShadowController(View anchorView, Lifecycle lifecycle,
66e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang            RecyclerView recyclerView) {
67e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang        mScrollChangeWatcher = new ScrollChangeWatcher(anchorView);
68e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang        mRecyclerView = recyclerView;
69e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang        attachScrollWatcher();
70e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang        lifecycle.addObserver(this);
71e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang    }
72e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang
73841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    @Override
74841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    public void onStop() {
75841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        detachScrollWatcher();
76841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    }
77841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang
78841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    private void detachScrollWatcher() {
79841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        mRecyclerView.removeOnScrollListener(mScrollChangeWatcher);
80841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        isScrollWatcherAttached = false;
81841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    }
82841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang
83841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    @Override
84841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    public void onStart() {
85841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        attachScrollWatcher();
86841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    }
87841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang
88841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    private void attachScrollWatcher() {
89841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        if (!isScrollWatcherAttached) {
90841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang            isScrollWatcherAttached = true;
91841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang            mRecyclerView.addOnScrollListener(mScrollChangeWatcher);
92841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang            mScrollChangeWatcher.updateDropShadow(mRecyclerView);
93841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        }
94841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    }
95841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang
96841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    /**
97841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang     * Update the drop shadow as the scrollable entity is scrolled.
98841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang     */
99e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang    final class ScrollChangeWatcher extends RecyclerView.OnScrollListener {
100841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang
101e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang        private final Activity mActivity;
102e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang        private final View mAnchorView;
103841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang
104841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        public ScrollChangeWatcher(Activity activity) {
105841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang            mActivity = activity;
106e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang            mAnchorView = null;
107e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang        }
108e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang
109e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang        public ScrollChangeWatcher(View anchorView) {
110e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang            mAnchorView = anchorView;
111e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang            mActivity = null;
112841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        }
113841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang
114841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        // RecyclerView scrolled.
115841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        @Override
116841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        public void onScrolled(RecyclerView view, int dx, int dy) {
117841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang            updateDropShadow(view);
118841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        }
119841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang
120841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        public void updateDropShadow(View view) {
121841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang            final boolean shouldShowShadow = view.canScrollVertically(-1);
122e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang            if (mAnchorView != null) {
123e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang                mAnchorView.setElevation(shouldShowShadow ? ELEVATION_HIGH : ELEVATION_LOW);
124d53a3128e8107569636da407d7cb75e458e22779Doris Ling            } else if (mActivity != null) { // activity can become null when running monkey
125e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang                final ActionBar actionBar = mActivity.getActionBar();
126e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang                if (actionBar != null) {
127e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang                    actionBar.setElevation(shouldShowShadow ? ELEVATION_HIGH : ELEVATION_LOW);
128e651ddf9e6c00a62338ef8c7114b5e5e9cd307ecFan Zhang                }
129841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang            }
130841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang        }
131841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang    }
132841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang
133841d1d5aabc33024b2e03276085b0d1dbdb92b23Fan Zhang}
134