blob: 065806df3dc831505d194e81f11178e7fbb0060b [file] [log] [blame]
/*
* Copyright (C) 2021 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.CAMERA_COMPAT_CONTROL_DISMISSED;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
import android.annotation.Nullable;
import android.app.TaskInfo;
import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.graphics.Rect;
import android.util.Log;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.CompatUIController.CompatUICallback;
import java.util.function.Consumer;
/**
* Window manager for the Size Compat restart button and Camera Compat control.
*/
class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
private final CompatUICallback mCallback;
private final CompatUIConfiguration mCompatUIConfiguration;
private final Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartButtonClicked;
// Remember the last reported states in case visibility changes due to keyguard or IME updates.
@VisibleForTesting
boolean mHasSizeCompat;
@VisibleForTesting
@CameraCompatControlState
int mCameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
@VisibleForTesting
CompatUIHintsState mCompatUIHintsState;
@Nullable
@VisibleForTesting
CompatUILayout mLayout;
CompatUIWindowManager(Context context, TaskInfo taskInfo,
SyncTransactionQueue syncQueue, CompatUICallback callback,
ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
CompatUIHintsState compatUIHintsState, CompatUIConfiguration compatUIConfiguration,
Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onRestartButtonClicked) {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
mCallback = callback;
mHasSizeCompat = taskInfo.topActivityInSizeCompat;
mCameraCompatControlState = taskInfo.cameraCompatControlState;
mCompatUIHintsState = compatUIHintsState;
mCompatUIConfiguration = compatUIConfiguration;
mOnRestartButtonClicked = onRestartButtonClicked;
}
@Override
protected int getZOrder() {
return TASK_CHILD_LAYER_COMPAT_UI + 1;
}
@Override
protected @Nullable View getLayout() {
return mLayout;
}
@Override
protected void removeLayout() {
mLayout = null;
}
@Override
protected boolean eligibleToShowLayout() {
return mHasSizeCompat || shouldShowCameraControl();
}
@Override
protected View createLayout() {
mLayout = inflateLayout();
mLayout.inject(this);
updateVisibilityOfViews();
if (mHasSizeCompat) {
mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
}
return mLayout;
}
@VisibleForTesting
CompatUILayout inflateLayout() {
return (CompatUILayout) LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout,
null);
}
@Override
public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
boolean canShow) {
final boolean prevHasSizeCompat = mHasSizeCompat;
final int prevCameraCompatControlState = mCameraCompatControlState;
mHasSizeCompat = taskInfo.topActivityInSizeCompat;
mCameraCompatControlState = taskInfo.cameraCompatControlState;
if (!super.updateCompatInfo(taskInfo, taskListener, canShow)) {
return false;
}
if (prevHasSizeCompat != mHasSizeCompat
|| prevCameraCompatControlState != mCameraCompatControlState) {
updateVisibilityOfViews();
}
return true;
}
/** Called when the restart button is clicked. */
void onRestartButtonClicked() {
mOnRestartButtonClicked.accept(Pair.create(getLastTaskInfo(), getTaskListener()));
}
/** Called when the camera treatment button is clicked. */
void onCameraTreatmentButtonClicked() {
if (!shouldShowCameraControl()) {
Log.w(getTag(), "Camera compat shouldn't receive clicks in the hidden state.");
return;
}
// When a camera control is shown, only two states are allowed: "treament applied" and
// "treatment suggested". Clicks on the conrol's treatment button toggle between these
// two states.
mCameraCompatControlState =
mCameraCompatControlState == CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED
? CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED
: CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
mCallback.onCameraControlStateUpdated(mTaskId, mCameraCompatControlState);
mLayout.updateCameraTreatmentButton(mCameraCompatControlState);
}
/** Called when the camera dismiss button is clicked. */
void onCameraDismissButtonClicked() {
if (!shouldShowCameraControl()) {
Log.w(getTag(), "Camera compat shouldn't receive clicks in the hidden state.");
return;
}
mCameraCompatControlState = CAMERA_COMPAT_CONTROL_DISMISSED;
mCallback.onCameraControlStateUpdated(mTaskId, CAMERA_COMPAT_CONTROL_DISMISSED);
mLayout.setCameraControlVisibility(/* show= */ false);
}
/** Called when the restart button is long clicked. */
void onRestartButtonLongClicked() {
if (mLayout == null) {
return;
}
mLayout.setSizeCompatHintVisibility(/* show= */ true);
}
/** Called when either dismiss or treatment camera buttons is long clicked. */
void onCameraButtonLongClicked() {
if (mLayout == null) {
return;
}
mLayout.setCameraCompatHintVisibility(/* show= */ true);
}
@Override
@VisibleForTesting
public void updateSurfacePosition() {
if (mLayout == null) {
return;
}
// Position of the button in the container coordinate.
final Rect taskBounds = getTaskBounds();
final Rect taskStableBounds = getTaskStableBounds();
final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
? taskStableBounds.left - taskBounds.left
: taskStableBounds.right - taskBounds.left - mLayout.getMeasuredWidth();
final int positionY = taskStableBounds.bottom - taskBounds.top
- mLayout.getMeasuredHeight();
updateSurfacePosition(positionX, positionY);
}
private void updateVisibilityOfViews() {
if (mLayout == null) {
return;
}
// Size Compat mode restart button.
mLayout.setRestartButtonVisibility(mHasSizeCompat);
// Only show by default for the first time.
if (mHasSizeCompat && !mCompatUIHintsState.mHasShownSizeCompatHint) {
mLayout.setSizeCompatHintVisibility(/* show= */ true);
mCompatUIHintsState.mHasShownSizeCompatHint = true;
}
// Camera control for stretched issues.
mLayout.setCameraControlVisibility(shouldShowCameraControl());
// Only show by default for the first time.
if (shouldShowCameraControl() && !mCompatUIHintsState.mHasShownCameraCompatHint) {
mLayout.setCameraCompatHintVisibility(/* show= */ true);
mCompatUIHintsState.mHasShownCameraCompatHint = true;
}
if (shouldShowCameraControl()) {
mLayout.updateCameraTreatmentButton(mCameraCompatControlState);
}
}
private boolean shouldShowCameraControl() {
return mCameraCompatControlState != CAMERA_COMPAT_CONTROL_HIDDEN
&& mCameraCompatControlState != CAMERA_COMPAT_CONTROL_DISMISSED;
}
/**
* A class holding the state of the compat UI hints, which is shared between all compat UI
* window managers.
*/
static class CompatUIHintsState {
@VisibleForTesting
boolean mHasShownSizeCompatHint;
@VisibleForTesting
boolean mHasShownCameraCompatHint;
}
}