Snap for 9434600 from d5a4b9e514 to tm-qpr2-release
Change-Id: I07c013ec90fe86a51c905b4aea0a3ef86409e92f
This commit is contained in:
@@ -115,4 +115,10 @@
|
||||
<!-- Components support to launch multiple instances into split-screen -->
|
||||
<string-array name="config_appsSupportMultiInstancesSplit">
|
||||
</string-array>
|
||||
|
||||
<!-- Whether the extended restart dialog is enabled -->
|
||||
<bool name="config_letterboxIsRestartDialogEnabled">false</bool>
|
||||
|
||||
<!-- Whether the additional education about reachability is enabled -->
|
||||
<bool name="config_letterboxIsReachabilityEducationEnabled">false</bool>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.wm.shell.compatui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.DeviceConfig;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.common.annotations.ShellMainThread;
|
||||
import com.android.wm.shell.dagger.WMSingleton;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Configuration flags for the CompatUX implementation
|
||||
*/
|
||||
@WMSingleton
|
||||
public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedListener {
|
||||
|
||||
static final String KEY_ENABLE_LETTERBOX_RESTART_DIALOG = "enable_letterbox_restart_dialog";
|
||||
|
||||
static final String KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION =
|
||||
"enable_letterbox_reachability_education";
|
||||
|
||||
// Whether the extended restart dialog is enabled
|
||||
private boolean mIsRestartDialogEnabled;
|
||||
|
||||
// Whether the additional education about reachability is enabled
|
||||
private boolean mIsReachabilityEducationEnabled;
|
||||
|
||||
// Whether the extended restart dialog is enabled
|
||||
private boolean mIsRestartDialogOverrideEnabled;
|
||||
|
||||
// Whether the additional education about reachability is enabled
|
||||
private boolean mIsReachabilityEducationOverrideEnabled;
|
||||
|
||||
// Whether the extended restart dialog is allowed from backend
|
||||
private boolean mIsLetterboxRestartDialogAllowed;
|
||||
|
||||
// Whether the additional education about reachability is allowed from backend
|
||||
private boolean mIsLetterboxReachabilityEducationAllowed;
|
||||
|
||||
@Inject
|
||||
public CompatUIConfiguration(Context context, @ShellMainThread ShellExecutor mainExecutor) {
|
||||
mIsRestartDialogEnabled = context.getResources().getBoolean(
|
||||
R.bool.config_letterboxIsRestartDialogEnabled);
|
||||
mIsReachabilityEducationEnabled = context.getResources().getBoolean(
|
||||
R.bool.config_letterboxIsReachabilityEducationEnabled);
|
||||
mIsLetterboxRestartDialogAllowed = DeviceConfig.getBoolean(
|
||||
DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_RESTART_DIALOG, false);
|
||||
mIsLetterboxReachabilityEducationAllowed = DeviceConfig.getBoolean(
|
||||
DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION,
|
||||
false);
|
||||
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_COMPAT, mainExecutor,
|
||||
this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@value true} if the restart dialog is enabled.
|
||||
*/
|
||||
boolean isRestartDialogEnabled() {
|
||||
return mIsRestartDialogOverrideEnabled || (mIsRestartDialogEnabled
|
||||
&& mIsLetterboxRestartDialogAllowed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables/Disables the restart education dialog
|
||||
*/
|
||||
void setIsRestartDialogOverrideEnabled(boolean enabled) {
|
||||
mIsRestartDialogOverrideEnabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@value true} if the reachability education is enabled.
|
||||
*/
|
||||
boolean isReachabilityEducationEnabled() {
|
||||
return mIsReachabilityEducationOverrideEnabled || (mIsReachabilityEducationEnabled
|
||||
&& mIsLetterboxReachabilityEducationAllowed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables/Disables the reachability education
|
||||
*/
|
||||
void setIsReachabilityEducationOverrideEnabled(boolean enabled) {
|
||||
mIsReachabilityEducationOverrideEnabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
|
||||
// TODO(b/263349751): Update flag and default value to true
|
||||
if (properties.getKeyset().contains(KEY_ENABLE_LETTERBOX_RESTART_DIALOG)) {
|
||||
mIsLetterboxRestartDialogAllowed = DeviceConfig.getBoolean(
|
||||
DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_RESTART_DIALOG,
|
||||
false);
|
||||
}
|
||||
if (properties.getKeyset().contains(KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION)) {
|
||||
mIsLetterboxReachabilityEducationAllowed = DeviceConfig.getBoolean(
|
||||
DeviceConfig.NAMESPACE_WINDOW_MANAGER,
|
||||
KEY_ENABLE_LETTERBOX_REACHABILITY_EDUCATION, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.wm.shell.compatui;
|
||||
|
||||
import com.android.wm.shell.dagger.WMSingleton;
|
||||
import com.android.wm.shell.sysui.ShellCommandHandler;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Handles the shell commands for the CompatUX.
|
||||
*
|
||||
* <p> Use with {@code adb shell dumpsys activity service SystemUIService WMShell compatui
|
||||
* <command>}.
|
||||
*/
|
||||
@WMSingleton
|
||||
public final class CompatUIShellCommandHandler implements
|
||||
ShellCommandHandler.ShellCommandActionHandler {
|
||||
|
||||
private final CompatUIConfiguration mCompatUIConfiguration;
|
||||
private final ShellCommandHandler mShellCommandHandler;
|
||||
|
||||
@Inject
|
||||
public CompatUIShellCommandHandler(ShellCommandHandler shellCommandHandler,
|
||||
CompatUIConfiguration compatUIConfiguration) {
|
||||
mShellCommandHandler = shellCommandHandler;
|
||||
mCompatUIConfiguration = compatUIConfiguration;
|
||||
}
|
||||
|
||||
void onInit() {
|
||||
mShellCommandHandler.addCommandCallback("compatui", this, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onShellCommand(String[] args, PrintWriter pw) {
|
||||
if (args.length != 2) {
|
||||
pw.println("Invalid command: " + args[0]);
|
||||
return false;
|
||||
}
|
||||
switch (args[0]) {
|
||||
case "restartDialogEnabled":
|
||||
return invokeOrError(args[1], pw,
|
||||
mCompatUIConfiguration::setIsRestartDialogOverrideEnabled);
|
||||
case "reachabilityEducationEnabled":
|
||||
return invokeOrError(args[1], pw,
|
||||
mCompatUIConfiguration::setIsReachabilityEducationOverrideEnabled);
|
||||
default:
|
||||
pw.println("Invalid command: " + args[0]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printShellCommandHelp(PrintWriter pw, String prefix) {
|
||||
pw.println(prefix + "restartDialogEnabled [0|false|1|true]");
|
||||
pw.println(prefix + " Enable/Disable the restart education dialog for Size Compat Mode");
|
||||
pw.println(prefix + "reachabilityEducationEnabled [0|false|1|true]");
|
||||
pw.println(prefix
|
||||
+ " Enable/Disable the restart education dialog for letterbox reachability");
|
||||
pw.println(prefix + " Disable the restart education dialog for letterbox reachability");
|
||||
}
|
||||
|
||||
private static boolean invokeOrError(String input, PrintWriter pw,
|
||||
Consumer<Boolean> setter) {
|
||||
Boolean asBoolean = strToBoolean(input);
|
||||
if (asBoolean == null) {
|
||||
pw.println("Error: expected true, 1, false, 0.");
|
||||
return false;
|
||||
}
|
||||
setter.accept(asBoolean);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Converts a String to boolean if possible or it returns null otherwise
|
||||
private static Boolean strToBoolean(String str) {
|
||||
switch(str) {
|
||||
case "1":
|
||||
case "true":
|
||||
return true;
|
||||
case "0":
|
||||
case "false":
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.wm.shell.compatui.letterboxedu;
|
||||
package com.android.wm.shell.compatui;
|
||||
|
||||
import static com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
|
||||
import static com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
|
||||
@@ -38,10 +38,15 @@ import android.view.animation.Animation;
|
||||
import com.android.internal.policy.TransitionAnimation;
|
||||
|
||||
/**
|
||||
* Controls the enter/exit animations of the letterbox education.
|
||||
* Controls the enter/exit a dialog.
|
||||
*
|
||||
* @param <T> The {@link DialogContainerSupplier} to use
|
||||
*/
|
||||
class LetterboxEduAnimationController {
|
||||
private static final String TAG = "LetterboxEduAnimation";
|
||||
public class DialogAnimationController<T extends DialogContainerSupplier> {
|
||||
|
||||
// The alpha of a background is a number between 0 (fully transparent) to 255 (fully opaque).
|
||||
// 204 is simply 255 * 0.8.
|
||||
static final int BACKGROUND_DIM_ALPHA = 204;
|
||||
|
||||
// If shell transitions are enabled, startEnterAnimation will be called after all transitions
|
||||
// have finished, and therefore the start delay should be shorter.
|
||||
@@ -49,6 +54,7 @@ class LetterboxEduAnimationController {
|
||||
|
||||
private final TransitionAnimation mTransitionAnimation;
|
||||
private final String mPackageName;
|
||||
private final String mTag;
|
||||
@AnyRes
|
||||
private final int mAnimStyleResId;
|
||||
|
||||
@@ -57,23 +63,24 @@ class LetterboxEduAnimationController {
|
||||
@Nullable
|
||||
private Animator mBackgroundDimAnimator;
|
||||
|
||||
LetterboxEduAnimationController(Context context) {
|
||||
mTransitionAnimation = new TransitionAnimation(context, /* debug= */ false, TAG);
|
||||
public DialogAnimationController(Context context, String tag) {
|
||||
mTransitionAnimation = new TransitionAnimation(context, /* debug= */ false, tag);
|
||||
mAnimStyleResId = (new ContextThemeWrapper(context,
|
||||
android.R.style.ThemeOverlay_Material_Dialog).getTheme()).obtainStyledAttributes(
|
||||
com.android.internal.R.styleable.Window).getResourceId(
|
||||
com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
|
||||
mPackageName = context.getPackageName();
|
||||
mTag = tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts both background dim fade-in animation and the dialog enter animation.
|
||||
*/
|
||||
void startEnterAnimation(@NonNull LetterboxEduDialogLayout layout, Runnable endCallback) {
|
||||
public void startEnterAnimation(@NonNull T layout, Runnable endCallback) {
|
||||
// Cancel any previous animation if it's still running.
|
||||
cancelAnimation();
|
||||
|
||||
final View dialogContainer = layout.getDialogContainer();
|
||||
final View dialogContainer = layout.getDialogContainerView();
|
||||
mDialogAnimation = loadAnimation(WindowAnimation_windowEnterAnimation);
|
||||
if (mDialogAnimation == null) {
|
||||
endCallback.run();
|
||||
@@ -86,8 +93,8 @@ class LetterboxEduAnimationController {
|
||||
endCallback.run();
|
||||
}));
|
||||
|
||||
mBackgroundDimAnimator = getAlphaAnimator(layout.getBackgroundDim(),
|
||||
/* endAlpha= */ LetterboxEduDialogLayout.BACKGROUND_DIM_ALPHA,
|
||||
mBackgroundDimAnimator = getAlphaAnimator(layout.getBackgroundDimDrawable(),
|
||||
/* endAlpha= */ BACKGROUND_DIM_ALPHA,
|
||||
mDialogAnimation.getDuration());
|
||||
mBackgroundDimAnimator.addListener(getDimAnimatorListener());
|
||||
|
||||
@@ -101,11 +108,11 @@ class LetterboxEduAnimationController {
|
||||
/**
|
||||
* Starts both the background dim fade-out animation and the dialog exit animation.
|
||||
*/
|
||||
void startExitAnimation(@NonNull LetterboxEduDialogLayout layout, Runnable endCallback) {
|
||||
public void startExitAnimation(@NonNull T layout, Runnable endCallback) {
|
||||
// Cancel any previous animation if it's still running.
|
||||
cancelAnimation();
|
||||
|
||||
final View dialogContainer = layout.getDialogContainer();
|
||||
final View dialogContainer = layout.getDialogContainerView();
|
||||
mDialogAnimation = loadAnimation(WindowAnimation_windowExitAnimation);
|
||||
if (mDialogAnimation == null) {
|
||||
endCallback.run();
|
||||
@@ -119,8 +126,8 @@ class LetterboxEduAnimationController {
|
||||
endCallback.run();
|
||||
}));
|
||||
|
||||
mBackgroundDimAnimator = getAlphaAnimator(layout.getBackgroundDim(), /* endAlpha= */ 0,
|
||||
mDialogAnimation.getDuration());
|
||||
mBackgroundDimAnimator = getAlphaAnimator(layout.getBackgroundDimDrawable(),
|
||||
/* endAlpha= */ 0, mDialogAnimation.getDuration());
|
||||
mBackgroundDimAnimator.addListener(getDimAnimatorListener());
|
||||
|
||||
dialogContainer.startAnimation(mDialogAnimation);
|
||||
@@ -130,7 +137,7 @@ class LetterboxEduAnimationController {
|
||||
/**
|
||||
* Cancels all animations and resets the state of the controller.
|
||||
*/
|
||||
void cancelAnimation() {
|
||||
public void cancelAnimation() {
|
||||
if (mDialogAnimation != null) {
|
||||
mDialogAnimation.cancel();
|
||||
mDialogAnimation = null;
|
||||
@@ -145,7 +152,7 @@ class LetterboxEduAnimationController {
|
||||
Animation animation = mTransitionAnimation.loadAnimationAttr(mPackageName, mAnimStyleResId,
|
||||
animAttr, /* translucent= */ false);
|
||||
if (animation == null) {
|
||||
Log.e(TAG, "Failed to load animation " + animAttr);
|
||||
Log.e(mTag, "Failed to load animation " + animAttr);
|
||||
}
|
||||
return animation;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.wm.shell.compatui;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* A component which can provide a {@link View} to use as a container for a Dialog
|
||||
*/
|
||||
public interface DialogContainerSupplier {
|
||||
|
||||
/**
|
||||
* @return The {@link View} to use as a container for a Dialog
|
||||
*/
|
||||
View getDialogContainerView();
|
||||
|
||||
/**
|
||||
* @return The {@link Drawable} to use as background of the dialog.
|
||||
*/
|
||||
Drawable getBackgroundDimDrawable();
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import android.widget.TextView;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.compatui.DialogContainerSupplier;
|
||||
|
||||
/**
|
||||
* Container for Letterbox Education Dialog and background dim.
|
||||
@@ -33,11 +34,7 @@ import com.android.wm.shell.R;
|
||||
* <p>This layout should fill the entire task and the background around the dialog acts as the
|
||||
* background dim which dismisses the dialog when clicked.
|
||||
*/
|
||||
class LetterboxEduDialogLayout extends ConstraintLayout {
|
||||
|
||||
// The alpha of a background is a number between 0 (fully transparent) to 255 (fully opaque).
|
||||
// 204 is simply 255 * 0.8.
|
||||
static final int BACKGROUND_DIM_ALPHA = 204;
|
||||
class LetterboxEduDialogLayout extends ConstraintLayout implements DialogContainerSupplier {
|
||||
|
||||
private View mDialogContainer;
|
||||
private TextView mDialogTitle;
|
||||
@@ -60,18 +57,20 @@ class LetterboxEduDialogLayout extends ConstraintLayout {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
View getDialogContainer() {
|
||||
@Override
|
||||
public View getDialogContainerView() {
|
||||
return mDialogContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getBackgroundDimDrawable() {
|
||||
return mBackgroundDim;
|
||||
}
|
||||
|
||||
TextView getDialogTitle() {
|
||||
return mDialogTitle;
|
||||
}
|
||||
|
||||
Drawable getBackgroundDim() {
|
||||
return mBackgroundDim;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback for the dismiss button and background dim.
|
||||
*
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.android.wm.shell.common.DisplayLayout;
|
||||
import com.android.wm.shell.common.DockStateReader;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.compatui.CompatUIWindowManagerAbstract;
|
||||
import com.android.wm.shell.compatui.DialogAnimationController;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
|
||||
/**
|
||||
@@ -63,7 +64,7 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
|
||||
*/
|
||||
private final SharedPreferences mSharedPreferences;
|
||||
|
||||
private final LetterboxEduAnimationController mAnimationController;
|
||||
private final DialogAnimationController<LetterboxEduDialogLayout> mAnimationController;
|
||||
|
||||
private final Transitions mTransitions;
|
||||
|
||||
@@ -96,14 +97,17 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
|
||||
DisplayLayout displayLayout, Transitions transitions,
|
||||
Runnable onDismissCallback, DockStateReader dockStateReader) {
|
||||
this(context, taskInfo, syncQueue, taskListener, displayLayout, transitions,
|
||||
onDismissCallback, new LetterboxEduAnimationController(context), dockStateReader);
|
||||
onDismissCallback,
|
||||
new DialogAnimationController<>(context, /* tag */ "LetterboxEduWindowManager"),
|
||||
dockStateReader);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
LetterboxEduWindowManager(Context context, TaskInfo taskInfo,
|
||||
SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
|
||||
DisplayLayout displayLayout, Transitions transitions, Runnable onDismissCallback,
|
||||
LetterboxEduAnimationController animationController, DockStateReader dockStateReader) {
|
||||
DialogAnimationController<LetterboxEduDialogLayout> animationController,
|
||||
DockStateReader dockStateReader) {
|
||||
super(context, taskInfo, syncQueue, taskListener, displayLayout);
|
||||
mTransitions = transitions;
|
||||
mOnDismissCallback = onDismissCallback;
|
||||
@@ -160,7 +164,7 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
|
||||
if (mLayout == null) {
|
||||
return;
|
||||
}
|
||||
final View dialogContainer = mLayout.getDialogContainer();
|
||||
final View dialogContainer = mLayout.getDialogContainerView();
|
||||
MarginLayoutParams marginParams = (MarginLayoutParams) dialogContainer.getLayoutParams();
|
||||
|
||||
final Rect taskBounds = getTaskBounds();
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.android.wm.shell.unfold;
|
||||
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.app.TaskInfo;
|
||||
@@ -56,6 +54,12 @@ public class UnfoldAnimationController implements UnfoldListener {
|
||||
private final SparseArray<SurfaceControl> mTaskSurfaces = new SparseArray<>();
|
||||
private final SparseArray<UnfoldTaskAnimator> mAnimatorsByTaskId = new SparseArray<>();
|
||||
|
||||
/**
|
||||
* Indicates whether we're in stage change process. This should be set to {@code true} in
|
||||
* {@link #onStateChangeStarted()} and {@code false} in {@link #onStateChangeFinished()}.
|
||||
*/
|
||||
private boolean mIsInStageChange;
|
||||
|
||||
public UnfoldAnimationController(
|
||||
@NonNull ShellInit shellInit,
|
||||
@NonNull TransactionPool transactionPool,
|
||||
@@ -123,7 +127,7 @@ public class UnfoldAnimationController implements UnfoldListener {
|
||||
animator.onTaskChanged(taskInfo);
|
||||
} else {
|
||||
// Became inapplicable
|
||||
resetTask(animator, taskInfo);
|
||||
maybeResetTask(animator, taskInfo);
|
||||
animator.onTaskVanished(taskInfo);
|
||||
mAnimatorsByTaskId.remove(taskInfo.taskId);
|
||||
}
|
||||
@@ -154,7 +158,7 @@ public class UnfoldAnimationController implements UnfoldListener {
|
||||
final boolean isCurrentlyApplicable = animator != null;
|
||||
|
||||
if (isCurrentlyApplicable) {
|
||||
resetTask(animator, taskInfo);
|
||||
maybeResetTask(animator, taskInfo);
|
||||
animator.onTaskVanished(taskInfo);
|
||||
mAnimatorsByTaskId.remove(taskInfo.taskId);
|
||||
}
|
||||
@@ -166,6 +170,7 @@ public class UnfoldAnimationController implements UnfoldListener {
|
||||
return;
|
||||
}
|
||||
|
||||
mIsInStageChange = true;
|
||||
SurfaceControl.Transaction transaction = null;
|
||||
for (int i = 0; i < mAnimators.size(); i++) {
|
||||
final UnfoldTaskAnimator animator = mAnimators.get(i);
|
||||
@@ -219,11 +224,12 @@ public class UnfoldAnimationController implements UnfoldListener {
|
||||
transaction.apply();
|
||||
|
||||
mTransactionPool.release(transaction);
|
||||
mIsInStageChange = false;
|
||||
}
|
||||
|
||||
private void resetTask(UnfoldTaskAnimator animator, TaskInfo taskInfo) {
|
||||
if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
|
||||
// PiP task has its own cleanup path, ignore surface reset to avoid conflict.
|
||||
private void maybeResetTask(UnfoldTaskAnimator animator, TaskInfo taskInfo) {
|
||||
if (!mIsInStageChange) {
|
||||
// No need to resetTask if there is no ongoing state change.
|
||||
return;
|
||||
}
|
||||
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
|
||||
|
||||
@@ -68,11 +68,11 @@ public class LetterboxEduDialogLayoutTest extends ShellTestCase {
|
||||
|
||||
@Test
|
||||
public void testOnFinishInflate() {
|
||||
assertEquals(mLayout.getDialogContainer(),
|
||||
assertEquals(mLayout.getDialogContainerView(),
|
||||
mLayout.findViewById(R.id.letterbox_education_dialog_container));
|
||||
assertEquals(mLayout.getDialogTitle(),
|
||||
mLayout.findViewById(R.id.letterbox_education_dialog_title));
|
||||
assertEquals(mLayout.getBackgroundDim(), mLayout.getBackground());
|
||||
assertEquals(mLayout.getBackgroundDimDrawable(), mLayout.getBackground());
|
||||
assertEquals(mLayout.getBackground().getAlpha(), 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ import com.android.wm.shell.ShellTestCase;
|
||||
import com.android.wm.shell.common.DisplayLayout;
|
||||
import com.android.wm.shell.common.DockStateReader;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.compatui.DialogAnimationController;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
|
||||
import org.junit.After;
|
||||
@@ -98,7 +99,7 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
|
||||
@Captor
|
||||
private ArgumentCaptor<Runnable> mRunOnIdleCaptor;
|
||||
|
||||
@Mock private LetterboxEduAnimationController mAnimationController;
|
||||
@Mock private DialogAnimationController<LetterboxEduDialogLayout> mAnimationController;
|
||||
@Mock private SyncTransactionQueue mSyncTransactionQueue;
|
||||
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
|
||||
@Mock private SurfaceControlViewHost mViewHost;
|
||||
@@ -366,7 +367,7 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
|
||||
assertThat(params.width).isEqualTo(expectedWidth);
|
||||
assertThat(params.height).isEqualTo(expectedHeight);
|
||||
MarginLayoutParams dialogParams =
|
||||
(MarginLayoutParams) layout.getDialogContainer().getLayoutParams();
|
||||
(MarginLayoutParams) layout.getDialogContainerView().getLayoutParams();
|
||||
int verticalMargin = (int) mContext.getResources().getDimension(
|
||||
R.dimen.letterbox_education_dialog_margin);
|
||||
assertThat(dialogParams.topMargin).isEqualTo(verticalMargin + expectedExtraTopMargin);
|
||||
|
||||
@@ -31,6 +31,52 @@ license {
|
||||
],
|
||||
}
|
||||
|
||||
// Opt-in configuration for code depending on Jetpack Compose.
|
||||
soong_config_module_type {
|
||||
name: "systemui_compose_java_defaults",
|
||||
module_type: "java_defaults",
|
||||
config_namespace: "ANDROID",
|
||||
bool_variables: ["SYSTEMUI_USE_COMPOSE"],
|
||||
properties: [
|
||||
"srcs",
|
||||
"static_libs",
|
||||
],
|
||||
}
|
||||
|
||||
systemui_compose_java_defaults {
|
||||
name: "SystemUI_compose_defaults",
|
||||
soong_config_variables: {
|
||||
SYSTEMUI_USE_COMPOSE: {
|
||||
// Because files in compose/features/ depend on SystemUI
|
||||
// code, we compile those files when compiling SystemUI-core.
|
||||
// We also compile the ComposeFacade in
|
||||
// compose/facade/enabled/.
|
||||
srcs: [
|
||||
"compose/features/src/**/*.kt",
|
||||
"compose/facade/enabled/src/**/*.kt",
|
||||
],
|
||||
|
||||
// The dependencies needed by SystemUIComposeFeatures,
|
||||
// except for SystemUI-core.
|
||||
// Copied from compose/features/Android.bp.
|
||||
static_libs: [
|
||||
"SystemUIComposeCore",
|
||||
|
||||
"androidx.compose.runtime_runtime",
|
||||
"androidx.compose.material3_material3",
|
||||
"androidx.activity_activity-compose",
|
||||
],
|
||||
|
||||
// By default, Compose is disabled and we compile the ComposeFacade
|
||||
// in compose/facade/disabled/.
|
||||
conditions_default: {
|
||||
srcs: ["compose/facade/disabled/src/**/*.kt"],
|
||||
static_libs: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "SystemUI-proto",
|
||||
|
||||
@@ -68,6 +114,9 @@ filegroup {
|
||||
|
||||
android_library {
|
||||
name: "SystemUI-core",
|
||||
defaults: [
|
||||
"SystemUI_compose_defaults",
|
||||
],
|
||||
srcs: [
|
||||
"src/**/*.kt",
|
||||
"src/**/*.java",
|
||||
@@ -227,6 +276,9 @@ android_library {
|
||||
|
||||
android_library {
|
||||
name: "SystemUI-tests",
|
||||
defaults: [
|
||||
"SystemUI_compose_defaults",
|
||||
],
|
||||
manifest: "tests/AndroidManifest-base.xml",
|
||||
additional_manifests: ["tests/AndroidManifest.xml"],
|
||||
srcs: [
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.compose.runtime
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.InternalComposeApi
|
||||
import androidx.compose.runtime.MovableContent
|
||||
import androidx.compose.runtime.currentComposer
|
||||
|
||||
/**
|
||||
* An overload of [androidx.compose.runtime.movableContentOf] with 5 parameters.
|
||||
*
|
||||
* @see androidx.compose.runtime.movableContentOf
|
||||
*/
|
||||
@OptIn(InternalComposeApi::class)
|
||||
fun <P1, P2, P3, P4, P5> movableContentOf(
|
||||
content: @Composable (P1, P2, P3, P4, P5) -> Unit
|
||||
): @Composable (P1, P2, P3, P4, P5) -> Unit {
|
||||
val movableContent =
|
||||
MovableContent<Pair<Triple<P1, P2, P3>, Pair<P4, P5>>> {
|
||||
content(
|
||||
it.first.first,
|
||||
it.first.second,
|
||||
it.first.third,
|
||||
it.second.first,
|
||||
it.second.second,
|
||||
)
|
||||
}
|
||||
return { p1, p2, p3, p4, p5 ->
|
||||
currentComposer.insertMovableContent(movableContent, Triple(p1, p2, p3) to (p4 to p5))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.compose
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
|
||||
|
||||
/** The Compose facade, when Compose is *not* available. */
|
||||
object ComposeFacade : BaseComposeFacade {
|
||||
override fun isComposeAvailable(): Boolean = false
|
||||
|
||||
override fun setPeopleSpaceActivityContent(
|
||||
activity: ComponentActivity,
|
||||
viewModel: PeopleViewModel,
|
||||
onResult: (PeopleViewModel.Result) -> Unit,
|
||||
) {
|
||||
throwComposeUnavailableError()
|
||||
}
|
||||
|
||||
private fun throwComposeUnavailableError() {
|
||||
error(
|
||||
"Compose is not available. Make sure to check isComposeAvailable() before calling any" +
|
||||
" other function on ComposeFacade."
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.compose
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import com.android.systemui.compose.theme.SystemUITheme
|
||||
import com.android.systemui.people.ui.compose.PeopleScreen
|
||||
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
|
||||
|
||||
/** The Compose facade, when Compose is available. */
|
||||
object ComposeFacade : BaseComposeFacade {
|
||||
override fun isComposeAvailable(): Boolean = true
|
||||
|
||||
override fun setPeopleSpaceActivityContent(
|
||||
activity: ComponentActivity,
|
||||
viewModel: PeopleViewModel,
|
||||
onResult: (PeopleViewModel.Result) -> Unit,
|
||||
) {
|
||||
activity.setContent { SystemUITheme { PeopleScreen(viewModel, onResult) } }
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ android_library {
|
||||
|
||||
"androidx.compose.runtime_runtime",
|
||||
"androidx.compose.material3_material3",
|
||||
"androidx.activity_activity-compose",
|
||||
],
|
||||
|
||||
kotlincflags: ["-Xjvm-default=all"],
|
||||
|
||||
@@ -139,11 +139,20 @@ private fun PeopleScreenWithConversations(
|
||||
bottom = PeopleSpacePadding,
|
||||
start = 8.dp,
|
||||
end = 8.dp,
|
||||
)
|
||||
),
|
||||
) {
|
||||
ConversationList(R.string.priority_conversations, priorityTiles, onTileClicked)
|
||||
item { Spacer(Modifier.height(35.dp)) }
|
||||
ConversationList(R.string.recent_conversations, recentTiles, onTileClicked)
|
||||
val hasPriorityConversations = priorityTiles.isNotEmpty()
|
||||
if (hasPriorityConversations) {
|
||||
ConversationList(R.string.priority_conversations, priorityTiles, onTileClicked)
|
||||
}
|
||||
|
||||
if (recentTiles.isNotEmpty()) {
|
||||
if (hasPriorityConversations) {
|
||||
item { Spacer(Modifier.height(35.dp)) }
|
||||
}
|
||||
|
||||
ConversationList(R.string.recent_conversations, recentTiles, onTileClicked)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.compose
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import com.android.systemui.people.ui.viewmodel.PeopleViewModel
|
||||
|
||||
/**
|
||||
* A facade to interact with Compose, when it is available.
|
||||
*
|
||||
* You should access this facade by calling the static methods on
|
||||
* [com.android.systemui.compose.ComposeFacade] directly.
|
||||
*/
|
||||
interface BaseComposeFacade {
|
||||
/**
|
||||
* Whether Compose is currently available. This function should be checked before calling any
|
||||
* other functions on this facade.
|
||||
*
|
||||
* This value will never change at runtime.
|
||||
*/
|
||||
fun isComposeAvailable(): Boolean
|
||||
|
||||
/** Bind the content of [activity] to [viewModel]. */
|
||||
fun setPeopleSpaceActivityContent(
|
||||
activity: ComponentActivity,
|
||||
viewModel: PeopleViewModel,
|
||||
onResult: (PeopleViewModel.Result) -> Unit,
|
||||
)
|
||||
}
|
||||
@@ -27,11 +27,15 @@ import android.view.ViewGroup;
|
||||
import androidx.activity.ComponentActivity;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.android.systemui.compose.ComposeFacade;
|
||||
import com.android.systemui.people.ui.view.PeopleViewBinder;
|
||||
import com.android.systemui.people.ui.viewmodel.PeopleViewModel;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
|
||||
/** People Tile Widget configuration activity that shows the user their conversation tiles. */
|
||||
public class PeopleSpaceActivity extends ComponentActivity {
|
||||
|
||||
@@ -58,13 +62,18 @@ public class PeopleSpaceActivity extends ComponentActivity {
|
||||
int widgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID);
|
||||
viewModel.onWidgetIdChanged(widgetId);
|
||||
|
||||
ViewGroup view = PeopleViewBinder.create(this);
|
||||
PeopleViewBinder.bind(view, viewModel, /* lifecycleOwner= */ this,
|
||||
(result) -> {
|
||||
finishActivity(result);
|
||||
return null;
|
||||
});
|
||||
setContentView(view);
|
||||
Function1<PeopleViewModel.Result, Unit> onResult = (result) -> {
|
||||
finishActivity(result);
|
||||
return null;
|
||||
};
|
||||
|
||||
if (ComposeFacade.INSTANCE.isComposeAvailable()) {
|
||||
ComposeFacade.INSTANCE.setPeopleSpaceActivityContent(this, viewModel, onResult);
|
||||
} else {
|
||||
ViewGroup view = PeopleViewBinder.create(this);
|
||||
PeopleViewBinder.bind(view, viewModel, /* lifecycleOwner= */ this, onResult);
|
||||
setContentView(view);
|
||||
}
|
||||
}
|
||||
|
||||
private void finishActivity(PeopleViewModel.Result result) {
|
||||
|
||||
Reference in New Issue
Block a user