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 int mWidth; 72 private int mHeight; 73 private int mTouchingWidth; 74 private int mTouchingHeight; 75 private int mCurrentWidth; 76 private int mCurrentHeight; 77 private AnimatorSet mAnimator; 78 private boolean mTouching; 79 private boolean mHovering; 80 private int mHoveringWidth; 81 private int mHoveringHeight; 82 private boolean mIsLeftRightSplit; 83 DividerHandleView(Context context, @Nullable AttributeSet attrs)84 public DividerHandleView(Context context, @Nullable AttributeSet attrs) { 85 super(context, attrs); 86 mPaint.setColor(getResources().getColor(R.color.docked_divider_handle, null)); 87 mPaint.setAntiAlias(true); 88 updateDimens(); 89 } 90 updateDimens()91 private void updateDimens() { 92 mWidth = getResources().getDimensionPixelSize(mIsLeftRightSplit 93 ? R.dimen.split_divider_handle_height 94 : R.dimen.split_divider_handle_width); 95 mHeight = getResources().getDimensionPixelSize(mIsLeftRightSplit 96 ? R.dimen.split_divider_handle_width 97 : R.dimen.split_divider_handle_height); 98 mCurrentWidth = mWidth; 99 mCurrentHeight = mHeight; 100 mTouchingWidth = mWidth > mHeight ? mWidth / 2 : mWidth; 101 mTouchingHeight = mHeight > mWidth ? mHeight / 2 : mHeight; 102 mHoveringWidth = mWidth > mHeight ? ((int) (mWidth * 1.5f)) : mWidth; 103 mHoveringHeight = mHeight > mWidth ? ((int) (mHeight * 1.5f)) : mHeight; 104 } 105 setIsLeftRightSplit(boolean isLeftRightSplit)106 void setIsLeftRightSplit(boolean isLeftRightSplit) { 107 mIsLeftRightSplit = isLeftRightSplit; 108 updateDimens(); 109 } 110 111 /** Sets touching state for this handle view. */ setTouching(boolean touching, boolean animate)112 public void setTouching(boolean touching, boolean animate) { 113 if (touching == mTouching) { 114 return; 115 } 116 setInputState(touching, animate, mTouchingWidth, mTouchingHeight); 117 mTouching = touching; 118 } 119 120 /** Sets hovering state for this handle view. */ setHovering(boolean hovering, boolean animate)121 public void setHovering(boolean hovering, boolean animate) { 122 if (hovering == mHovering) { 123 return; 124 } 125 setInputState(hovering, animate, mHoveringWidth, mHoveringHeight); 126 mHovering = hovering; 127 } 128 setInputState(boolean stateOn, boolean animate, int stateWidth, int stateHeight)129 private void setInputState(boolean stateOn, boolean animate, int stateWidth, int stateHeight) { 130 if (mAnimator != null) { 131 mAnimator.cancel(); 132 mAnimator = null; 133 } 134 if (!animate) { 135 mCurrentWidth = stateOn ? stateWidth : mWidth; 136 mCurrentHeight = stateOn ? stateHeight : mHeight; 137 invalidate(); 138 } else { 139 animateToTarget(stateOn ? stateWidth : mWidth, 140 stateOn ? stateHeight : mHeight, stateOn); 141 } 142 } 143 animateToTarget(int targetWidth, int targetHeight, boolean touching)144 private void animateToTarget(int targetWidth, int targetHeight, boolean touching) { 145 ObjectAnimator widthAnimator = ObjectAnimator.ofInt(this, WIDTH_PROPERTY, 146 mCurrentWidth, targetWidth); 147 ObjectAnimator heightAnimator = ObjectAnimator.ofInt(this, HEIGHT_PROPERTY, 148 mCurrentHeight, targetHeight); 149 mAnimator = new AnimatorSet(); 150 mAnimator.playTogether(widthAnimator, heightAnimator); 151 mAnimator.setDuration(touching 152 ? TOUCH_ANIMATION_DURATION 153 : TOUCH_RELEASE_ANIMATION_DURATION); 154 mAnimator.setInterpolator(touching 155 ? Interpolators.TOUCH_RESPONSE 156 : Interpolators.FAST_OUT_SLOW_IN); 157 mAnimator.addListener(new AnimatorListenerAdapter() { 158 @Override 159 public void onAnimationEnd(Animator animation) { 160 mAnimator = null; 161 } 162 }); 163 mAnimator.start(); 164 } 165 166 @Override onDraw(Canvas canvas)167 protected void onDraw(Canvas canvas) { 168 int left = getWidth() / 2 - mCurrentWidth / 2; 169 int top = getHeight() / 2 - mCurrentHeight / 2; 170 int radius = Math.min(mCurrentWidth, mCurrentHeight) / 2; 171 canvas.drawRoundRect(left, top, left + mCurrentWidth, top + mCurrentHeight, 172 radius, radius, mPaint); 173 } 174 175 @Override hasOverlappingRendering()176 public boolean hasOverlappingRendering() { 177 return false; 178 } 179 } 180