1 /* 2 * Copyright (C) 2019 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.settingslib.widget; 18 19 import static androidx.lifecycle.Lifecycle.Event.ON_START; 20 import static androidx.lifecycle.Lifecycle.Event.ON_STOP; 21 22 import android.app.ActionBar; 23 import android.app.Activity; 24 import android.view.View; 25 26 import androidx.annotation.VisibleForTesting; 27 import androidx.lifecycle.Lifecycle; 28 import androidx.lifecycle.LifecycleObserver; 29 import androidx.lifecycle.OnLifecycleEvent; 30 31 /** 32 * UI controller that adds a shadow appear/disappear animation to action bar scroll. 33 */ 34 public class ActionBarShadowController implements LifecycleObserver { 35 36 @VisibleForTesting 37 static final float ELEVATION_HIGH = 8; 38 @VisibleForTesting 39 static final float ELEVATION_LOW = 0; 40 41 @VisibleForTesting 42 ScrollChangeWatcher mScrollChangeWatcher; 43 private View mScrollView; 44 private boolean mIsScrollWatcherAttached; 45 46 /** 47 * Wire up the animation to to an {@link Activity}. Shadow will be applied to activity's 48 * action bar. 49 */ attachToView( Activity activity, Lifecycle lifecycle, View scrollView)50 public static ActionBarShadowController attachToView( 51 Activity activity, Lifecycle lifecycle, View scrollView) { 52 return new ActionBarShadowController(activity, lifecycle, scrollView); 53 } 54 55 /** 56 * Wire up the animation to to a {@link View}. Shadow will be applied to the view. 57 */ attachToView( View anchorView, Lifecycle lifecycle, View scrollView)58 public static ActionBarShadowController attachToView( 59 View anchorView, Lifecycle lifecycle, View scrollView) { 60 return new ActionBarShadowController(anchorView, lifecycle, scrollView); 61 } 62 ActionBarShadowController(Activity activity, Lifecycle lifecycle, View scrollView)63 private ActionBarShadowController(Activity activity, Lifecycle lifecycle, View scrollView) { 64 mScrollChangeWatcher = new ActionBarShadowController.ScrollChangeWatcher(activity); 65 mScrollView = scrollView; 66 attachScrollWatcher(); 67 lifecycle.addObserver(this); 68 } 69 ActionBarShadowController(View anchorView, Lifecycle lifecycle, View scrollView)70 private ActionBarShadowController(View anchorView, Lifecycle lifecycle, View scrollView) { 71 mScrollChangeWatcher = new ActionBarShadowController.ScrollChangeWatcher(anchorView); 72 mScrollView = scrollView; 73 attachScrollWatcher(); 74 lifecycle.addObserver(this); 75 } 76 77 @OnLifecycleEvent(ON_START) attachScrollWatcher()78 private void attachScrollWatcher() { 79 if (!mIsScrollWatcherAttached) { 80 mIsScrollWatcherAttached = true; 81 mScrollView.setOnScrollChangeListener(mScrollChangeWatcher); 82 mScrollChangeWatcher.updateDropShadow(mScrollView); 83 } 84 } 85 86 @OnLifecycleEvent(ON_STOP) detachScrollWatcher()87 private void detachScrollWatcher() { 88 mScrollView.setOnScrollChangeListener(null); 89 mIsScrollWatcherAttached = false; 90 } 91 92 /** 93 * Update the drop shadow as the scrollable entity is scrolled. 94 */ 95 final class ScrollChangeWatcher implements View.OnScrollChangeListener { 96 97 private final Activity mActivity; 98 private final View mAnchorView; 99 ScrollChangeWatcher(Activity activity)100 ScrollChangeWatcher(Activity activity) { 101 mActivity = activity; 102 mAnchorView = null; 103 } 104 ScrollChangeWatcher(View anchorView)105 ScrollChangeWatcher(View anchorView) { 106 mAnchorView = anchorView; 107 mActivity = null; 108 } 109 110 @Override onScrollChange(View view, int scrollX, int scrollY, int oldScrollX, int oldScrollY)111 public void onScrollChange(View view, int scrollX, int scrollY, int oldScrollX, 112 int oldScrollY) { 113 updateDropShadow(view); 114 } 115 updateDropShadow(View view)116 public void updateDropShadow(View view) { 117 final boolean shouldShowShadow = view.canScrollVertically(-1); 118 if (mAnchorView != null) { 119 mAnchorView.setElevation(shouldShowShadow ? ELEVATION_HIGH : ELEVATION_LOW); 120 } else if (mActivity != null) { // activity can become null when running monkey 121 final ActionBar actionBar = mActivity.getActionBar(); 122 if (actionBar != null) { 123 actionBar.setElevation(shouldShowShadow ? ELEVATION_HIGH : ELEVATION_LOW); 124 } 125 } 126 } 127 } 128 } 129