270 lines
11 KiB
Java
270 lines
11 KiB
Java
/*
|
|
* Copyright (C) 2023 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.wm.shell.compatui;
|
|
|
|
import static android.app.TaskInfo.PROPERTY_VALUE_UNSET;
|
|
|
|
import android.animation.Animator;
|
|
import android.animation.AnimatorListenerAdapter;
|
|
import android.animation.ObjectAnimator;
|
|
import android.animation.ValueAnimator;
|
|
import android.app.TaskInfo;
|
|
import android.content.Context;
|
|
import android.util.AttributeSet;
|
|
import android.view.View;
|
|
import android.widget.FrameLayout;
|
|
|
|
import com.android.wm.shell.R;
|
|
|
|
import java.util.function.BiConsumer;
|
|
import java.util.function.Function;
|
|
|
|
/**
|
|
* Container for reachability education which handles all the show/hide animations.
|
|
*/
|
|
public class ReachabilityEduLayout extends FrameLayout {
|
|
|
|
private static final float ALPHA_FULL_TRANSPARENT = 0f;
|
|
|
|
private static final float ALPHA_FULL_OPAQUE = 1f;
|
|
|
|
private static final long VISIBILITY_ANIMATION_DURATION_MS = 400;
|
|
|
|
private static final long MARGINS_ANIMATION_DURATION_MS = 250;
|
|
|
|
private static final String ALPHA_PROPERTY_NAME = "alpha";
|
|
|
|
private ReachabilityEduWindowManager mWindowManager;
|
|
|
|
private View mMoveLeftButton;
|
|
private View mMoveRightButton;
|
|
private View mMoveUpButton;
|
|
private View mMoveDownButton;
|
|
|
|
private int mLastLeftMargin = PROPERTY_VALUE_UNSET;
|
|
private int mLastRightMargin = PROPERTY_VALUE_UNSET;
|
|
private int mLastTopMargin = PROPERTY_VALUE_UNSET;
|
|
private int mLastBottomMargin = PROPERTY_VALUE_UNSET;
|
|
|
|
public ReachabilityEduLayout(Context context) {
|
|
this(context, null);
|
|
}
|
|
|
|
public ReachabilityEduLayout(Context context, AttributeSet attrs) {
|
|
this(context, attrs, 0);
|
|
}
|
|
|
|
public ReachabilityEduLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
this(context, attrs, defStyleAttr, 0);
|
|
}
|
|
|
|
public ReachabilityEduLayout(Context context, AttributeSet attrs, int defStyleAttr,
|
|
int defStyleRes) {
|
|
super(context, attrs, defStyleAttr, defStyleRes);
|
|
}
|
|
|
|
void inject(ReachabilityEduWindowManager windowManager) {
|
|
mWindowManager = windowManager;
|
|
}
|
|
|
|
void handleVisibility(boolean horizontalEnabled, boolean verticalEnabled,
|
|
int letterboxVerticalPosition,
|
|
int letterboxHorizontalPosition, int availableWidth, int availableHeight,
|
|
CompatUIConfiguration compatUIConfiguration, TaskInfo taskInfo) {
|
|
hideAllImmediately();
|
|
if (horizontalEnabled && letterboxHorizontalPosition != PROPERTY_VALUE_UNSET) {
|
|
handleLetterboxHorizontalPosition(availableWidth, letterboxHorizontalPosition);
|
|
compatUIConfiguration.setUserHasSeenHorizontalReachabilityEducation(taskInfo);
|
|
} else if (verticalEnabled && letterboxVerticalPosition != PROPERTY_VALUE_UNSET) {
|
|
handleLetterboxVerticalPosition(availableHeight, letterboxVerticalPosition);
|
|
compatUIConfiguration.setUserHasSeenVerticalReachabilityEducation(taskInfo);
|
|
}
|
|
}
|
|
|
|
void hideAllImmediately() {
|
|
hideImmediately(mMoveLeftButton);
|
|
hideImmediately(mMoveRightButton);
|
|
hideImmediately(mMoveUpButton);
|
|
hideImmediately(mMoveDownButton);
|
|
mLastLeftMargin = PROPERTY_VALUE_UNSET;
|
|
mLastRightMargin = PROPERTY_VALUE_UNSET;
|
|
mLastTopMargin = PROPERTY_VALUE_UNSET;
|
|
mLastBottomMargin = PROPERTY_VALUE_UNSET;
|
|
}
|
|
|
|
@Override
|
|
protected void onFinishInflate() {
|
|
super.onFinishInflate();
|
|
mMoveLeftButton = findViewById(R.id.reachability_move_left_button);
|
|
mMoveRightButton = findViewById(R.id.reachability_move_right_button);
|
|
mMoveUpButton = findViewById(R.id.reachability_move_up_button);
|
|
mMoveDownButton = findViewById(R.id.reachability_move_down_button);
|
|
mMoveLeftButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
|
|
mMoveRightButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
|
|
mMoveUpButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
|
|
mMoveDownButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
|
|
}
|
|
|
|
private void hideImmediately(View view) {
|
|
view.setAlpha(0);
|
|
view.setVisibility(View.INVISIBLE);
|
|
}
|
|
|
|
private Animator marginAnimator(View view, Function<LayoutParams, Integer> marginSupplier,
|
|
BiConsumer<LayoutParams, Integer> marginConsumer, int from, int to) {
|
|
final LayoutParams layoutParams = ((LayoutParams) view.getLayoutParams());
|
|
ValueAnimator animator = ValueAnimator.ofInt(marginSupplier.apply(layoutParams), from, to);
|
|
animator.addUpdateListener(valueAnimator -> {
|
|
marginConsumer.accept(layoutParams, (Integer) valueAnimator.getAnimatedValue());
|
|
view.requestLayout();
|
|
});
|
|
animator.setDuration(MARGINS_ANIMATION_DURATION_MS);
|
|
return animator;
|
|
}
|
|
|
|
private void handleLetterboxHorizontalPosition(int availableWidth,
|
|
int letterboxHorizontalPosition) {
|
|
hideItem(mMoveUpButton);
|
|
hideItem(mMoveDownButton);
|
|
mLastTopMargin = PROPERTY_VALUE_UNSET;
|
|
mLastBottomMargin = PROPERTY_VALUE_UNSET;
|
|
// We calculate the available space on the left and right
|
|
final int horizontalGap = availableWidth / 2;
|
|
final int leftAvailableSpace = letterboxHorizontalPosition * horizontalGap;
|
|
final int rightAvailableSpace = availableWidth - leftAvailableSpace;
|
|
// We show the button if we have enough space
|
|
if (leftAvailableSpace >= mMoveLeftButton.getMeasuredWidth()) {
|
|
int newLeftMargin = (horizontalGap - mMoveLeftButton.getMeasuredWidth()) / 2;
|
|
if (mLastLeftMargin == PROPERTY_VALUE_UNSET) {
|
|
mLastLeftMargin = newLeftMargin;
|
|
}
|
|
if (mLastLeftMargin != newLeftMargin) {
|
|
marginAnimator(mMoveLeftButton, layoutParams -> layoutParams.leftMargin,
|
|
(layoutParams, margin) -> layoutParams.leftMargin = margin,
|
|
mLastLeftMargin, newLeftMargin).start();
|
|
} else {
|
|
final LayoutParams leftParams = ((LayoutParams) mMoveLeftButton.getLayoutParams());
|
|
leftParams.leftMargin = mLastLeftMargin;
|
|
mMoveLeftButton.setLayoutParams(leftParams);
|
|
}
|
|
showItem(mMoveLeftButton);
|
|
} else {
|
|
hideItem(mMoveLeftButton);
|
|
mLastLeftMargin = PROPERTY_VALUE_UNSET;
|
|
}
|
|
if (rightAvailableSpace >= mMoveRightButton.getMeasuredWidth()) {
|
|
int newRightMargin = (horizontalGap - mMoveRightButton.getMeasuredWidth()) / 2;
|
|
if (mLastRightMargin == PROPERTY_VALUE_UNSET) {
|
|
mLastRightMargin = newRightMargin;
|
|
}
|
|
if (mLastRightMargin != newRightMargin) {
|
|
marginAnimator(mMoveRightButton, layoutParams -> layoutParams.rightMargin,
|
|
(layoutParams, margin) -> layoutParams.rightMargin = margin,
|
|
mLastRightMargin, newRightMargin).start();
|
|
} else {
|
|
final LayoutParams rightParams =
|
|
((LayoutParams) mMoveRightButton.getLayoutParams());
|
|
rightParams.rightMargin = mLastRightMargin;
|
|
mMoveRightButton.setLayoutParams(rightParams);
|
|
}
|
|
showItem(mMoveRightButton);
|
|
} else {
|
|
hideItem(mMoveRightButton);
|
|
mLastRightMargin = PROPERTY_VALUE_UNSET;
|
|
}
|
|
}
|
|
|
|
private void handleLetterboxVerticalPosition(int availableHeight,
|
|
int letterboxVerticalPosition) {
|
|
hideItem(mMoveLeftButton);
|
|
hideItem(mMoveRightButton);
|
|
mLastLeftMargin = PROPERTY_VALUE_UNSET;
|
|
mLastRightMargin = PROPERTY_VALUE_UNSET;
|
|
// We calculate the available space on the left and right
|
|
final int verticalGap = availableHeight / 2;
|
|
final int topAvailableSpace = letterboxVerticalPosition * verticalGap;
|
|
final int bottomAvailableSpace = availableHeight - topAvailableSpace;
|
|
if (topAvailableSpace >= mMoveUpButton.getMeasuredHeight()) {
|
|
int newTopMargin = (verticalGap - mMoveUpButton.getMeasuredHeight()) / 2;
|
|
if (mLastTopMargin == PROPERTY_VALUE_UNSET) {
|
|
mLastTopMargin = newTopMargin;
|
|
}
|
|
if (mLastTopMargin != newTopMargin) {
|
|
marginAnimator(mMoveUpButton, layoutParams -> layoutParams.topMargin,
|
|
(layoutParams, margin) -> layoutParams.topMargin = margin,
|
|
mLastTopMargin, newTopMargin).start();
|
|
} else {
|
|
final LayoutParams topParams = ((LayoutParams) mMoveUpButton.getLayoutParams());
|
|
topParams.topMargin = mLastTopMargin;
|
|
mMoveUpButton.setLayoutParams(topParams);
|
|
}
|
|
showItem(mMoveUpButton);
|
|
} else {
|
|
hideItem(mMoveUpButton);
|
|
mLastTopMargin = PROPERTY_VALUE_UNSET;
|
|
}
|
|
if (bottomAvailableSpace >= mMoveDownButton.getMeasuredHeight()) {
|
|
int newBottomMargin = (verticalGap - mMoveDownButton.getMeasuredHeight()) / 2;
|
|
if (mLastBottomMargin == PROPERTY_VALUE_UNSET) {
|
|
mLastBottomMargin = newBottomMargin;
|
|
}
|
|
if (mLastBottomMargin != newBottomMargin) {
|
|
marginAnimator(mMoveDownButton, layoutParams -> layoutParams.bottomMargin,
|
|
(layoutParams, margin) -> layoutParams.bottomMargin = margin,
|
|
mLastBottomMargin, newBottomMargin).start();
|
|
} else {
|
|
final LayoutParams bottomParams =
|
|
((LayoutParams) mMoveDownButton.getLayoutParams());
|
|
bottomParams.bottomMargin = mLastBottomMargin;
|
|
mMoveDownButton.setLayoutParams(bottomParams);
|
|
}
|
|
showItem(mMoveDownButton);
|
|
} else {
|
|
hideItem(mMoveDownButton);
|
|
mLastBottomMargin = PROPERTY_VALUE_UNSET;
|
|
}
|
|
}
|
|
|
|
private void showItem(View view) {
|
|
view.setVisibility(View.VISIBLE);
|
|
ObjectAnimator fadeIn = ObjectAnimator.ofFloat(view, ALPHA_PROPERTY_NAME,
|
|
ALPHA_FULL_TRANSPARENT, ALPHA_FULL_OPAQUE);
|
|
fadeIn.setDuration(VISIBILITY_ANIMATION_DURATION_MS);
|
|
fadeIn.addListener(new AnimatorListenerAdapter() {
|
|
@Override
|
|
public void onAnimationEnd(Animator animation) {
|
|
view.setVisibility(View.VISIBLE);
|
|
}
|
|
});
|
|
fadeIn.start();
|
|
}
|
|
|
|
private void hideItem(View view) {
|
|
ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, ALPHA_PROPERTY_NAME,
|
|
ALPHA_FULL_OPAQUE, ALPHA_FULL_TRANSPARENT);
|
|
fadeOut.setDuration(VISIBILITY_ANIMATION_DURATION_MS);
|
|
fadeOut.addListener(new AnimatorListenerAdapter() {
|
|
@Override
|
|
public void onAnimationEnd(Animator animation) {
|
|
view.setVisibility(View.INVISIBLE);
|
|
}
|
|
});
|
|
fadeOut.start();
|
|
}
|
|
|
|
}
|