1 /*
2  * Copyright (C) 2015 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.systemui.stackdivider;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.AnimatorSet;
22 import android.animation.ObjectAnimator;
23 import android.annotation.Nullable;
24 import android.content.Context;
25 import android.graphics.Canvas;
26 import android.graphics.Paint;
27 import android.util.AttributeSet;
28 import android.util.Property;
29 import android.view.View;
30 
31 import com.android.systemui.Interpolators;
32 import com.android.systemui.R;
33 
34 /**
35  * View for the handle in the docked stack divider.
36  */
37 public class DividerHandleView extends View {
38 
39     private final static Property<DividerHandleView, Integer> WIDTH_PROPERTY
40             = new Property<DividerHandleView, Integer>(Integer.class, "width") {
41 
42         @Override
43         public Integer get(DividerHandleView object) {
44             return object.mCurrentWidth;
45         }
46 
47         @Override
48         public void set(DividerHandleView object, Integer value) {
49             object.mCurrentWidth = value;
50             object.invalidate();
51         }
52     };
53 
54     private final static Property<DividerHandleView, Integer> HEIGHT_PROPERTY
55             = new Property<DividerHandleView, Integer>(Integer.class, "height") {
56 
57         @Override
58         public Integer get(DividerHandleView object) {
59             return object.mCurrentHeight;
60         }
61 
62         @Override
63         public void set(DividerHandleView object, Integer value) {
64             object.mCurrentHeight = value;
65             object.invalidate();
66         }
67     };
68 
69     private final Paint mPaint = new Paint();
70     private final int mWidth;
71     private final int mHeight;
72     private final int mCircleDiameter;
73     private int mCurrentWidth;
74     private int mCurrentHeight;
75     private AnimatorSet mAnimator;
76     private boolean mTouching;
77 
DividerHandleView(Context context, @Nullable AttributeSet attrs)78     public DividerHandleView(Context context, @Nullable AttributeSet attrs) {
79         super(context, attrs);
80         mPaint.setColor(getResources().getColor(R.color.docked_divider_handle, null));
81         mPaint.setAntiAlias(true);
82         mWidth = getResources().getDimensionPixelSize(R.dimen.docked_divider_handle_width);
83         mHeight = getResources().getDimensionPixelSize(R.dimen.docked_divider_handle_height);
84         mCurrentWidth = mWidth;
85         mCurrentHeight = mHeight;
86         mCircleDiameter = (mWidth + mHeight) / 3;
87     }
88 
setTouching(boolean touching, boolean animate)89     public void setTouching(boolean touching, boolean animate) {
90         if (touching == mTouching) {
91             return;
92         }
93         if (mAnimator != null) {
94             mAnimator.cancel();
95             mAnimator = null;
96         }
97         if (!animate) {
98             if (touching) {
99                 mCurrentWidth = mCircleDiameter;
100                 mCurrentHeight = mCircleDiameter;
101             } else {
102                 mCurrentWidth = mWidth;
103                 mCurrentHeight = mHeight;
104             }
105             invalidate();
106         } else {
107             animateToTarget(touching ? mCircleDiameter : mWidth,
108                     touching ? mCircleDiameter : mHeight, touching);
109         }
110         mTouching = touching;
111     }
112 
animateToTarget(int targetWidth, int targetHeight, boolean touching)113     private void animateToTarget(int targetWidth, int targetHeight, boolean touching) {
114         ObjectAnimator widthAnimator = ObjectAnimator.ofInt(this, WIDTH_PROPERTY,
115                 mCurrentWidth, targetWidth);
116         ObjectAnimator heightAnimator = ObjectAnimator.ofInt(this, HEIGHT_PROPERTY,
117                 mCurrentHeight, targetHeight);
118         mAnimator = new AnimatorSet();
119         mAnimator.playTogether(widthAnimator, heightAnimator);
120         mAnimator.setDuration(touching
121                 ? DividerView.TOUCH_ANIMATION_DURATION
122                 : DividerView.TOUCH_RELEASE_ANIMATION_DURATION);
123         mAnimator.setInterpolator(touching
124                 ? Interpolators.TOUCH_RESPONSE
125                 : Interpolators.FAST_OUT_SLOW_IN);
126         mAnimator.addListener(new AnimatorListenerAdapter() {
127             @Override
128             public void onAnimationEnd(Animator animation) {
129                 mAnimator = null;
130             }
131         });
132         mAnimator.start();
133     }
134 
135     @Override
onDraw(Canvas canvas)136     protected void onDraw(Canvas canvas) {
137         int left = getWidth() / 2 - mCurrentWidth / 2;
138         int top = getHeight() / 2 - mCurrentHeight / 2;
139         int radius = Math.min(mCurrentWidth, mCurrentHeight) / 2;
140         canvas.drawRoundRect(left, top, left + mCurrentWidth, top + mCurrentHeight,
141                 radius, radius, mPaint);
142     }
143 
144     @Override
hasOverlappingRendering()145     public boolean hasOverlappingRendering() {
146         return false;
147     }
148 }
149