1 /* 2 * Copyright (C) 2017 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.settings.widget; 18 19 import android.app.ActionBar; 20 import android.app.Activity; 21 import android.support.annotation.VisibleForTesting; 22 import android.support.v7.widget.RecyclerView; 23 import android.view.View; 24 25 import com.android.settingslib.core.lifecycle.Lifecycle; 26 import com.android.settingslib.core.lifecycle.LifecycleObserver; 27 import com.android.settingslib.core.lifecycle.events.OnStart; 28 import com.android.settingslib.core.lifecycle.events.OnStop; 29 30 /** 31 * A controller that adds shadow to actionbar when content view scrolls. 32 * <p/> 33 * It also works on custom views acting as an actionbar. 34 */ 35 public class ActionBarShadowController implements LifecycleObserver, OnStart, OnStop { 36 37 @VisibleForTesting 38 static final float ELEVATION_HIGH = 8; 39 @VisibleForTesting 40 static final float ELEVATION_LOW = 0; 41 42 @VisibleForTesting 43 ScrollChangeWatcher mScrollChangeWatcher; 44 private RecyclerView mRecyclerView; 45 private boolean isScrollWatcherAttached; 46 attachToRecyclerView(Activity activity, Lifecycle lifecycle, RecyclerView recyclerView)47 public static ActionBarShadowController attachToRecyclerView(Activity activity, 48 Lifecycle lifecycle, RecyclerView recyclerView) { 49 return new ActionBarShadowController(activity, lifecycle, recyclerView); 50 } 51 attachToRecyclerView(View anchorView, Lifecycle lifecycle, RecyclerView recyclerView)52 public static ActionBarShadowController attachToRecyclerView(View anchorView, 53 Lifecycle lifecycle, RecyclerView recyclerView) { 54 return new ActionBarShadowController(anchorView, lifecycle, recyclerView); 55 } 56 ActionBarShadowController(Activity activity, Lifecycle lifecycle, RecyclerView recyclerView)57 private ActionBarShadowController(Activity activity, Lifecycle lifecycle, 58 RecyclerView recyclerView) { 59 mScrollChangeWatcher = new ScrollChangeWatcher(activity); 60 mRecyclerView = recyclerView; 61 attachScrollWatcher(); 62 lifecycle.addObserver(this); 63 } 64 ActionBarShadowController(View anchorView, Lifecycle lifecycle, RecyclerView recyclerView)65 private ActionBarShadowController(View anchorView, Lifecycle lifecycle, 66 RecyclerView recyclerView) { 67 mScrollChangeWatcher = new ScrollChangeWatcher(anchorView); 68 mRecyclerView = recyclerView; 69 attachScrollWatcher(); 70 lifecycle.addObserver(this); 71 } 72 73 @Override onStop()74 public void onStop() { 75 detachScrollWatcher(); 76 } 77 detachScrollWatcher()78 private void detachScrollWatcher() { 79 mRecyclerView.removeOnScrollListener(mScrollChangeWatcher); 80 isScrollWatcherAttached = false; 81 } 82 83 @Override onStart()84 public void onStart() { 85 attachScrollWatcher(); 86 } 87 attachScrollWatcher()88 private void attachScrollWatcher() { 89 if (!isScrollWatcherAttached) { 90 isScrollWatcherAttached = true; 91 mRecyclerView.addOnScrollListener(mScrollChangeWatcher); 92 mScrollChangeWatcher.updateDropShadow(mRecyclerView); 93 } 94 } 95 96 /** 97 * Update the drop shadow as the scrollable entity is scrolled. 98 */ 99 final class ScrollChangeWatcher extends RecyclerView.OnScrollListener { 100 101 private final Activity mActivity; 102 private final View mAnchorView; 103 ScrollChangeWatcher(Activity activity)104 public ScrollChangeWatcher(Activity activity) { 105 mActivity = activity; 106 mAnchorView = null; 107 } 108 ScrollChangeWatcher(View anchorView)109 public ScrollChangeWatcher(View anchorView) { 110 mAnchorView = anchorView; 111 mActivity = null; 112 } 113 114 // RecyclerView scrolled. 115 @Override onScrolled(RecyclerView view, int dx, int dy)116 public void onScrolled(RecyclerView view, int dx, int dy) { 117 updateDropShadow(view); 118 } 119 updateDropShadow(View view)120 public void updateDropShadow(View view) { 121 final boolean shouldShowShadow = view.canScrollVertically(-1); 122 if (mAnchorView != null) { 123 mAnchorView.setElevation(shouldShowShadow ? ELEVATION_HIGH : ELEVATION_LOW); 124 } else if (mActivity != null) { // activity can become null when running monkey 125 final ActionBar actionBar = mActivity.getActionBar(); 126 if (actionBar != null) { 127 actionBar.setElevation(shouldShowShadow ? ELEVATION_HIGH : ELEVATION_LOW); 128 } 129 } 130 } 131 } 132 133 } 134