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