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