• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 package com.android.launcher3.views;
17 
18 import android.content.Context;
19 import android.graphics.Canvas;
20 import android.util.AttributeSet;
21 import android.widget.EdgeEffect;
22 import android.widget.RelativeLayout;
23 
24 import androidx.annotation.NonNull;
25 import androidx.recyclerview.widget.RecyclerView;
26 import androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory;
27 
28 import com.android.launcher3.Utilities;
29 
30 /**
31  * View group to allow rendering overscroll effect in a child at the parent level
32  */
33 public class SpringRelativeLayout extends RelativeLayout {
34 
35     // fixed edge at the time force is applied
36     private final EdgeEffect mEdgeGlowTop;
37     private final EdgeEffect mEdgeGlowBottom;
38 
SpringRelativeLayout(Context context)39     public SpringRelativeLayout(Context context) {
40         this(context, null);
41     }
42 
SpringRelativeLayout(Context context, AttributeSet attrs)43     public SpringRelativeLayout(Context context, AttributeSet attrs) {
44         this(context, attrs, 0);
45     }
46 
SpringRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr)47     public SpringRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
48         super(context, attrs, defStyleAttr);
49         mEdgeGlowTop = Utilities.ATLEAST_S
50                 ? new EdgeEffect(context, attrs) : new EdgeEffect(context);
51         mEdgeGlowBottom = Utilities.ATLEAST_S
52                 ? new EdgeEffect(context, attrs) : new EdgeEffect(context);
53         setWillNotDraw(false);
54     }
55 
56     @Override
draw(Canvas canvas)57     public void draw(Canvas canvas) {
58         super.draw(canvas);
59         if (!mEdgeGlowTop.isFinished()) {
60             final int restoreCount = canvas.save();
61             canvas.translate(0, 0);
62             mEdgeGlowTop.setSize(getWidth(), getHeight());
63             if (mEdgeGlowTop.draw(canvas)) {
64                 postInvalidateOnAnimation();
65             }
66             canvas.restoreToCount(restoreCount);
67         }
68         if (!mEdgeGlowBottom.isFinished()) {
69             final int restoreCount = canvas.save();
70             final int width = getWidth();
71             final int height = getHeight();
72             canvas.translate(-width, height);
73             canvas.rotate(180, width, 0);
74             mEdgeGlowBottom.setSize(width, height);
75             if (mEdgeGlowBottom.draw(canvas)) {
76                 postInvalidateOnAnimation();
77             }
78             canvas.restoreToCount(restoreCount);
79         }
80     }
81 
82 
83     /**
84      * Absorbs the velocity as a result for swipe-up fling
85      */
absorbSwipeUpVelocity(int velocity)86     protected void absorbSwipeUpVelocity(int velocity) {
87         mEdgeGlowBottom.onAbsorb(velocity);
88         invalidate();
89     }
90 
absorbPullDeltaDistance(float deltaDistance, float displacement)91     protected void absorbPullDeltaDistance(float deltaDistance, float displacement) {
92         mEdgeGlowBottom.onPull(deltaDistance, displacement);
93         invalidate();
94     }
95 
onRelease()96     public void onRelease() {
97         mEdgeGlowBottom.onRelease();
98     }
99 
createEdgeEffectFactory()100     public EdgeEffectFactory createEdgeEffectFactory() {
101         return new ProxyEdgeEffectFactory();
102     }
103 
104     private class ProxyEdgeEffectFactory extends EdgeEffectFactory {
105 
106         @NonNull @Override
createEdgeEffect(RecyclerView view, int direction)107         protected EdgeEffect createEdgeEffect(RecyclerView view, int direction) {
108             if (direction == DIRECTION_TOP) {
109                 return new EdgeEffectProxy(getContext(), mEdgeGlowTop);
110             }
111             return super.createEdgeEffect(view, direction);
112         }
113     }
114 
115     private class EdgeEffectProxy extends EdgeEffect {
116 
117         private final EdgeEffect mParent;
118 
EdgeEffectProxy(Context context, EdgeEffect parent)119         EdgeEffectProxy(Context context, EdgeEffect parent) {
120             super(context);
121             mParent = parent;
122         }
123 
124         @Override
draw(Canvas canvas)125         public boolean draw(Canvas canvas) {
126             return false;
127         }
128 
invalidateParentScrollEffect()129         private void invalidateParentScrollEffect() {
130             if (!mParent.isFinished()) {
131                 invalidate();
132             }
133         }
134 
135         @Override
onAbsorb(int velocity)136         public void onAbsorb(int velocity) {
137             mParent.onAbsorb(velocity);
138             invalidateParentScrollEffect();
139         }
140 
141         @Override
onPull(float deltaDistance)142         public void onPull(float deltaDistance) {
143             mParent.onPull(deltaDistance);
144             invalidateParentScrollEffect();
145         }
146 
147         @Override
onPull(float deltaDistance, float displacement)148         public void onPull(float deltaDistance, float displacement) {
149             mParent.onPull(deltaDistance, displacement);
150             invalidateParentScrollEffect();
151         }
152 
153         @Override
onRelease()154         public void onRelease() {
155             mParent.onRelease();
156             invalidateParentScrollEffect();
157         }
158 
159         @Override
finish()160         public void finish() {
161             mParent.finish();
162         }
163 
164         @Override
isFinished()165         public boolean isFinished() {
166             return mParent.isFinished();
167         }
168     }
169 }