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