| /* |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.systemui.accessibility.floatingmenu; |
| |
| import android.animation.Animator; |
| import android.animation.AnimatorListenerAdapter; |
| import android.animation.ValueAnimator; |
| import android.view.MotionEvent; |
| |
| import androidx.annotation.NonNull; |
| import androidx.dynamicanimation.animation.DynamicAnimation; |
| |
| import com.android.systemui.R; |
| import com.android.wm.shell.bubbles.DismissView; |
| import com.android.wm.shell.common.magnetictarget.MagnetizedObject; |
| |
| /** |
| * Controls the interaction between {@link MagnetizedObject} and |
| * {@link MagnetizedObject.MagneticTarget}. |
| */ |
| class DismissAnimationController { |
| private static final boolean ENABLE_FLING_TO_DISMISS_MENU = false; |
| private static final float COMPLETELY_OPAQUE = 1.0f; |
| private static final float COMPLETELY_TRANSPARENT = 0.0f; |
| private static final float CIRCLE_VIEW_DEFAULT_SCALE = 1.0f; |
| private static final float ANIMATING_MAX_ALPHA = 0.7f; |
| |
| private final DismissView mDismissView; |
| private final MenuView mMenuView; |
| private final ValueAnimator mDismissAnimator; |
| private final MagnetizedObject<?> mMagnetizedObject; |
| private float mMinDismissSize; |
| private float mSizePercent; |
| |
| DismissAnimationController(DismissView dismissView, MenuView menuView) { |
| mDismissView = dismissView; |
| mDismissView.setPivotX(dismissView.getWidth() / 2.0f); |
| mDismissView.setPivotY(dismissView.getHeight() / 2.0f); |
| mMenuView = menuView; |
| |
| updateResources(); |
| |
| mDismissAnimator = ValueAnimator.ofFloat(COMPLETELY_OPAQUE, COMPLETELY_TRANSPARENT); |
| mDismissAnimator.addUpdateListener(dismissAnimation -> { |
| final float animatedValue = (float) dismissAnimation.getAnimatedValue(); |
| final float scaleValue = Math.max(animatedValue, mSizePercent); |
| dismissView.getCircle().setScaleX(scaleValue); |
| dismissView.getCircle().setScaleY(scaleValue); |
| |
| menuView.setAlpha(Math.max(animatedValue, ANIMATING_MAX_ALPHA)); |
| }); |
| |
| mDismissAnimator.addListener(new AnimatorListenerAdapter() { |
| @Override |
| public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { |
| super.onAnimationEnd(animation, isReverse); |
| |
| if (isReverse) { |
| mDismissView.getCircle().setScaleX(CIRCLE_VIEW_DEFAULT_SCALE); |
| mDismissView.getCircle().setScaleY(CIRCLE_VIEW_DEFAULT_SCALE); |
| mMenuView.setAlpha(COMPLETELY_OPAQUE); |
| } |
| } |
| }); |
| |
| mMagnetizedObject = |
| new MagnetizedObject<MenuView>(mMenuView.getContext(), mMenuView, |
| new MenuAnimationController.MenuPositionProperty( |
| DynamicAnimation.TRANSLATION_X), |
| new MenuAnimationController.MenuPositionProperty( |
| DynamicAnimation.TRANSLATION_Y)) { |
| @Override |
| public void getLocationOnScreen(MenuView underlyingObject, int[] loc) { |
| underlyingObject.getLocationOnScreen(loc); |
| } |
| |
| @Override |
| public float getHeight(MenuView underlyingObject) { |
| return underlyingObject.getHeight(); |
| } |
| |
| @Override |
| public float getWidth(MenuView underlyingObject) { |
| return underlyingObject.getWidth(); |
| } |
| }; |
| |
| final MagnetizedObject.MagneticTarget magneticTarget = new MagnetizedObject.MagneticTarget( |
| dismissView.getCircle(), (int) mMinDismissSize); |
| mMagnetizedObject.addTarget(magneticTarget); |
| mMagnetizedObject.setFlingToTargetEnabled(ENABLE_FLING_TO_DISMISS_MENU); |
| } |
| |
| void showDismissView(boolean show) { |
| if (show) { |
| mDismissView.show(); |
| } else { |
| mDismissView.hide(); |
| } |
| } |
| |
| void setMagnetListener(MagnetizedObject.MagnetListener magnetListener) { |
| mMagnetizedObject.setMagnetListener(magnetListener); |
| } |
| |
| void maybeConsumeDownMotionEvent(MotionEvent event) { |
| mMagnetizedObject.maybeConsumeMotionEvent(event); |
| } |
| |
| /** |
| * This used to pass {@link MotionEvent#ACTION_DOWN} to the magnetized object to check if it was |
| * within the magnetic field. It should be used in the {@link MenuListViewTouchHandler}. |
| * |
| * @param event that move the magnetized object which is also the menu list view. |
| * @return true if the location of the motion events moves within the magnetic field of a |
| * target, but false if didn't set |
| * {@link DismissAnimationController#setMagnetListener(MagnetizedObject.MagnetListener)}. |
| */ |
| boolean maybeConsumeMoveMotionEvent(MotionEvent event) { |
| return mMagnetizedObject.maybeConsumeMotionEvent(event); |
| } |
| |
| /** |
| * This used to pass {@link MotionEvent#ACTION_UP} to the magnetized object to check if it was |
| * within the magnetic field. It should be used in the {@link MenuListViewTouchHandler}. |
| * |
| * @param event that move the magnetized object which is also the menu list view. |
| * @return true if the location of the motion events moves within the magnetic field of a |
| * target, but false if didn't set |
| * {@link DismissAnimationController#setMagnetListener(MagnetizedObject.MagnetListener)}. |
| */ |
| boolean maybeConsumeUpMotionEvent(MotionEvent event) { |
| return mMagnetizedObject.maybeConsumeMotionEvent(event); |
| } |
| |
| void animateDismissMenu(boolean scaleUp) { |
| if (scaleUp) { |
| mDismissAnimator.start(); |
| } else { |
| mDismissAnimator.reverse(); |
| } |
| } |
| |
| void updateResources() { |
| final float maxDismissSize = mDismissView.getResources().getDimensionPixelSize( |
| R.dimen.dismiss_circle_size); |
| mMinDismissSize = mDismissView.getResources().getDimensionPixelSize( |
| R.dimen.dismiss_circle_small); |
| mSizePercent = mMinDismissSize / maxDismissSize; |
| } |
| |
| interface DismissCallback { |
| void onDismiss(); |
| } |
| } |