diff options
Diffstat (limited to 'libs')
305 files changed, 7588 insertions, 1523 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java index 69a68c85a2fe..4ce294213526 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java @@ -24,6 +24,7 @@ import android.util.Log; import androidx.annotation.NonNull; +import java.util.ArrayList; import java.util.List; /** @@ -57,7 +58,7 @@ class BackupHelper { void scheduleBackup() { if (!mBackupIdlerScheduled) { mBackupIdlerScheduled = true; - Looper.myQueue().addIdleHandler(mBackupIdler); + Looper.getMainLooper().getQueue().addIdleHandler(mBackupIdler); } } @@ -80,9 +81,14 @@ class BackupHelper { } if (DEBUG) Log.d(TAG, "Start to back up " + taskContainers); + final List<ParcelableTaskContainerData> parcelableTaskContainerDataList = new ArrayList<>( + taskContainers.size()); + for (TaskContainer taskContainer : taskContainers) { + parcelableTaskContainerDataList.add(taskContainer.getParcelableData()); + } final Bundle state = new Bundle(); - state.setClassLoader(TaskContainer.class.getClassLoader()); - state.putParcelableList(KEY_TASK_CONTAINERS, taskContainers); + state.setClassLoader(ParcelableTaskContainerData.class.getClassLoader()); + state.putParcelableList(KEY_TASK_CONTAINERS, parcelableTaskContainerDataList); mController.setSavedState(state); } @@ -91,10 +97,12 @@ class BackupHelper { return; } - final List<TaskContainer> taskContainers = savedState.getParcelableArrayList( - KEY_TASK_CONTAINERS, TaskContainer.class); - for (TaskContainer taskContainer : taskContainers) { - if (DEBUG) Log.d(TAG, "restore task " + taskContainer.getTaskId()); + final List<ParcelableTaskContainerData> parcelableTaskContainerDataList = + savedState.getParcelableArrayList(KEY_TASK_CONTAINERS, + ParcelableTaskContainerData.class); + for (ParcelableTaskContainerData data : parcelableTaskContainerDataList) { + final TaskContainer taskContainer = new TaskContainer(data, mController); + if (DEBUG) Log.d(TAG, "Restoring task " + taskContainer.getTaskId()); // TODO(b/289875940): implement the TaskContainer restoration. } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java index 1eb95c1efb08..9ea2943bc6da 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java @@ -70,6 +70,10 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { @NonNull private final TaskFragmentCallback mCallback; + @VisibleForTesting + @Nullable + TaskFragmentAnimationController mAnimationController; + /** * Callback that notifies the controller about changes to task fragments. */ @@ -87,6 +91,25 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { mCallback = callback; } + @Override + public void unregisterOrganizer() { + if (mAnimationController != null) { + mAnimationController.unregisterRemoteAnimations(); + mAnimationController = null; + } + super.unregisterOrganizer(); + } + + /** + * Overrides the animation for transitions of embedded activities organized by this organizer. + */ + void overrideSplitAnimation() { + if (mAnimationController == null) { + mAnimationController = new TaskFragmentAnimationController(this); + } + mAnimationController.registerRemoteAnimations(); + } + /** * Starts a new Activity and puts it into split with an existing Activity side-by-side. * @param launchingFragmentToken token for the launching TaskFragment. If it exists, it will diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java new file mode 100644 index 000000000000..817cfce69b2e --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2024 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 androidx.window.extensions.embedding; + +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * This class holds the Parcelable data of a {@link SplitContainer}. + */ +class ParcelableSplitContainerData implements Parcelable { + + /** + * A reference to the target {@link SplitContainer} that owns the data. This will not be + * parcelled and will be {@code null} when the data is created from a parcel. + */ + @Nullable + final SplitContainer mSplitContainer; + + @NonNull + final IBinder mToken; + + @NonNull + private final IBinder mPrimaryContainerToken; + + @NonNull + private final IBinder mSecondaryContainerToken; + + // TODO(b/289875940): making this as non-null once the tag can be auto-generated from the rule. + @Nullable + final String mSplitRuleTag; + + /** + * Whether the selection of which container is primary can be changed at runtime. Runtime + * updates is currently possible only for {@link SplitPinContainer} + * + * @see SplitPinContainer + */ + final boolean mIsPrimaryContainerMutable; + + ParcelableSplitContainerData(@NonNull SplitContainer splitContainer, @NonNull IBinder token, + @NonNull IBinder primaryContainerToken, @NonNull IBinder secondaryContainerToken, + @Nullable String splitRuleTag, boolean isPrimaryContainerMutable) { + mSplitContainer = splitContainer; + mToken = token; + mPrimaryContainerToken = primaryContainerToken; + mSecondaryContainerToken = secondaryContainerToken; + mSplitRuleTag = splitRuleTag; + mIsPrimaryContainerMutable = isPrimaryContainerMutable; + } + + private ParcelableSplitContainerData(Parcel in) { + mSplitContainer = null; + mToken = in.readStrongBinder(); + mPrimaryContainerToken = in.readStrongBinder(); + mSecondaryContainerToken = in.readStrongBinder(); + mSplitRuleTag = in.readString(); + mIsPrimaryContainerMutable = in.readBoolean(); + } + + public static final Creator<ParcelableSplitContainerData> CREATOR = new Creator<>() { + @Override + public ParcelableSplitContainerData createFromParcel(Parcel in) { + return new ParcelableSplitContainerData(in); + } + + @Override + public ParcelableSplitContainerData[] newArray(int size) { + return new ParcelableSplitContainerData[size]; + } + }; + + @NonNull + private IBinder getPrimaryContainerToken() { + return mSplitContainer != null ? mSplitContainer.getPrimaryContainer().getToken() + : mPrimaryContainerToken; + } + + @NonNull + private IBinder getSecondaryContainerToken() { + return mSplitContainer != null ? mSplitContainer.getSecondaryContainer().getToken() + : mSecondaryContainerToken; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongBinder(mToken); + dest.writeStrongBinder(getPrimaryContainerToken()); + dest.writeStrongBinder(getSecondaryContainerToken()); + dest.writeString(mSplitRuleTag); + dest.writeBoolean(mIsPrimaryContainerMutable); + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java new file mode 100644 index 000000000000..7377d005cda4 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2024 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 androidx.window.extensions.embedding; + +import static android.app.ActivityTaskManager.INVALID_TASK_ID; + +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class holds the Parcelable data of a {@link TaskContainer}. + */ +class ParcelableTaskContainerData implements Parcelable { + + /** + * A reference to the target {@link TaskContainer} that owns the data. This will not be + * parcelled and will be {@code null} when the data is created from a parcel. + */ + @Nullable + final TaskContainer mTaskContainer; + + /** + * The unique task id. + */ + final int mTaskId; + + /** + * The parcelable data of the active TaskFragmentContainers in this Task. + * Note that this will only be populated before parcelling, and will not be copied when + * making a new instance copy. + */ + @NonNull + private final List<ParcelableTaskFragmentContainerData> + mParcelableTaskFragmentContainerDataList = new ArrayList<>(); + + /** + * The parcelable data of the SplitContainers in this Task. + * Note that this will only be populated before parcelling, and will not be copied when + * making a new instance copy. + */ + @NonNull + private final List<ParcelableSplitContainerData> mParcelableSplitContainerDataList = + new ArrayList<>(); + + ParcelableTaskContainerData(int taskId, @NonNull TaskContainer taskContainer) { + if (taskId == INVALID_TASK_ID) { + throw new IllegalArgumentException("Invalid Task id"); + } + + mTaskId = taskId; + mTaskContainer = taskContainer; + } + + ParcelableTaskContainerData(@NonNull ParcelableTaskContainerData data, + @NonNull TaskContainer taskContainer) { + mTaskId = data.mTaskId; + mTaskContainer = taskContainer; + } + + private ParcelableTaskContainerData(Parcel in) { + mTaskId = in.readInt(); + mTaskContainer = null; + in.readParcelableList(mParcelableTaskFragmentContainerDataList, + ParcelableTaskFragmentContainerData.class.getClassLoader(), + ParcelableTaskFragmentContainerData.class); + in.readParcelableList(mParcelableSplitContainerDataList, + ParcelableSplitContainerData.class.getClassLoader(), + ParcelableSplitContainerData.class); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mTaskId); + dest.writeParcelableList(getParcelableTaskFragmentContainerDataList(), flags); + dest.writeParcelableList(getParcelableSplitContainerDataList(), flags); + } + + @NonNull + List<? extends ParcelableTaskFragmentContainerData> + getParcelableTaskFragmentContainerDataList() { + return mTaskContainer != null ? mTaskContainer.getParcelableTaskFragmentContainerDataList() + : mParcelableTaskFragmentContainerDataList; + } + + @NonNull + List<? extends ParcelableSplitContainerData> getParcelableSplitContainerDataList() { + return mTaskContainer != null ? mTaskContainer.getParcelableSplitContainerDataList() + : mParcelableSplitContainerDataList; + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator<ParcelableTaskContainerData> CREATOR = new Creator<>() { + @Override + public ParcelableTaskContainerData createFromParcel(Parcel in) { + return new ParcelableTaskContainerData(in); + } + + @Override + public ParcelableTaskContainerData[] newArray(int size) { + return new ParcelableTaskContainerData[size]; + } + }; +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java new file mode 100644 index 000000000000..a79a89a210ac --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2024 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 androidx.window.extensions.embedding; + +import android.app.Activity; +import android.graphics.Rect; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * This class holds the Parcelable data of a {@link TaskFragmentContainer}. + */ +class ParcelableTaskFragmentContainerData implements Parcelable { + + /** + * Client-created token that uniquely identifies the task fragment container instance. + */ + @NonNull + final IBinder mToken; + + /** + * The tag specified in launch options. {@code null} if this taskFragment container is not an + * overlay container. + */ + @Nullable + final String mOverlayTag; + + /** + * The associated {@link Activity#getActivityToken()} of the overlay container. + * Must be {@code null} for non-overlay container. + * <p> + * If an overlay container is associated with an activity, this overlay container will be + * dismissed when the associated activity is destroyed. If the overlay container is visible, + * activity will be launched on top of the overlay container and expanded to fill the parent + * container. + */ + @Nullable + final IBinder mAssociatedActivityToken; + + /** + * Bounds that were requested last via {@link android.window.WindowContainerTransaction}. + */ + @NonNull + final Rect mLastRequestedBounds; + + ParcelableTaskFragmentContainerData(@NonNull IBinder token, @Nullable String overlayTag, + @Nullable IBinder associatedActivityToken) { + mToken = token; + mOverlayTag = overlayTag; + mAssociatedActivityToken = associatedActivityToken; + mLastRequestedBounds = new Rect(); + } + + private ParcelableTaskFragmentContainerData(Parcel in) { + mToken = in.readStrongBinder(); + mOverlayTag = in.readString(); + mAssociatedActivityToken = in.readStrongBinder(); + mLastRequestedBounds = in.readTypedObject(Rect.CREATOR); + } + + public static final Creator<ParcelableTaskFragmentContainerData> CREATOR = new Creator<>() { + @Override + public ParcelableTaskFragmentContainerData createFromParcel(Parcel in) { + return new ParcelableTaskFragmentContainerData(in); + } + + @Override + public ParcelableTaskFragmentContainerData[] newArray(int size) { + return new ParcelableTaskFragmentContainerData[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongBinder(mToken); + dest.writeString(mOverlayTag); + dest.writeStrongBinder(mAssociatedActivityToken); + dest.writeTypedObject(mLastRequestedBounds, flags); + } + +} + diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java index 39cfacec8447..6d436ec01d98 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java @@ -33,6 +33,8 @@ import androidx.window.extensions.core.util.function.Function; */ class SplitContainer { @NonNull + private final ParcelableSplitContainerData mParcelableData; + @NonNull private TaskFragmentContainer mPrimaryContainer; @NonNull private final TaskFragmentContainer mSecondaryContainer; @@ -44,16 +46,6 @@ class SplitContainer { /** @see SplitContainer#getDefaultSplitAttributes() */ @NonNull private SplitAttributes mDefaultSplitAttributes; - @NonNull - private final IBinder mToken; - - /** - * Whether the selection of which container is primary can be changed at runtime. Runtime - * updates is currently possible only for {@link SplitPinContainer} - * - * @see SplitPinContainer - */ - private final boolean mIsPrimaryContainerMutable; SplitContainer(@NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @@ -69,13 +61,14 @@ class SplitContainer { @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule, @NonNull SplitAttributes splitAttributes, boolean isPrimaryContainerMutable) { + mParcelableData = new ParcelableSplitContainerData(this, new Binder("SplitContainer"), + primaryContainer.getToken(), secondaryContainer.getToken(), splitRule.getTag(), + isPrimaryContainerMutable); mPrimaryContainer = primaryContainer; mSecondaryContainer = secondaryContainer; mSplitRule = splitRule; mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes(); mCurrentSplitAttributes = splitAttributes; - mToken = new Binder("SplitContainer"); - mIsPrimaryContainerMutable = isPrimaryContainerMutable; if (shouldFinishPrimaryWithSecondary(splitRule)) { if (mPrimaryContainer.getRunningActivityCount() == 1 @@ -94,7 +87,7 @@ class SplitContainer { } void setPrimaryContainer(@NonNull TaskFragmentContainer primaryContainer) { - if (!mIsPrimaryContainerMutable) { + if (!mParcelableData.mIsPrimaryContainerMutable) { throw new IllegalStateException("Cannot update primary TaskFragmentContainer"); } mPrimaryContainer = primaryContainer; @@ -150,7 +143,12 @@ class SplitContainer { @NonNull IBinder getToken() { - return mToken; + return mParcelableData.mToken; + } + + @NonNull + ParcelableSplitContainerData getParcelableData() { + return mParcelableData; } /** @@ -201,7 +199,7 @@ class SplitContainer { return null; } return new SplitInfo(primaryActivityStack, secondaryActivityStack, - mCurrentSplitAttributes, SplitInfo.Token.createFromBinder(mToken)); + mCurrentSplitAttributes, SplitInfo.Token.createFromBinder(mParcelableData.mToken)); } static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index bb384c5d0e84..f2f2b7ea7174 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -58,18 +58,22 @@ import android.app.Activity; import android.app.ActivityClient; import android.app.ActivityOptions; import android.app.ActivityThread; +import android.app.AppGlobals; import android.app.Application; import android.app.Instrumentation; import android.app.servertransaction.ClientTransactionListenerController; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.RemoteException; +import android.os.SystemProperties; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -116,6 +120,7 @@ import java.util.function.BiConsumer; public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback, ActivityEmbeddingComponent, DividerPresenter.DragEventCallback { static final String TAG = "SplitController"; + static final boolean ENABLE_SHELL_TRANSITIONS = getShellTransitEnabled(); // TODO(b/243518738): Move to WM Extensions if we have requirement of overlay without // association. It's not set in WM Extensions nor Wm Jetpack library currently. @@ -906,7 +911,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (taskContainer.isVisible()) { updateContainersInTask(wct, taskContainer); - } else if (Flags.fixNoContainerUpdateWithoutResize()) { + } else { // the TaskFragmentContainers need to be updated when the task becomes visible taskContainer.mTaskFragmentContainersNeedsUpdate = true; } @@ -919,7 +924,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // Update all TaskFragments in the Task. Make a copy of the list since some may be // removed on updating. - final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers(); + final List<TaskFragmentContainer> containers + = new ArrayList<>(taskContainer.getTaskFragmentContainers()); for (int i = containers.size() - 1; i >= 0; i--) { final TaskFragmentContainer container = containers.get(i); // Wait until onTaskFragmentAppeared to update new container. @@ -3307,4 +3313,17 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen transactionRecord.apply(false /* shouldApplyIndependently */); } } + + // TODO(b/207070762): cleanup with legacy app transition + private static boolean getShellTransitEnabled() { + try { + if (AppGlobals.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_AUTOMOTIVE, 0)) { + return SystemProperties.getBoolean("persist.wm.debug.shell_transit", true); + } + } catch (RemoteException re) { + Log.w(TAG, "Error getting system features"); + } + return true; + } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index fb8efc4ad490..abc7b291fc32 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -169,12 +169,17 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { mController = controller; final Bundle outSavedState = new Bundle(); if (Flags.aeBackStackRestore()) { - outSavedState.setClassLoader(TaskContainer.class.getClassLoader()); + outSavedState.setClassLoader(ParcelableTaskContainerData.class.getClassLoader()); registerOrganizer(false /* isSystemOrganizer */, outSavedState); } else { registerOrganizer(); } mBackupHelper = new BackupHelper(controller, outSavedState); + if (!SplitController.ENABLE_SHELL_TRANSITIONS) { + // TODO(b/207070762): cleanup with legacy app transition + // Animation will be handled by WM Shell when Shell transition is enabled. + overrideSplitAnimation(); + } } void scheduleBackup() { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index 5795e8da18c2..608a3bee7509 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -16,7 +16,6 @@ package androidx.window.extensions.embedding; -import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -32,8 +31,6 @@ import android.app.WindowConfiguration.WindowingMode; import android.content.res.Configuration; import android.graphics.Rect; import android.os.IBinder; -import android.os.Parcel; -import android.os.Parcelable; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.Log; @@ -57,11 +54,12 @@ import java.util.List; import java.util.Set; /** Represents TaskFragments and split pairs below a Task. */ -class TaskContainer implements Parcelable { +class TaskContainer { private static final String TAG = TaskContainer.class.getSimpleName(); - /** The unique task id. */ - private final int mTaskId; + /** Parcelable data of this TaskContainer. */ + @NonNull + private final ParcelableTaskContainerData mParcelableTaskContainerData; /** Active TaskFragments in this Task. */ @NonNull @@ -130,16 +128,15 @@ class TaskContainer implements Parcelable { * @param splitController The {@link SplitController}. */ TaskContainer(int taskId, @NonNull Activity activityInTask, - @Nullable SplitController splitController) { - if (taskId == INVALID_TASK_ID) { - throw new IllegalArgumentException("Invalid Task id"); - } - mTaskId = taskId; + @NonNull SplitController splitController) { + mParcelableTaskContainerData = new ParcelableTaskContainerData(taskId, this); + final TaskProperties taskProperties = TaskProperties .getTaskPropertiesFromActivity(activityInTask); mInfo = new TaskFragmentParentInfo( taskProperties.getConfiguration(), taskProperties.getDisplayId(), + taskId, // Note that it is always called when there's a new Activity is started, which // implies the host task is visible and has an activity in the task. true /* visible */, @@ -148,8 +145,44 @@ class TaskContainer implements Parcelable { mSplitController = splitController; } + /** This is only used when restoring it from a {@link ParcelableTaskContainerData}. */ + TaskContainer(@NonNull ParcelableTaskContainerData data, + @NonNull SplitController splitController) { + mParcelableTaskContainerData = new ParcelableTaskContainerData(data, this); + mSplitController = splitController; + for (ParcelableTaskFragmentContainerData tfData : + data.getParcelableTaskFragmentContainerDataList()) { + final TaskFragmentContainer container = + new TaskFragmentContainer(tfData, splitController, this); + mContainers.add(container); + } + } + + @NonNull + ParcelableTaskContainerData getParcelableData() { + return mParcelableTaskContainerData; + } + + @NonNull + List<ParcelableTaskFragmentContainerData> getParcelableTaskFragmentContainerDataList() { + final List<ParcelableTaskFragmentContainerData> data = new ArrayList<>(mContainers.size()); + for (TaskFragmentContainer container : mContainers) { + data.add(container.getParcelableData()); + } + return data; + } + + @NonNull + List<ParcelableSplitContainerData> getParcelableSplitContainerDataList() { + final List<ParcelableSplitContainerData> data = new ArrayList<>(mSplitContainers.size()); + for (SplitContainer splitContainer : mSplitContainers) { + data.add(splitContainer.getParcelableData()); + } + return data; + } + int getTaskId() { - return mTaskId; + return mParcelableTaskContainerData.mTaskId; } int getDisplayId() { @@ -162,7 +195,8 @@ class TaskContainer implements Parcelable { void setInvisible() { mInfo = new TaskFragmentParentInfo(mInfo.getConfiguration(), mInfo.getDisplayId(), - false /* visible */, mInfo.hasDirectActivity(), mInfo.getDecorSurface()); + mInfo.getTaskId(), false /* visible */, mInfo.hasDirectActivity(), + mInfo.getDecorSurface()); } boolean hasDirectActivity() { @@ -680,34 +714,6 @@ class TaskContainer implements Parcelable { return activityStacks; } - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mTaskId); - // TODO(b/289875940) - } - - protected TaskContainer(Parcel in) { - mTaskId = in.readInt(); - // TODO(b/289875940) - } - - public static final Creator<TaskContainer> CREATOR = new Creator<>() { - @Override - public TaskContainer createFromParcel(Parcel in) { - return new TaskContainer(in); - } - - @Override - public TaskContainer[] newArray(int size) { - return new TaskContainer[size]; - } - }; - /** A wrapper class which contains the information of {@link TaskContainer} */ static final class TaskProperties { private final int mDisplayId; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java new file mode 100644 index 000000000000..33220c44a3b5 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java @@ -0,0 +1,224 @@ +/* + * 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 androidx.window.extensions.embedding; + +import static android.graphics.Matrix.MTRANS_X; +import static android.graphics.Matrix.MTRANS_Y; +import static android.view.RemoteAnimationTarget.MODE_CLOSING; + +import android.graphics.Point; +import android.graphics.Rect; +import android.view.Choreographer; +import android.view.RemoteAnimationTarget; +import android.view.SurfaceControl; +import android.view.animation.Animation; +import android.view.animation.Transformation; + +import androidx.annotation.NonNull; + +/** + * Wrapper to handle the TaskFragment animation update in one {@link SurfaceControl.Transaction}. + * + * The base adapter can be used for {@link RemoteAnimationTarget} that is simple open/close. + */ +class TaskFragmentAnimationAdapter { + + /** + * If {@link #mOverrideLayer} is set to this value, we don't want to override the surface layer. + */ + private static final int LAYER_NO_OVERRIDE = -1; + + @NonNull + final Animation mAnimation; + @NonNull + final RemoteAnimationTarget mTarget; + @NonNull + final SurfaceControl mLeash; + /** Area in absolute coordinate that the animation surface shouldn't go beyond. */ + @NonNull + private final Rect mWholeAnimationBounds = new Rect(); + /** + * Area in absolute coordinate that should represent all the content to show for this window. + * This should be the end bounds for opening window, and start bounds for closing window in case + * the window is resizing during the open/close transition. + */ + @NonNull + private final Rect mContentBounds = new Rect(); + /** Offset relative to the window parent surface for {@link #mContentBounds}. */ + @NonNull + private final Point mContentRelOffset = new Point(); + + @NonNull + final Transformation mTransformation = new Transformation(); + @NonNull + final float[] mMatrix = new float[9]; + @NonNull + final float[] mVecs = new float[4]; + @NonNull + final Rect mRect = new Rect(); + private boolean mIsFirstFrame = true; + private int mOverrideLayer = LAYER_NO_OVERRIDE; + + TaskFragmentAnimationAdapter(@NonNull Animation animation, + @NonNull RemoteAnimationTarget target) { + this(animation, target, target.leash, target.screenSpaceBounds); + } + + /** + * @param leash the surface to animate. + * @param wholeAnimationBounds area in absolute coordinate that the animation surface shouldn't + * go beyond. + */ + TaskFragmentAnimationAdapter(@NonNull Animation animation, + @NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash, + @NonNull Rect wholeAnimationBounds) { + mAnimation = animation; + mTarget = target; + mLeash = leash; + mWholeAnimationBounds.set(wholeAnimationBounds); + if (target.mode == MODE_CLOSING) { + // When it is closing, we want to show the content at the start position in case the + // window is resizing as well. For example, when the activities is changing from split + // to stack, the bottom TaskFragment will be resized to fullscreen when hiding. + final Rect startBounds = target.startBounds; + final Rect endBounds = target.screenSpaceBounds; + mContentBounds.set(startBounds); + mContentRelOffset.set(target.localBounds.left, target.localBounds.top); + mContentRelOffset.offset( + startBounds.left - endBounds.left, + startBounds.top - endBounds.top); + } else { + mContentBounds.set(target.screenSpaceBounds); + mContentRelOffset.set(target.localBounds.left, target.localBounds.top); + } + } + + /** + * Surface layer to be set at the first frame of the animation. We will not set the layer if it + * is set to {@link #LAYER_NO_OVERRIDE}. + */ + final void overrideLayer(int layer) { + mOverrideLayer = layer; + } + + /** Called on frame update. */ + final void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) { + if (mIsFirstFrame) { + t.show(mLeash); + if (mOverrideLayer != LAYER_NO_OVERRIDE) { + t.setLayer(mLeash, mOverrideLayer); + } + mIsFirstFrame = false; + } + + // Extract the transformation to the current time. + mAnimation.getTransformation(Math.min(currentPlayTime, mAnimation.getDuration()), + mTransformation); + t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId()); + onAnimationUpdateInner(t); + } + + /** To be overridden by subclasses to adjust the animation surface change. */ + void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) { + // Update the surface position and alpha. + mTransformation.getMatrix().postTranslate(mContentRelOffset.x, mContentRelOffset.y); + t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix); + t.setAlpha(mLeash, mTransformation.getAlpha()); + + // Get current surface bounds in absolute coordinate. + // positionX/Y are in local coordinate, so minus the local offset to get the slide amount. + final int positionX = Math.round(mMatrix[MTRANS_X]); + final int positionY = Math.round(mMatrix[MTRANS_Y]); + final Rect cropRect = new Rect(mContentBounds); + cropRect.offset(positionX - mContentRelOffset.x, positionY - mContentRelOffset.y); + + // Store the current offset of the surface top left from (0,0) in absolute coordinate. + final int offsetX = cropRect.left; + final int offsetY = cropRect.top; + + // Intersect to make sure the animation happens within the whole animation bounds. + if (!cropRect.intersect(mWholeAnimationBounds)) { + // Hide the surface when it is outside of the animation area. + t.setAlpha(mLeash, 0); + } + + // cropRect is in absolute coordinate, so we need to translate it to surface top left. + cropRect.offset(-offsetX, -offsetY); + t.setCrop(mLeash, cropRect); + } + + /** Called after animation finished. */ + final void onAnimationEnd(@NonNull SurfaceControl.Transaction t) { + onAnimationUpdate(t, mAnimation.getDuration()); + } + + final long getDurationHint() { + return mAnimation.computeDurationHint(); + } + + /** + * Should be used for the animation of the snapshot of a {@link RemoteAnimationTarget} that has + * size change. + */ + static class SnapshotAdapter extends TaskFragmentAnimationAdapter { + + SnapshotAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) { + // Start leash is the snapshot of the starting surface. + super(animation, target, target.startLeash, target.screenSpaceBounds); + } + + @Override + void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) { + // Snapshot should always be placed at the top left of the animation leash. + mTransformation.getMatrix().postTranslate(0, 0); + t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix); + t.setAlpha(mLeash, mTransformation.getAlpha()); + } + } + + /** + * Should be used for the animation of the {@link RemoteAnimationTarget} that has size change. + */ + static class BoundsChangeAdapter extends TaskFragmentAnimationAdapter { + + BoundsChangeAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) { + super(animation, target); + } + + @Override + void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) { + mTransformation.getMatrix().postTranslate( + mTarget.localBounds.left, mTarget.localBounds.top); + t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix); + t.setAlpha(mLeash, mTransformation.getAlpha()); + + // The following applies an inverse scale to the clip-rect so that it crops "after" the + // scale instead of before. + mVecs[1] = mVecs[2] = 0; + mVecs[0] = mVecs[3] = 1; + mTransformation.getMatrix().mapVectors(mVecs); + mVecs[0] = 1.f / mVecs[0]; + mVecs[3] = 1.f / mVecs[3]; + final Rect clipRect = mTransformation.getClipRect(); + mRect.left = (int) (clipRect.left * mVecs[0] + 0.5f); + mRect.right = (int) (clipRect.right * mVecs[0] + 0.5f); + mRect.top = (int) (clipRect.top * mVecs[3] + 0.5f); + mRect.bottom = (int) (clipRect.bottom * mVecs[3] + 0.5f); + t.setWindowCrop(mLeash, mRect); + } + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java new file mode 100644 index 000000000000..d7eb9a01f57c --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java @@ -0,0 +1,79 @@ +/* + * 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 androidx.window.extensions.embedding; + +import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN; + +import android.util.Log; +import android.view.RemoteAnimationAdapter; +import android.view.RemoteAnimationDefinition; +import android.window.TaskFragmentOrganizer; + +import androidx.annotation.NonNull; + +import com.android.internal.annotations.VisibleForTesting; + +/** Controls the TaskFragment remote animations. */ +class TaskFragmentAnimationController { + + private static final String TAG = "TaskFragAnimationCtrl"; + static final boolean DEBUG = false; + + private final TaskFragmentOrganizer mOrganizer; + private final TaskFragmentAnimationRunner mRemoteRunner = new TaskFragmentAnimationRunner(); + @VisibleForTesting + final RemoteAnimationDefinition mDefinition; + private boolean mIsRegistered; + + TaskFragmentAnimationController(@NonNull TaskFragmentOrganizer organizer) { + mOrganizer = organizer; + mDefinition = new RemoteAnimationDefinition(); + final RemoteAnimationAdapter animationAdapter = + new RemoteAnimationAdapter(mRemoteRunner, 0, 0, true /* changeNeedsSnapshot */); + mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter); + } + + void registerRemoteAnimations() { + if (DEBUG) { + Log.v(TAG, "registerRemoteAnimations"); + } + if (mIsRegistered) { + return; + } + mOrganizer.registerRemoteAnimations(mDefinition); + mIsRegistered = true; + } + + void unregisterRemoteAnimations() { + if (DEBUG) { + Log.v(TAG, "unregisterRemoteAnimations"); + } + if (!mIsRegistered) { + return; + } + mOrganizer.unregisterRemoteAnimations(); + mIsRegistered = false; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java new file mode 100644 index 000000000000..d9b73a8290f5 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java @@ -0,0 +1,309 @@ +/* + * 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 androidx.window.extensions.embedding; + +import static android.os.Process.THREAD_PRIORITY_DISPLAY; +import static android.view.RemoteAnimationTarget.MODE_CHANGING; +import static android.view.RemoteAnimationTarget.MODE_CLOSING; +import static android.view.RemoteAnimationTarget.MODE_OPENING; +import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN; +import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.graphics.Rect; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.RemoteException; +import android.util.Log; +import android.view.IRemoteAnimationFinishedCallback; +import android.view.IRemoteAnimationRunner; +import android.view.RemoteAnimationTarget; +import android.view.SurfaceControl; +import android.view.WindowManager; +import android.view.animation.Animation; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; + +/** To run the TaskFragment animations. */ +class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub { + + private static final String TAG = "TaskFragAnimationRunner"; + private final Handler mHandler; + private final TaskFragmentAnimationSpec mAnimationSpec; + + TaskFragmentAnimationRunner() { + HandlerThread animationThread = new HandlerThread( + "androidx.window.extensions.embedding", THREAD_PRIORITY_DISPLAY); + animationThread.start(); + mHandler = animationThread.getThreadHandler(); + mAnimationSpec = new TaskFragmentAnimationSpec(mHandler); + } + + @Nullable + private Animator mAnimator; + + @Override + public void onAnimationStart(@WindowManager.TransitionOldType int transit, + @NonNull RemoteAnimationTarget[] apps, + @NonNull RemoteAnimationTarget[] wallpapers, + @NonNull RemoteAnimationTarget[] nonApps, + @NonNull IRemoteAnimationFinishedCallback finishedCallback) { + if (wallpapers.length != 0 || nonApps.length != 0) { + throw new IllegalArgumentException("TaskFragment shouldn't handle animation with" + + "wallpaper or non-app windows."); + } + if (TaskFragmentAnimationController.DEBUG) { + Log.v(TAG, "onAnimationStart transit=" + transit); + } + mHandler.post(() -> startAnimation(transit, apps, finishedCallback)); + } + + @Override + public void onAnimationCancelled() { + mHandler.post(this::cancelAnimation); + } + + /** Creates and starts animation. */ + private void startAnimation(@WindowManager.TransitionOldType int transit, + @NonNull RemoteAnimationTarget[] targets, + @NonNull IRemoteAnimationFinishedCallback finishedCallback) { + if (mAnimator != null) { + Log.w(TAG, "start new animation when the previous one is not finished yet."); + mAnimator.cancel(); + } + mAnimator = createAnimator(transit, targets, finishedCallback); + mAnimator.start(); + } + + /** Cancels animation. */ + private void cancelAnimation() { + if (mAnimator == null) { + return; + } + mAnimator.cancel(); + mAnimator = null; + } + + /** Creates the animator given the transition type and windows. */ + @NonNull + private Animator createAnimator(@WindowManager.TransitionOldType int transit, + @NonNull RemoteAnimationTarget[] targets, + @NonNull IRemoteAnimationFinishedCallback finishedCallback) { + final List<TaskFragmentAnimationAdapter> adapters = + createAnimationAdapters(transit, targets); + long duration = 0; + for (TaskFragmentAnimationAdapter adapter : adapters) { + duration = Math.max(duration, adapter.getDurationHint()); + } + final ValueAnimator animator = ValueAnimator.ofFloat(0, 1); + animator.setDuration(duration); + animator.addUpdateListener((anim) -> { + // Update all adapters in the same transaction. + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + for (TaskFragmentAnimationAdapter adapter : adapters) { + adapter.onAnimationUpdate(t, animator.getCurrentPlayTime()); + } + t.apply(); + }); + animator.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) {} + + @Override + public void onAnimationEnd(Animator animation) { + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + for (TaskFragmentAnimationAdapter adapter : adapters) { + adapter.onAnimationEnd(t); + } + t.apply(); + + try { + finishedCallback.onAnimationFinished(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + mAnimator = null; + } + + @Override + public void onAnimationCancel(Animator animation) {} + + @Override + public void onAnimationRepeat(Animator animation) {} + }); + return animator; + } + + /** List of {@link TaskFragmentAnimationAdapter} to handle animations on all window targets. */ + @NonNull + private List<TaskFragmentAnimationAdapter> createAnimationAdapters( + @WindowManager.TransitionOldType int transit, + @NonNull RemoteAnimationTarget[] targets) { + switch (transit) { + case TRANSIT_OLD_ACTIVITY_OPEN: + case TRANSIT_OLD_TASK_FRAGMENT_OPEN: + return createOpenAnimationAdapters(targets); + case TRANSIT_OLD_ACTIVITY_CLOSE: + case TRANSIT_OLD_TASK_FRAGMENT_CLOSE: + return createCloseAnimationAdapters(targets); + case TRANSIT_OLD_TASK_FRAGMENT_CHANGE: + return createChangeAnimationAdapters(targets); + default: + throw new IllegalArgumentException("Unhandled transit type=" + transit); + } + } + + @NonNull + private List<TaskFragmentAnimationAdapter> createOpenAnimationAdapters( + @NonNull RemoteAnimationTarget[] targets) { + return createOpenCloseAnimationAdapters(targets, true /* isOpening */, + mAnimationSpec::loadOpenAnimation); + } + + @NonNull + private List<TaskFragmentAnimationAdapter> createCloseAnimationAdapters( + @NonNull RemoteAnimationTarget[] targets) { + return createOpenCloseAnimationAdapters(targets, false /* isOpening */, + mAnimationSpec::loadCloseAnimation); + } + + /** + * Creates {@link TaskFragmentAnimationAdapter} for OPEN and CLOSE types of transition. + * @param isOpening {@code true} for OPEN type, {@code false} for CLOSE type. + */ + @NonNull + private List<TaskFragmentAnimationAdapter> createOpenCloseAnimationAdapters( + @NonNull RemoteAnimationTarget[] targets, boolean isOpening, + @NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider) { + // We need to know if the target window is only a partial of the whole animation screen. + // If so, we will need to adjust it to make the whole animation screen looks like one. + final List<RemoteAnimationTarget> openingTargets = new ArrayList<>(); + final List<RemoteAnimationTarget> closingTargets = new ArrayList<>(); + final Rect openingWholeScreenBounds = new Rect(); + final Rect closingWholeScreenBounds = new Rect(); + for (RemoteAnimationTarget target : targets) { + if (target.mode != MODE_CLOSING) { + openingTargets.add(target); + openingWholeScreenBounds.union(target.screenSpaceBounds); + } else { + closingTargets.add(target); + closingWholeScreenBounds.union(target.screenSpaceBounds); + // Union the start bounds since this may be the ClosingChanging animation. + closingWholeScreenBounds.union(target.startBounds); + } + } + + // For OPEN transition, open windows should be above close windows. + // For CLOSE transition, open windows should be below close windows. + int offsetLayer = TYPE_LAYER_OFFSET; + final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>(); + for (RemoteAnimationTarget target : openingTargets) { + final TaskFragmentAnimationAdapter adapter = createOpenCloseAnimationAdapter(target, + animationProvider, openingWholeScreenBounds); + if (isOpening) { + adapter.overrideLayer(offsetLayer++); + } + adapters.add(adapter); + } + for (RemoteAnimationTarget target : closingTargets) { + final TaskFragmentAnimationAdapter adapter = createOpenCloseAnimationAdapter(target, + animationProvider, closingWholeScreenBounds); + if (!isOpening) { + adapter.overrideLayer(offsetLayer++); + } + adapters.add(adapter); + } + return adapters; + } + + @NonNull + private TaskFragmentAnimationAdapter createOpenCloseAnimationAdapter( + @NonNull RemoteAnimationTarget target, + @NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider, + @NonNull Rect wholeAnimationBounds) { + final Animation animation = animationProvider.apply(target, wholeAnimationBounds); + return new TaskFragmentAnimationAdapter(animation, target, target.leash, + wholeAnimationBounds); + } + + @NonNull + private List<TaskFragmentAnimationAdapter> createChangeAnimationAdapters( + @NonNull RemoteAnimationTarget[] targets) { + if (shouldUseJumpCutForChangeAnimation(targets)) { + return new ArrayList<>(); + } + + final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>(); + for (RemoteAnimationTarget target : targets) { + if (target.mode == MODE_CHANGING) { + // This is the target with bounds change. + final Animation[] animations = + mAnimationSpec.createChangeBoundsChangeAnimations(target); + // Adapter for the starting snapshot leash. + adapters.add(new TaskFragmentAnimationAdapter.SnapshotAdapter( + animations[0], target)); + // Adapter for the ending bounds changed leash. + adapters.add(new TaskFragmentAnimationAdapter.BoundsChangeAdapter( + animations[1], target)); + continue; + } + + // These are the other targets that don't have bounds change in the same transition. + final Animation animation; + if (target.hasAnimatingParent) { + // No-op if it will be covered by the changing parent window. + animation = TaskFragmentAnimationSpec.createNoopAnimation(target); + } else if (target.mode == MODE_CLOSING) { + animation = mAnimationSpec.createChangeBoundsCloseAnimation(target); + } else { + animation = mAnimationSpec.createChangeBoundsOpenAnimation(target); + } + adapters.add(new TaskFragmentAnimationAdapter(animation, target)); + } + return adapters; + } + + /** + * Whether we should use jump cut for the change transition. + * This normally happens when opening a new secondary with the existing primary using a + * different split layout. This can be complicated, like from horizontal to vertical split with + * new split pairs. + * Uses a jump cut animation to simplify. + */ + private boolean shouldUseJumpCutForChangeAnimation(@NonNull RemoteAnimationTarget[] targets) { + boolean hasOpeningWindow = false; + boolean hasClosingWindow = false; + for (RemoteAnimationTarget target : targets) { + if (target.hasAnimatingParent) { + continue; + } + hasOpeningWindow |= target.mode == MODE_OPENING; + hasClosingWindow |= target.mode == MODE_CLOSING; + } + return hasOpeningWindow && hasClosingWindow; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java new file mode 100644 index 000000000000..1f866c3b99c9 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java @@ -0,0 +1,267 @@ +/* + * 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 androidx.window.extensions.embedding; + +import static android.view.RemoteAnimationTarget.MODE_CLOSING; + +import android.app.ActivityThread; +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.graphics.Rect; +import android.os.Handler; +import android.provider.Settings; +import android.view.RemoteAnimationTarget; +import android.view.WindowManager; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.AnimationSet; +import android.view.animation.AnimationUtils; +import android.view.animation.ClipRectAnimation; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; +import android.view.animation.ScaleAnimation; +import android.view.animation.TranslateAnimation; + +import androidx.annotation.NonNull; + +import com.android.internal.R; +import com.android.internal.policy.AttributeCache; +import com.android.internal.policy.TransitionAnimation; + +/** Animation spec for TaskFragment transition. */ +// TODO(b/206557124): provide an easier way to customize animation +class TaskFragmentAnimationSpec { + + private static final String TAG = "TaskFragAnimationSpec"; + private static final int CHANGE_ANIMATION_DURATION = 517; + private static final int CHANGE_ANIMATION_FADE_DURATION = 80; + private static final int CHANGE_ANIMATION_FADE_OFFSET = 30; + + private final Context mContext; + private final TransitionAnimation mTransitionAnimation; + private final Interpolator mFastOutExtraSlowInInterpolator; + private final LinearInterpolator mLinearInterpolator; + private float mTransitionAnimationScaleSetting; + + TaskFragmentAnimationSpec(@NonNull Handler handler) { + mContext = ActivityThread.currentActivityThread().getApplication(); + mTransitionAnimation = new TransitionAnimation(mContext, false /* debug */, TAG); + // Initialize the AttributeCache for the TransitionAnimation. + AttributeCache.init(mContext); + mFastOutExtraSlowInInterpolator = AnimationUtils.loadInterpolator( + mContext, android.R.interpolator.fast_out_extra_slow_in); + mLinearInterpolator = new LinearInterpolator(); + + // The transition animation should be adjusted based on the developer option. + final ContentResolver resolver = mContext.getContentResolver(); + mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting(); + resolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE), false, + new SettingsObserver(handler)); + } + + /** For target that doesn't need to be animated. */ + @NonNull + static Animation createNoopAnimation(@NonNull RemoteAnimationTarget target) { + // Noop but just keep the target showing/hiding. + final float alpha = target.mode == MODE_CLOSING ? 0f : 1f; + return new AlphaAnimation(alpha, alpha); + } + + /** Animation for target that is opening in a change transition. */ + @NonNull + Animation createChangeBoundsOpenAnimation(@NonNull RemoteAnimationTarget target) { + final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds(); + final Rect bounds = target.screenSpaceBounds; + final int startLeft; + final int startTop; + if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) { + // The window will be animated in from left or right depending on its position. + startTop = 0; + startLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width(); + } else { + // The window will be animated in from top or bottom depending on its position. + startTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height(); + startLeft = 0; + } + + // The position should be 0-based as we will post translate in + // TaskFragmentAnimationAdapter#onAnimationUpdate + final Animation animation = new TranslateAnimation(startLeft, 0, startTop, 0); + animation.setInterpolator(mFastOutExtraSlowInInterpolator); + animation.setDuration(CHANGE_ANIMATION_DURATION); + animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height()); + animation.scaleCurrentDuration(mTransitionAnimationScaleSetting); + return animation; + } + + /** Animation for target that is closing in a change transition. */ + @NonNull + Animation createChangeBoundsCloseAnimation(@NonNull RemoteAnimationTarget target) { + final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds(); + // Use startBounds if the window is closing in case it may also resize. + final Rect bounds = target.startBounds; + final int endTop; + final int endLeft; + if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) { + // The window will be animated out to left or right depending on its position. + endTop = 0; + endLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width(); + } else { + // The window will be animated out to top or bottom depending on its position. + endTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height(); + endLeft = 0; + } + + // The position should be 0-based as we will post translate in + // TaskFragmentAnimationAdapter#onAnimationUpdate + final Animation animation = new TranslateAnimation(0, endLeft, 0, endTop); + animation.setInterpolator(mFastOutExtraSlowInInterpolator); + animation.setDuration(CHANGE_ANIMATION_DURATION); + animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height()); + animation.scaleCurrentDuration(mTransitionAnimationScaleSetting); + return animation; + } + + /** + * Animation for target that is changing (bounds change) in a change transition. + * @return the return array always has two elements. The first one is for the start leash, and + * the second one is for the end leash. + */ + @NonNull + Animation[] createChangeBoundsChangeAnimations(@NonNull RemoteAnimationTarget target) { + // Both start bounds and end bounds are in screen coordinates. We will post translate + // to the local coordinates in TaskFragmentAnimationAdapter#onAnimationUpdate + final Rect startBounds = target.startBounds; + final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds(); + final Rect endBounds = target.screenSpaceBounds; + float scaleX = ((float) startBounds.width()) / endBounds.width(); + float scaleY = ((float) startBounds.height()) / endBounds.height(); + // Start leash is a child of the end leash. Reverse the scale so that the start leash won't + // be scaled up with its parent. + float startScaleX = 1.f / scaleX; + float startScaleY = 1.f / scaleY; + + // The start leash will be fade out. + final AnimationSet startSet = new AnimationSet(false /* shareInterpolator */); + final Animation startAlpha = new AlphaAnimation(1f, 0f); + startAlpha.setInterpolator(mLinearInterpolator); + startAlpha.setDuration(CHANGE_ANIMATION_FADE_DURATION); + startAlpha.setStartOffset(CHANGE_ANIMATION_FADE_OFFSET); + startSet.addAnimation(startAlpha); + final Animation startScale = new ScaleAnimation(startScaleX, startScaleX, startScaleY, + startScaleY); + startScale.setInterpolator(mFastOutExtraSlowInInterpolator); + startScale.setDuration(CHANGE_ANIMATION_DURATION); + startSet.addAnimation(startScale); + startSet.initialize(startBounds.width(), startBounds.height(), endBounds.width(), + endBounds.height()); + startSet.scaleCurrentDuration(mTransitionAnimationScaleSetting); + + // The end leash will be moved into the end position while scaling. + final AnimationSet endSet = new AnimationSet(true /* shareInterpolator */); + endSet.setInterpolator(mFastOutExtraSlowInInterpolator); + final Animation endScale = new ScaleAnimation(scaleX, 1, scaleY, 1); + endScale.setDuration(CHANGE_ANIMATION_DURATION); + endSet.addAnimation(endScale); + // The position should be 0-based as we will post translate in + // TaskFragmentAnimationAdapter#onAnimationUpdate + final Animation endTranslate = new TranslateAnimation(startBounds.left - endBounds.left, 0, + startBounds.top - endBounds.top, 0); + endTranslate.setDuration(CHANGE_ANIMATION_DURATION); + endSet.addAnimation(endTranslate); + // The end leash is resizing, we should update the window crop based on the clip rect. + final Rect startClip = new Rect(startBounds); + final Rect endClip = new Rect(endBounds); + startClip.offsetTo(0, 0); + endClip.offsetTo(0, 0); + final Animation clipAnim = new ClipRectAnimation(startClip, endClip); + clipAnim.setDuration(CHANGE_ANIMATION_DURATION); + endSet.addAnimation(clipAnim); + endSet.initialize(startBounds.width(), startBounds.height(), parentBounds.width(), + parentBounds.height()); + endSet.scaleCurrentDuration(mTransitionAnimationScaleSetting); + + return new Animation[]{startSet, endSet}; + } + + @NonNull + Animation loadOpenAnimation(@NonNull RemoteAnimationTarget target, + @NonNull Rect wholeAnimationBounds) { + final boolean isEnter = target.mode != MODE_CLOSING; + final Animation animation; + // Background color on TaskDisplayArea has already been set earlier in + // WindowContainer#getAnimationAdapter. + if (target.showBackdrop) { + animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter + ? com.android.internal.R.anim.task_fragment_clear_top_open_enter + : com.android.internal.R.anim.task_fragment_clear_top_open_exit); + } else { + animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter + ? com.android.internal.R.anim.task_fragment_open_enter + : com.android.internal.R.anim.task_fragment_open_exit); + } + // Use the whole animation bounds instead of the change bounds, so that when multiple change + // targets are opening at the same time, the animation applied to each will be the same. + // Otherwise, we may see gap between the activities that are launching together. + animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(), + wholeAnimationBounds.width(), wholeAnimationBounds.height()); + animation.scaleCurrentDuration(mTransitionAnimationScaleSetting); + return animation; + } + + @NonNull + Animation loadCloseAnimation(@NonNull RemoteAnimationTarget target, + @NonNull Rect wholeAnimationBounds) { + final boolean isEnter = target.mode != MODE_CLOSING; + final Animation animation; + if (target.showBackdrop) { + animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter + ? com.android.internal.R.anim.task_fragment_clear_top_close_enter + : com.android.internal.R.anim.task_fragment_clear_top_close_exit); + } else { + animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter + ? com.android.internal.R.anim.task_fragment_close_enter + : com.android.internal.R.anim.task_fragment_close_exit); + } + // Use the whole animation bounds instead of the change bounds, so that when multiple change + // targets are closing at the same time, the animation applied to each will be the same. + // Otherwise, we may see gap between the activities that are finishing together. + animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(), + wholeAnimationBounds.width(), wholeAnimationBounds.height()); + animation.scaleCurrentDuration(mTransitionAnimationScaleSetting); + return animation; + } + + private float getTransitionAnimationScaleSetting() { + return WindowManager.fixScale(Settings.Global.getFloat(mContext.getContentResolver(), + Settings.Global.TRANSITION_ANIMATION_SCALE, mContext.getResources().getFloat( + R.dimen.config_appTransitionAnimationDurationScaleDefault))); + } + + private class SettingsObserver extends ContentObserver { + SettingsObserver(@NonNull Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean selfChange) { + mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting(); + } + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index dc6506b070af..dc1d983997c6 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -53,14 +53,12 @@ import java.util.Objects; class TaskFragmentContainer { private static final int APPEAR_EMPTY_TIMEOUT_MS = 3000; + /** Parcelable data of this TaskFragmentContainer. */ @NonNull - private final SplitController mController; + private final ParcelableTaskFragmentContainerData mParcelableData; - /** - * Client-created token that uniquely identifies the task fragment container instance. - */ @NonNull - private final IBinder mToken; + private final SplitController mController; /** Parent leaf Task. */ @NonNull @@ -103,9 +101,6 @@ class TaskFragmentContainer { */ private final List<IBinder> mActivitiesToFinishOnExit = new ArrayList<>(); - @Nullable - private final String mOverlayTag; - /** * The launch options that was used to create this container. Must not {@link Bundle#isEmpty()} * for {@link #isOverlay()} container. @@ -113,29 +108,13 @@ class TaskFragmentContainer { @NonNull private final Bundle mLaunchOptions = new Bundle(); - /** - * The associated {@link Activity#getActivityToken()} of the overlay container. - * Must be {@code null} for non-overlay container. - * <p> - * If an overlay container is associated with an activity, this overlay container will be - * dismissed when the associated activity is destroyed. If the overlay container is visible, - * activity will be launched on top of the overlay container and expanded to fill the parent - * container. - */ - @Nullable - private final IBinder mAssociatedActivityToken; - /** Indicates whether the container was cleaned up after the last activity was removed. */ private boolean mIsFinished; /** - * Bounds that were requested last via {@link android.window.WindowContainerTransaction}. - */ - private final Rect mLastRequestedBounds = new Rect(); - - /** * Windowing mode that was requested last via {@link android.window.WindowContainerTransaction}. */ + // TODO(b/289875940): review this and other field that might need to be moved in the base class. @WindowingMode private int mLastRequestedWindowingMode = WINDOWING_MODE_UNDEFINED; @@ -208,17 +187,17 @@ class TaskFragmentContainer { @NonNull SplitController controller, @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag, @Nullable Bundle launchOptions, @Nullable Activity associatedActivity) { + mParcelableData = new ParcelableTaskFragmentContainerData( + new Binder("TaskFragmentContainer"), overlayTag, + associatedActivity != null ? associatedActivity.getActivityToken() : null); + if ((pendingAppearedActivity == null && pendingAppearedIntent == null) || (pendingAppearedActivity != null && pendingAppearedIntent != null)) { throw new IllegalArgumentException( "One and only one of pending activity and intent must be non-null"); } mController = controller; - mToken = new Binder("TaskFragmentContainer"); mTaskContainer = taskContainer; - mOverlayTag = overlayTag; - mAssociatedActivityToken = associatedActivity != null - ? associatedActivity.getActivityToken() : null; if (launchOptions != null) { mLaunchOptions.putAll(launchOptions); @@ -259,18 +238,26 @@ class TaskFragmentContainer { if (overlayTag != null && pendingAppearedIntent != null && associatedActivity != null && !associatedActivity.isFinishing()) { final IBinder associatedActivityToken = associatedActivity.getActivityToken(); - final OverlayContainerRestoreParams params = new OverlayContainerRestoreParams(mToken, - launchOptions, pendingAppearedIntent); + final OverlayContainerRestoreParams params = new OverlayContainerRestoreParams( + mParcelableData.mToken, launchOptions, pendingAppearedIntent); mController.mOverlayRestoreParams.put(associatedActivityToken, params); } } + /** This is only used when restoring it from a {@link ParcelableTaskFragmentContainerData}. */ + TaskFragmentContainer(@NonNull ParcelableTaskFragmentContainerData data, + @NonNull SplitController splitController, @NonNull TaskContainer taskContainer) { + mParcelableData = data; + mController = splitController; + mTaskContainer = taskContainer; + } + /** * Returns the client-created token that uniquely identifies this container. */ @NonNull IBinder getTaskFragmentToken() { - return mToken; + return mParcelableData.mToken; } /** List of non-finishing activities that belong to this container and live in this process. */ @@ -389,7 +376,8 @@ class TaskFragmentContainer { return null; } return new ActivityStack(activities, isEmpty(), - ActivityStack.Token.createFromBinder(mToken), mOverlayTag); + ActivityStack.Token.createFromBinder(mParcelableData.mToken), + mParcelableData.mOverlayTag); } /** Adds the activity that will be reparented to this container. */ @@ -413,7 +401,7 @@ class TaskFragmentContainer { final ActivityThread.ActivityClientRecord record = ActivityThread .currentActivityThread().getActivityClient(activityToken); if (record != null) { - record.mTaskFragmentToken = mToken; + record.mTaskFragmentToken = mParcelableData.mToken; } } @@ -469,7 +457,7 @@ class TaskFragmentContainer { if (!isOverlayWithActivityAssociation()) { return; } - if (mAssociatedActivityToken == activityToken) { + if (mParcelableData.mAssociatedActivityToken == activityToken) { // If the associated activity is destroyed, also finish this overlay container. mController.mPresenter.cleanupContainer(wct, this, false /* shouldFinishDependent */); } @@ -776,8 +764,8 @@ class TaskFragmentContainer { * @see WindowContainerTransaction#setRelativeBounds */ boolean areLastRequestedBoundsEqual(@Nullable Rect relBounds) { - return (relBounds == null && mLastRequestedBounds.isEmpty()) - || mLastRequestedBounds.equals(relBounds); + return (relBounds == null && mParcelableData.mLastRequestedBounds.isEmpty()) + || mParcelableData.mLastRequestedBounds.equals(relBounds); } /** @@ -787,14 +775,14 @@ class TaskFragmentContainer { */ void setLastRequestedBounds(@Nullable Rect relBounds) { if (relBounds == null) { - mLastRequestedBounds.setEmpty(); + mParcelableData.mLastRequestedBounds.setEmpty(); } else { - mLastRequestedBounds.set(relBounds); + mParcelableData.mLastRequestedBounds.set(relBounds); } } @NonNull Rect getLastRequestedBounds() { - return mLastRequestedBounds; + return mParcelableData.mLastRequestedBounds; } /** @@ -965,6 +953,16 @@ class TaskFragmentContainer { return mTaskContainer.getTaskId(); } + @NonNull + IBinder getToken() { + return mParcelableData.mToken; + } + + @NonNull + ParcelableTaskFragmentContainerData getParcelableData() { + return mParcelableData; + } + /** Gets the parent Task. */ @NonNull TaskContainer getTaskContainer() { @@ -1011,7 +1009,7 @@ class TaskFragmentContainer { /** Returns whether this taskFragment container is an overlay container. */ boolean isOverlay() { - return mOverlayTag != null; + return mParcelableData.mOverlayTag != null; } /** @@ -1020,7 +1018,7 @@ class TaskFragmentContainer { */ @Nullable String getOverlayTag() { - return mOverlayTag; + return mParcelableData.mOverlayTag; } /** @@ -1045,7 +1043,7 @@ class TaskFragmentContainer { */ @Nullable IBinder getAssociatedActivityToken() { - return mAssociatedActivityToken; + return mParcelableData.mAssociatedActivityToken; } /** @@ -1053,11 +1051,11 @@ class TaskFragmentContainer { * a non-fill-parent overlay without activity association. */ boolean isAlwaysOnTopOverlay() { - return isOverlay() && mAssociatedActivityToken == null; + return isOverlay() && mParcelableData.mAssociatedActivityToken == null; } boolean isOverlayWithActivityAssociation() { - return isOverlay() && mAssociatedActivityToken != null; + return isOverlay() && mParcelableData.mAssociatedActivityToken != null; } @Override @@ -1074,13 +1072,13 @@ class TaskFragmentContainer { private String toString(boolean includeContainersToFinishOnExit) { return "TaskFragmentContainer{" + " parentTaskId=" + getTaskId() - + " token=" + mToken + + " token=" + mParcelableData.mToken + " topNonFinishingActivity=" + getTopNonFinishingActivity() + " runningActivityCount=" + getRunningActivityCount() + " isFinished=" + mIsFinished - + " overlayTag=" + mOverlayTag - + " associatedActivityToken=" + mAssociatedActivityToken - + " lastRequestedBounds=" + mLastRequestedBounds + + " overlayTag=" + mParcelableData.mOverlayTag + + " associatedActivityToken=" + mParcelableData.mAssociatedActivityToken + + " lastRequestedBounds=" + mParcelableData.mLastRequestedBounds + " pendingAppearedActivities=" + mPendingAppearedActivities + (includeContainersToFinishOnExit ? " containersToFinishOnExit=" + containersToFinishOnExitToString() : "") diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java index 8911d18b9b97..ac004c301598 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java @@ -23,6 +23,8 @@ import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTest import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -84,6 +86,24 @@ public class JetpackTaskFragmentOrganizerTest { } @Test + public void testUnregisterOrganizer() { + mOrganizer.overrideSplitAnimation(); + mOrganizer.unregisterOrganizer(); + + verify(mOrganizer).unregisterRemoteAnimations(); + } + + @Test + public void testOverrideSplitAnimation() { + assertNull(mOrganizer.mAnimationController); + + mOrganizer.overrideSplitAnimation(); + + assertNotNull(mOrganizer.mAnimationController); + verify(mOrganizer).registerRemoteAnimations(mOrganizer.mAnimationController.mDefinition); + } + + @Test public void testExpandTaskFragment() { final TaskContainer taskContainer = createTestTaskContainer(); doReturn(taskContainer).when(mSplitController).getTaskContainer(anyInt()); diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java index 90eeb583d070..5b97e7e2ca71 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java @@ -549,7 +549,7 @@ public class OverlayPresentationTest { assertThat(taskContainer.getTaskFragmentContainers()).containsExactly(overlayContainer); taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(Configuration.EMPTY, - DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */, + DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */, null /* decorSurface */)); mSplitController.updateOverlayContainer(mTransaction, overlayContainer); @@ -618,7 +618,8 @@ public class OverlayPresentationTest { final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties(); final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo( new Configuration(taskProperties.getConfiguration()), taskProperties.getDisplayId(), - true /* visible */, false /* hasDirectActivity */, null /* decorSurface */); + TASK_ID, true /* visible */, false /* hasDirectActivity */, + null /* decorSurface */); parentInfo.getConfiguration().windowConfiguration.getBounds().offset(10, 10); mSplitController.onTaskFragmentParentInfoChanged(mTransaction, TASK_ID, parentInfo); @@ -642,7 +643,8 @@ public class OverlayPresentationTest { final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties(); final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo( new Configuration(taskProperties.getConfiguration()), taskProperties.getDisplayId(), - true /* visible */, false /* hasDirectActivity */, null /* decorSurface */); + TASK_ID, true /* visible */, false /* hasDirectActivity */, + null /* decorSurface */); mSplitController.onTaskFragmentParentInfoChanged(mTransaction, TASK_ID, parentInfo); diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index d852204b88a8..05124121fe7b 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -1164,7 +1164,7 @@ public class SplitControllerTest { public void testOnTransactionReady_taskFragmentParentInfoChanged() { final TaskFragmentTransaction transaction = new TaskFragmentTransaction(); final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(Configuration.EMPTY, - DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */, + DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */, null /* decorSurface */); transaction.addChange(new TaskFragmentTransaction.Change( TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED) @@ -1625,7 +1625,7 @@ public class SplitControllerTest { final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID); final Configuration configuration = new Configuration(); final TaskFragmentParentInfo originalInfo = new TaskFragmentParentInfo(configuration, - DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */, + DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */, null /* decorSurface */); mSplitController.onTaskFragmentParentInfoChanged(mock(WindowContainerTransaction.class), TASK_ID, originalInfo); @@ -1634,7 +1634,7 @@ public class SplitControllerTest { // Making a public configuration change while the Task is invisible. configuration.densityDpi += 100; final TaskFragmentParentInfo invisibleInfo = new TaskFragmentParentInfo(configuration, - DEFAULT_DISPLAY, false /* visible */, false /* hasDirectActivity */, + DEFAULT_DISPLAY, TASK_ID, false /* visible */, false /* hasDirectActivity */, null /* decorSurface */); mSplitController.onTaskFragmentParentInfoChanged(mock(WindowContainerTransaction.class), TASK_ID, invisibleInfo); @@ -1646,7 +1646,7 @@ public class SplitControllerTest { // Updates when Task to become visible final TaskFragmentParentInfo visibleInfo = new TaskFragmentParentInfo(configuration, - DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */, + DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */, null /* decorSurface */); mSplitController.onTaskFragmentParentInfoChanged(mock(WindowContainerTransaction.class), TASK_ID, visibleInfo); diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java index 284723279b80..97f4d0736312 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java @@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.Display.DEFAULT_DISPLAY; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer; import static org.junit.Assert.assertEquals; @@ -82,7 +83,7 @@ public class TaskContainerTest { configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration, - DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */, + DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */, null /* decorSurface */)); assertEquals(WINDOWING_MODE_MULTI_WINDOW, @@ -90,7 +91,7 @@ public class TaskContainerTest { configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration, - DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */, + DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */, null /* decorSurface */)); assertEquals(WINDOWING_MODE_FREEFORM, @@ -111,14 +112,14 @@ public class TaskContainerTest { configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration, - DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */, + DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */, null /* decorSurface */)); assertFalse(taskContainer.isInPictureInPicture()); configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED); taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration, - DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */, + DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */, null /* decorSurface */)); assertTrue(taskContainer.isInPictureInPicture()); diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java new file mode 100644 index 000000000000..a1e9f08585f6 --- /dev/null +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java @@ -0,0 +1,88 @@ +/* + * 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 androidx.window.extensions.embedding; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static org.mockito.Mockito.never; + +import android.platform.test.annotations.Presubmit; +import android.window.TaskFragmentOrganizer; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +/** + * Test class for {@link TaskFragmentAnimationController}. + * + * Build/Install/Run: + * atest WMJetpackUnitTests:TaskFragmentAnimationControllerTest + */ +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +public class TaskFragmentAnimationControllerTest { + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Mock + private TaskFragmentOrganizer mOrganizer; + private TaskFragmentAnimationController mAnimationController; + + @Before + public void setup() { + mAnimationController = new TaskFragmentAnimationController(mOrganizer); + } + + @Test + public void testRegisterRemoteAnimations() { + mAnimationController.registerRemoteAnimations(); + + verify(mOrganizer).registerRemoteAnimations(mAnimationController.mDefinition); + + mAnimationController.registerRemoteAnimations(); + + // No extra call if it has been registered. + verify(mOrganizer).registerRemoteAnimations(mAnimationController.mDefinition); + } + + @Test + public void testUnregisterRemoteAnimations() { + mAnimationController.unregisterRemoteAnimations(); + + // No call if it is not registered. + verify(mOrganizer, never()).unregisterRemoteAnimations(); + + mAnimationController.registerRemoteAnimations(); + mAnimationController.unregisterRemoteAnimations(); + + verify(mOrganizer).unregisterRemoteAnimations(); + + mAnimationController.unregisterRemoteAnimations(); + + // No extra call if it has been unregistered. + verify(mOrganizer).unregisterRemoteAnimations(); + } +} diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 1a3aa8eec5ec..a79bc97c440c 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -39,20 +39,6 @@ filegroup { path: "src", } -// Sources that have no dependencies that can be used directly downstream of this library -// TODO(b/322791067): move these sources to WindowManager-Shell-shared -filegroup { - name: "wm_shell_util-sources", - srcs: [ - "src/com/android/wm/shell/common/bubbles/*.kt", - "src/com/android/wm/shell/common/bubbles/*.java", - "src/com/android/wm/shell/common/desktopmode/*.kt", - "src/com/android/wm/shell/pip/PipContentOverlay.java", - "src/com/android/wm/shell/util/**/*.java", - ], - path: "src", -} - // Aidls which can be used directly downstream of this library filegroup { name: "wm_shell-aidls", @@ -187,9 +173,11 @@ java_library { ":wm_shell-shared-aidls", ], static_libs: [ + "androidx.core_core-animation", "androidx.dynamicanimation_dynamicanimation", "jsr330", ], + kotlincflags: ["-Xjvm-default=all"], } java_library { @@ -208,13 +196,13 @@ android_library { // TODO(b/168581922) protologtool do not support kotlin(*.kt) ":wm_shell-sources-kt", ":wm_shell-aidls", + ":wm_shell-shared-aidls", ], resource_dirs: [ "res", ], static_libs: [ "androidx.appcompat_appcompat", - "androidx.core_core-animation", "androidx.core_core-ktx", "androidx.arch.core_core-runtime", "androidx.datastore_datastore", diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig index 9de10c0619da..df1a98c4b417 100644 --- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig +++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig @@ -138,3 +138,17 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "enable_bubble_to_fullscreen" + namespace: "multitasking" + description: "Enable an option to move bubbles to fullscreen" + bug: "363326492" +} + +flag { + name: "enable_flexible_split" + namespace: "multitasking" + description: "Enables flexibile split feature for split screen" + bug: "349828130" +} diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png Binary files differindex 027b28e7ace7..991cdcf09416 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png Binary files differindex 027b28e7ace7..991cdcf09416 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png Binary files differindex e02c89ae07bd..c72944222e66 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png Binary files differindex e02c89ae07bd..c72944222e66 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt index d35f493a8f60..f09969d253d3 100644 --- a/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt +++ b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt @@ -16,7 +16,7 @@ package com.android.wm.shell.bubbles import android.view.LayoutInflater -import com.android.wm.shell.common.bubbles.BubblePopupView +import com.android.wm.shell.shared.bubbles.BubblePopupView import com.android.wm.shell.testing.goldenpathmanager.WMShellGoldenPathManager import com.android.wm.shell.R import org.junit.Rule diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt index 4b97451a0c41..b38d00da6dfa 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt @@ -30,7 +30,7 @@ import androidx.test.filters.SmallTest import com.android.internal.protolog.ProtoLog import com.android.wm.shell.R import com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT -import com.android.wm.shell.common.bubbles.BubbleBarLocation +import com.android.wm.shell.shared.bubbles.BubbleBarLocation import com.google.common.truth.Truth.assertThat import com.google.common.util.concurrent.MoreExecutors.directExecutor import org.junit.Before diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt index faadf1d623c9..96ffa03a1f65 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt @@ -53,7 +53,7 @@ import org.junit.runner.RunWith import org.mockito.kotlin.mock import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags -import com.android.wm.shell.common.bubbles.BubbleBarLocation +import com.android.wm.shell.shared.bubbles.BubbleBarLocation import java.util.concurrent.Semaphore import java.util.concurrent.TimeUnit import java.util.function.Consumer diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt new file mode 100644 index 000000000000..9fdde128ce41 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2024 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.bubbles + +import android.content.Context +import android.content.pm.LauncherApps +import android.content.pm.ShortcutInfo +import android.content.res.Resources +import android.graphics.Color +import android.os.Handler +import android.os.UserManager +import android.view.IWindowManager +import android.view.WindowManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.internal.R +import com.android.internal.protolog.ProtoLog +import com.android.internal.statusbar.IStatusBarService +import com.android.launcher3.icons.BubbleIconFactory +import com.android.wm.shell.ShellTaskOrganizer +import com.android.wm.shell.WindowManagerShellWrapper +import com.android.wm.shell.bubbles.properties.BubbleProperties +import com.android.wm.shell.bubbles.storage.BubblePersistentRepository +import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.DisplayInsetsController +import com.android.wm.shell.common.FloatingContentCoordinator +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.common.SyncTransactionQueue +import com.android.wm.shell.common.TaskStackListenerImpl +import com.android.wm.shell.shared.TransactionPool +import com.android.wm.shell.sysui.ShellCommandHandler +import com.android.wm.shell.sysui.ShellController +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.taskview.TaskView +import com.android.wm.shell.taskview.TaskViewTransitions +import com.android.wm.shell.transition.Transitions +import com.google.common.truth.Truth.assertThat +import com.google.common.util.concurrent.MoreExecutors.directExecutor +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.mock + +/** Test inflating bubbles with [BubbleViewInfoTask]. */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class BubbleViewInfoTaskTest { + + private val context = ApplicationProvider.getApplicationContext<Context>() + private lateinit var metadataFlagListener: Bubbles.BubbleMetadataFlagListener + private lateinit var iconFactory: BubbleIconFactory + private lateinit var bubbleController: BubbleController + private lateinit var mainExecutor: TestExecutor + private lateinit var bgExecutor: TestExecutor + private lateinit var bubbleStackView: BubbleStackView + private lateinit var bubblePositioner: BubblePositioner + private lateinit var expandedViewManager: BubbleExpandedViewManager + + private val bubbleTaskViewFactory = BubbleTaskViewFactory { + BubbleTaskView(mock<TaskView>(), directExecutor()) + } + + @Before + fun setUp() { + ProtoLog.REQUIRE_PROTOLOGTOOL = false + metadataFlagListener = Bubbles.BubbleMetadataFlagListener {} + iconFactory = + BubbleIconFactory( + context, + 60, + 30, + Color.RED, + context.resources.getDimensionPixelSize(R.dimen.importance_ring_stroke_width) + ) + + mainExecutor = TestExecutor() + bgExecutor = TestExecutor() + val windowManager = context.getSystemService(WindowManager::class.java) + val shellInit = ShellInit(mainExecutor) + val shellCommandHandler = ShellCommandHandler() + val shellController = + ShellController( + context, + shellInit, + shellCommandHandler, + mock<DisplayInsetsController>(), + mainExecutor + ) + bubblePositioner = BubblePositioner(context, windowManager) + val bubbleData = + BubbleData( + context, + mock<BubbleLogger>(), + bubblePositioner, + BubbleEducationController(context), + mainExecutor, + bgExecutor + ) + + val surfaceSynchronizer = { obj: Runnable -> obj.run() } + + val bubbleDataRepository = + BubbleDataRepository( + mock<LauncherApps>(), + mainExecutor, + bgExecutor, + BubblePersistentRepository(context) + ) + + bubbleController = + BubbleController( + context, + shellInit, + shellCommandHandler, + shellController, + bubbleData, + surfaceSynchronizer, + FloatingContentCoordinator(), + bubbleDataRepository, + mock<IStatusBarService>(), + windowManager, + WindowManagerShellWrapper(mainExecutor), + mock<UserManager>(), + mock<LauncherApps>(), + mock<BubbleLogger>(), + mock<TaskStackListenerImpl>(), + mock<ShellTaskOrganizer>(), + bubblePositioner, + mock<DisplayController>(), + null, + null, + mainExecutor, + mock<Handler>(), + bgExecutor, + mock<TaskViewTransitions>(), + mock<Transitions>(), + SyncTransactionQueue(TransactionPool(), mainExecutor), + mock<IWindowManager>(), + mock<BubbleProperties>() + ) + + val bubbleStackViewManager = BubbleStackViewManager.fromBubbleController(bubbleController) + bubbleStackView = + BubbleStackView( + context, + bubbleStackViewManager, + bubblePositioner, + bubbleData, + surfaceSynchronizer, + FloatingContentCoordinator(), + bubbleController, + mainExecutor + ) + expandedViewManager = BubbleExpandedViewManager.fromBubbleController(bubbleController) + } + + @Test + fun start_runsOnExecutors() { + val bubble = createBubbleWithShortcut() + val task = createBubbleViewInfoTask(bubble) + + task.start() + + assertThat(bubble.isInflated).isFalse() + assertThat(bubble.expandedView).isNull() + assertThat(task.isFinished).isFalse() + + bgExecutor.flushAll() + assertThat(bubble.isInflated).isFalse() + assertThat(bubble.expandedView).isNull() + assertThat(task.isFinished).isFalse() + + mainExecutor.flushAll() + assertThat(bubble.isInflated).isTrue() + assertThat(bubble.expandedView).isNotNull() + assertThat(task.isFinished).isTrue() + } + + @Test + fun startSync_runsImmediately() { + val bubble = createBubbleWithShortcut() + val task = createBubbleViewInfoTask(bubble) + + task.startSync() + assertThat(bubble.isInflated).isTrue() + assertThat(bubble.expandedView).isNotNull() + assertThat(task.isFinished).isTrue() + } + + @Test + fun start_calledTwice_throwsIllegalStateException() { + val bubble = createBubbleWithShortcut() + val task = createBubbleViewInfoTask(bubble) + task.start() + Assert.assertThrows(IllegalStateException::class.java) { task.start() } + } + + @Test + fun startSync_calledTwice_throwsIllegalStateException() { + val bubble = createBubbleWithShortcut() + val task = createBubbleViewInfoTask(bubble) + task.startSync() + Assert.assertThrows(IllegalStateException::class.java) { task.startSync() } + } + + @Test + fun start_callbackNotified() { + val bubble = createBubbleWithShortcut() + var bubbleFromCallback: Bubble? = null + val callback = BubbleViewInfoTask.Callback { b: Bubble? -> bubbleFromCallback = b } + val task = createBubbleViewInfoTask(bubble, callback) + task.start() + bgExecutor.flushAll() + mainExecutor.flushAll() + assertThat(bubbleFromCallback).isSameInstanceAs(bubble) + } + + @Test + fun startSync_callbackNotified() { + val bubble = createBubbleWithShortcut() + var bubbleFromCallback: Bubble? = null + val callback = BubbleViewInfoTask.Callback { b: Bubble? -> bubbleFromCallback = b } + val task = createBubbleViewInfoTask(bubble, callback) + task.startSync() + assertThat(bubbleFromCallback).isSameInstanceAs(bubble) + } + + @Test + fun cancel_beforeBackgroundWorkStarts_bubbleNotInflated() { + val bubble = createBubbleWithShortcut() + val task = createBubbleViewInfoTask(bubble) + task.start() + + // Cancel before allowing background or main executor to run + task.cancel() + bgExecutor.flushAll() + mainExecutor.flushAll() + + assertThat(bubble.isInflated).isFalse() + assertThat(bubble.expandedView).isNull() + assertThat(task.isFinished).isTrue() + } + + @Test + fun cancel_afterBackgroundWorkBeforeMainThreadWork_bubbleNotInflated() { + val bubble = createBubbleWithShortcut() + val task = createBubbleViewInfoTask(bubble) + task.start() + + // Cancel after background executor runs, but before main executor runs + bgExecutor.flushAll() + task.cancel() + mainExecutor.flushAll() + + assertThat(bubble.isInflated).isFalse() + assertThat(bubble.expandedView).isNull() + assertThat(task.isFinished).isTrue() + } + + @Test + fun cancel_beforeStart_bubbleNotInflated() { + val bubble = createBubbleWithShortcut() + val task = createBubbleViewInfoTask(bubble) + task.cancel() + task.start() + bgExecutor.flushAll() + mainExecutor.flushAll() + + assertThat(task.isFinished).isTrue() + assertThat(bubble.isInflated).isFalse() + assertThat(bubble.expandedView).isNull() + } + + private fun createBubbleWithShortcut(): Bubble { + val shortcutInfo = ShortcutInfo.Builder(context, "mockShortcutId").build() + return Bubble( + "mockKey", + shortcutInfo, + 1000, + Resources.ID_NULL, + "mockTitle", + 0 /* taskId */, + "mockLocus", + true /* isDismissible */, + mainExecutor, + bgExecutor, + metadataFlagListener + ) + } + + private fun createBubbleViewInfoTask( + bubble: Bubble, + callback: BubbleViewInfoTask.Callback? = null + ): BubbleViewInfoTask { + return BubbleViewInfoTask( + bubble, + context, + expandedViewManager, + bubbleTaskViewFactory, + bubblePositioner, + bubbleStackView, + null /* layerView */, + iconFactory, + false /* skipInflation */, + callback, + mainExecutor, + bgExecutor + ) + } + + private class TestExecutor : ShellExecutor { + + private val runnables: MutableList<Runnable> = mutableListOf() + + override fun execute(runnable: Runnable) { + runnables.add(runnable) + } + + override fun executeDelayed(runnable: Runnable, delayMillis: Long) { + execute(runnable) + } + + override fun removeCallbacks(runnable: Runnable?) {} + + override fun hasCallback(runnable: Runnable?): Boolean = false + + fun flushAll() { + while (runnables.isNotEmpty()) { + runnables.removeAt(0).run() + } + } + } +} diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt index 935d12916f56..ecb2b25a02f1 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt @@ -31,12 +31,12 @@ import com.android.internal.protolog.ProtoLog import com.android.wm.shell.R import com.android.wm.shell.bubbles.BubblePositioner import com.android.wm.shell.bubbles.DeviceConfig -import com.android.wm.shell.common.bubbles.BaseBubblePinController -import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_IN_DURATION -import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_OUT_DURATION -import com.android.wm.shell.common.bubbles.BubbleBarLocation -import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT -import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT +import com.android.wm.shell.shared.bubbles.BaseBubblePinController +import com.android.wm.shell.shared.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_IN_DURATION +import com.android.wm.shell.shared.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_OUT_DURATION +import com.android.wm.shell.shared.bubbles.BubbleBarLocation +import com.android.wm.shell.shared.bubbles.BubbleBarLocation.LEFT +import com.android.wm.shell.shared.bubbles.BubbleBarLocation.RIGHT import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Before diff --git a/libs/WindowManager/Shell/res/drawable/decor_back_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_back_button_dark.xml index 5ecba380fb60..a36b21f0408c 100644 --- a/libs/WindowManager/Shell/res/drawable/decor_back_button_dark.xml +++ b/libs/WindowManager/Shell/res/drawable/decor_back_button_dark.xml @@ -15,6 +15,7 @@ --> <vector xmlns:android="http://schemas.android.com/apk/res/android" + android:autoMirrored="true" android:width="32.0dp" android:height="32.0dp" android:viewportWidth="32.0" diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_minimize.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_minimize.xml new file mode 100644 index 000000000000..b35dc022e210 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_minimize.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 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. + --> +<vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <path + android:fillColor="#FF000000" + android:pathData="M6,21V19H18V21Z"/> +</vector> diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml index a0a06f1b3721..806d026a7e7c 100644 --- a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml +++ b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml @@ -14,7 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<com.android.wm.shell.common.bubbles.BubblePopupView +<com.android.wm.shell.shared.bubbles.BubblePopupView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -53,4 +53,4 @@ android:textAlignment="center" android:text="@string/bubble_bar_education_manage_text"/> -</com.android.wm.shell.common.bubbles.BubblePopupView>
\ No newline at end of file +</com.android.wm.shell.shared.bubbles.BubblePopupView>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml index b489a5c1acd0..7fa586c626be 100644 --- a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml +++ b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml @@ -14,7 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<com.android.wm.shell.common.bubbles.BubblePopupView +<com.android.wm.shell.shared.bubbles.BubblePopupView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -53,4 +53,4 @@ android:textAlignment="center" android:text="@string/bubble_bar_education_stack_text"/> -</com.android.wm.shell.common.bubbles.BubblePopupView>
\ No newline at end of file +</com.android.wm.shell.shared.bubbles.BubblePopupView>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml index 7b31c1420a7c..7dcb3c237c51 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml @@ -76,6 +76,18 @@ android:layout_height="40dp" android:layout_weight="1"/> + <ImageButton + android:id="@+id/minimize_window" + android:layout_width="44dp" + android:layout_height="40dp" + android:paddingHorizontal="10dp" + android:paddingVertical="8dp" + android:layout_marginEnd="8dp" + android:contentDescription="@string/minimize_button_text" + android:src="@drawable/desktop_mode_header_ic_minimize" + android:scaleType="centerCrop" + android:gravity="end"/> + <com.android.wm.shell.windowdecor.MaximizeButtonView android:id="@+id/maximize_button_view" android:layout_width="44dp" diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml index 71236909726e..d1b98a693c47 100644 --- a/libs/WindowManager/Shell/res/values-af/strings.xml +++ b/libs/WindowManager/Shell/res/values-af/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Beweeg na regs bo"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Beweeg na links onder"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Beweeg na regs onder"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"vou kieslys uit"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"vou kieslys in"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Skuif links"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Skuif regs"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"vou <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> uit"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"vou <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> in"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-instellings"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Maak kieslys oop"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimeer skerm"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gryp skerm vas"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Hierdie app se grootte kan nie verander word nie"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml index 7504c3771fbc..80447192aeae 100644 --- a/libs/WindowManager/Shell/res/values-am/strings.xml +++ b/libs/WindowManager/Shell/res/values-am/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ወደ ላይኛው ቀኝ አንቀሳቅስ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"የግርጌውን ግራ አንቀሳቅስ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ታችኛውን ቀኝ ያንቀሳቅሱ"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ምናሌን ዘርጋ"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ምናሌን ሰብስብ"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ወደ ግራ ውሰድ"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ወደ ቀኝ ውሰድ"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ን ዘርጋ"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ን ሰብስብ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"የ<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ቅንብሮች"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"ምናሌን ክፈት"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"የማያ ገጹ መጠን አሳድግ"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ማያ ገጹን አሳድግ"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ይህ መተግበሪያ መጠኑ ሊቀየር አይችልም"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml index d6070086e5c1..21aa34e6f526 100644 --- a/libs/WindowManager/Shell/res/values-ar/strings.xml +++ b/libs/WindowManager/Shell/res/values-ar/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"الانتقال إلى أعلى اليسار"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"نقل إلى أسفل يمين الشاشة"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"نقل إلى أسفل اليسار"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"توسيع القائمة"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"تصغير القائمة"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"نقل لليسار"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"نقل لليمين"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"توسيع <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"تصغير <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"إعدادات <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"فتح القائمة"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"تكبير الشاشة إلى أقصى حدّ"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"التقاط صورة للشاشة"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"لا يمكن تغيير حجم نافذة هذا التطبيق"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml index 88566a724f81..c59f4705f58a 100644 --- a/libs/WindowManager/Shell/res/values-as/strings.xml +++ b/libs/WindowManager/Shell/res/values-as/strings.xml @@ -67,10 +67,8 @@ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"তলৰ সোঁফালে নিয়ক"</string> <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"মেনু বিস্তাৰ কৰক"</string> <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"মেনু সংকোচন কৰক"</string> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"বাওঁফাললৈ নিয়ক"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"সোঁফাললৈ নিয়ক"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> বিস্তাৰ কৰক"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> সংকোচন কৰক"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ছেটিং"</string> @@ -129,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"মেনু খোলক"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্ৰীন মেক্সিমাইজ কৰক"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্ৰীন স্নেপ কৰক"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"এই এপ্টোৰ আকাৰ সলনি কৰিব নোৱাৰি"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml index 82cebb758d57..841323ebe777 100644 --- a/libs/WindowManager/Shell/res/values-az/strings.xml +++ b/libs/WindowManager/Shell/res/values-az/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Yuxarıya sağa köçürün"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Aşağıya sola köçürün"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Aşağıya sağa köçürün"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"menyunu genişləndirin"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"menyunu yığcamlaşdırın"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Sola köçürün"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Sağa köçürün"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"genişləndirin: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"yığcamlaşdırın: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ayarları"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Menyunu açın"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı maksimum böyüdün"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranı çəkin"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Bu tətbiqin ölçüsünü dəyişmək olmur"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml index 566956a1cc1d..86ab548d6478 100644 --- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml +++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premesti gore desno"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premesti dole levo"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premesti dole desno"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"proširi meni"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"skupi meni"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Pomerite nalevo"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Pomerite nadesno"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"proširite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skupite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Podešavanja za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Otvorite meni"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Povećaj ekran"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Uklopi ekran"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Veličina ove aplikacije ne može da se promeni"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml index ddd287adff6b..bcbc1ae3c26d 100644 --- a/libs/WindowManager/Shell/res/values-be/strings.xml +++ b/libs/WindowManager/Shell/res/values-be/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Перамясціце правей і вышэй"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Перамясціць лявей і ніжэй"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Перамясціць правей і ніжэй"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"разгарнуць меню"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"згарнуць меню"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Перамясціць улева"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Перамясціць управа"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: разгарнуць"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: згарнуць"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Налады \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\""</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Адкрыць меню"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Разгарнуць на ўвесь экран"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Размясціць на палавіне экрана"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Немагчыма змяніць памер праграмы"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml index e34eb3cbd512..4d1208b8d4c9 100644 --- a/libs/WindowManager/Shell/res/values-bg/strings.xml +++ b/libs/WindowManager/Shell/res/values-bg/strings.xml @@ -67,10 +67,8 @@ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Преместване долу вдясно"</string> <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"разгъване на менюто"</string> <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"свиване на менюто"</string> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Преместване наляво"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Преместване надясно"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"разгъване на <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"свиване на <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Настройки за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -129,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Отваряне на менюто"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Увеличаване на екрана"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Прилепване на екрана"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Това приложение не може да бъде преоразмерено"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml index 9e164fd6468f..bf8bc99df009 100644 --- a/libs/WindowManager/Shell/res/values-bn/strings.xml +++ b/libs/WindowManager/Shell/res/values-bn/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"উপরে ডানদিকে সরান"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"নিচে বাঁদিকে সরান"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"নিচে ডান দিকে সরান"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"মেনু বড় করে দেখুন"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"মেনু আড়াল করুন"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"বাঁদিকে সরান"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ডানদিকে সরান"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> বড় করুন"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> আড়াল করুন"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> সেটিংস"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"মেনু খুলুন"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্রিন বড় করুন"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্রিনে অ্যাপ মানানসই হিসেবে ছোট বড় করুন"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"এই অ্যাপ ছোট বড় করা যাবে না"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml index f4150729cd03..cf53d258ab12 100644 --- a/libs/WindowManager/Shell/res/values-bs/strings.xml +++ b/libs/WindowManager/Shell/res/values-bs/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pomjerite gore desno"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pomjeri dolje lijevo"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pomjerite dolje desno"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"proširivanje menija"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"sužavanje menija"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Pomicanje ulijevo"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Pomicanje udesno"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"proširivanje oblačića <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sužavanje oblačića <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Postavke aplikacije <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Otvaranje menija"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiziraj ekran"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snimi ekran"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Nije moguće promijeniti veličinu aplikacije"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml index 6fe22847448e..87ea62e172c5 100644 --- a/libs/WindowManager/Shell/res/values-ca/strings.xml +++ b/libs/WindowManager/Shell/res/values-ca/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mou a dalt a la dreta"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mou a baix a l\'esquerra"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mou a baix a la dreta"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"desplega el menú"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"replega el menú"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Mou cap a l\'esquerra"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Mou cap a la dreta"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"desplega <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"replega <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuració de l\'aplicació <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Obre el menú"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximitza la pantalla"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajusta la pantalla"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"No es pot canviar la mida d\'aquesta aplicació"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml index ab6abfc78a3c..e21213b6e479 100644 --- a/libs/WindowManager/Shell/res/values-cs/strings.xml +++ b/libs/WindowManager/Shell/res/values-cs/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Přesunout vpravo nahoru"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Přesunout vlevo dolů"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Přesunout vpravo dolů"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"rozbalit nabídku"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"sbalit nabídku"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Přesunout doleva"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Přesunout doprava"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"rozbalit <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sbalit <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavení <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Otevřít nabídku"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovat obrazovku"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Rozpůlit obrazovku"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Velikost aplikace nelze změnit"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml index 7f9b81b7f904..1c4647fc2521 100644 --- a/libs/WindowManager/Shell/res/values-da/strings.xml +++ b/libs/WindowManager/Shell/res/values-da/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flyt op til højre"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flyt ned til venstre"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flyt ned til højre"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"udvid menu"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"minimer menu"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Flyt til venstre"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Flyt til højre"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"udvid <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skjul <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Indstillinger for <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Åbn menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimér skærm"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tilpas skærm"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Størrelsen på denne app kan ikke justeres"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml index 9228d1cd8bc8..88a5789e7222 100644 --- a/libs/WindowManager/Shell/res/values-de/strings.xml +++ b/libs/WindowManager/Shell/res/values-de/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Nach oben rechts verschieben"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Nach unten links verschieben"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Nach unten rechts verschieben"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"Menü maximieren"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"Menü minimieren"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Nach links bewegen"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Nach rechts bewegen"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> maximieren"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> minimieren"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Einstellungen für <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Menü öffnen"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Bildschirm maximieren"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Bildschirm teilen"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Die Größe dieser App kann nicht geändert werden"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml index a5383a0a4564..beeefee1835f 100644 --- a/libs/WindowManager/Shell/res/values-el/strings.xml +++ b/libs/WindowManager/Shell/res/values-el/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Μετακίνηση επάνω δεξιά"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Μετακίνηση κάτω αριστερά"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Μετακίνηση κάτω δεξιά"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ανάπτυξη μενού"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"σύμπτυξη μενού"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Μετακίνηση αριστερά"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Μετακίνηση δεξιά"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ανάπτυξη <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"σύμπτυξη <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Ρυθμίσεις <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Άνοιγμα μενού"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Μεγιστοποίηση οθόνης"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Προβολή στο μισό της οθόνης"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Δεν είναι δυνατή η αλλαγή μεγέθους αυτής της εφαρμογής"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml index 640a3909ea23..72f4070cfd8d 100644 --- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"expand menu"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"collapse menu"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Move left"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Move right"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"This app can\'t be resized"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml index 536efb2d7298..d11f521a187e 100644 --- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml @@ -67,10 +67,8 @@ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string> <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"expand menu"</string> <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"collapse menu"</string> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Move left"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Move right"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string> @@ -129,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Open Menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximize Screen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap Screen"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"This app can\'t be resized"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml index 640a3909ea23..72f4070cfd8d 100644 --- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"expand menu"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"collapse menu"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Move left"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Move right"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"This app can\'t be resized"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml index 640a3909ea23..72f4070cfd8d 100644 --- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"expand menu"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"collapse menu"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Move left"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Move right"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"This app can\'t be resized"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml index 05341b7dfe14..8002468a3659 100644 --- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml @@ -67,10 +67,8 @@ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string> <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"expand menu"</string> <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"collapse menu"</string> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Move left"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Move right"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string> @@ -129,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Open Menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximize Screen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap Screen"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"This app can\'t be resized"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml index df7fb008532a..5756aae321b1 100644 --- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml +++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Ubicar arriba a la derecha"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Ubicar abajo a la izquierda"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ubicar abajo a la derecha"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"expandir menú"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"contraer menú"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Mover hacia la izquierda"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Mover hacia la derecha"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expandir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Abrir el menú"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"No se puede cambiar el tamaño de esta app"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml index 4126b658b39a..3c55bf62fa95 100644 --- a/libs/WindowManager/Shell/res/values-es/strings.xml +++ b/libs/WindowManager/Shell/res/values-es/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover arriba a la derecha"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover abajo a la izquierda."</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover abajo a la derecha"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"mostrar menú"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ocultar menú"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Mover hacia la izquierda"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Mover hacia la derecha"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"desplegar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Ajustes de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Abrir menú"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"No se puede cambiar el tamaño de esta aplicación"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml index 2a2553ea3521..d92196729a00 100644 --- a/libs/WindowManager/Shell/res/values-et/strings.xml +++ b/libs/WindowManager/Shell/res/values-et/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Teisalda üles paremale"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Teisalda alla vasakule"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Teisalda alla paremale"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"menüü laiendamine"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"menüü ahendamine"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Liiguta vasakule"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Liiguta paremale"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"laienda <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ahenda <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Rakenduse <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> seaded"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Ava menüü"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Kuva täisekraanil"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Kuva poolel ekraanil"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Selle rakenduse aknasuurust ei saa muuta"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml index 20d4ff35b7e1..f319af1c4e81 100644 --- a/libs/WindowManager/Shell/res/values-eu/strings.xml +++ b/libs/WindowManager/Shell/res/values-eu/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Eraman goialdera, eskuinetara"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Eraman behealdera, ezkerretara"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Eraman behealdera, eskuinetara"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"zabaldu menua"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"tolestu menua"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Eraman ezkerrera"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Eraman eskuinera"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"zabaldu <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"tolestu <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> aplikazioaren ezarpenak"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Ireki menua"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Handitu pantaila"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zatitu pantaila"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Ezin zaio aldatu tamaina aplikazio honi"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml index c4609c6a036f..44a0929ab59b 100644 --- a/libs/WindowManager/Shell/res/values-fa/strings.xml +++ b/libs/WindowManager/Shell/res/values-fa/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"انتقال به بالا سمت چپ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"انتقال به پایین سمت راست"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"انتقال به پایین سمت چپ"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ازهم بازکردن منو"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"جمع کردن منو"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"انتقال بهچپ"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"انتقال بهراست"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ازهم باز کردن <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"جمع کردن <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"تنظیمات <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"باز کردن منو"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"بزرگ کردن صفحه"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"بزرگ کردن صفحه"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"اندازه این برنامه را نمیتوان تغییر داد"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml index 9da30d0f8646..59cd6e0c1e2a 100644 --- a/libs/WindowManager/Shell/res/values-fi/strings.xml +++ b/libs/WindowManager/Shell/res/values-fi/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Siirrä oikeaan yläreunaan"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Siirrä vasempaan alareunaan"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Siirrä oikeaan alareunaan"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"laajenna valikko"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"tiivistä valikko"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Siirrä vasemmalle"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Siirrä oikealle"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"laajenna <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"tiivistä <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: asetukset"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Avaa valikko"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Suurenna näyttö"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Jaa näyttö"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Tämän sovellusikkunan kokoa ei voi muuttaa"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml index 0af6771d71d5..02f832bdc255 100644 --- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Déplacer en haut à droite"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Déplacer en bas à gauche"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Déplacer en bas à droite"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"développer le menu"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"réduire le menu"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Déplacer vers la gauche"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Déplacer vers la droite"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"développer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"réduire <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Paramètres <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Ouvrir le menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Agrandir l\'écran"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Aligner l\'écran"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Impossible de redimensionner cette appli"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml index d36c1af2e31b..5d916f4b74b9 100644 --- a/libs/WindowManager/Shell/res/values-fr/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Déplacer en haut à droite"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Déplacer en bas à gauche"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Déplacer en bas à droite"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"développer le menu"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"réduire le menu"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Déplacer vers la gauche"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Déplacer vers la droite"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"Développer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Réduire <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Paramètres <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Ouvrir le menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mettre en plein écran"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fractionner l\'écran"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Impossible de redimensionner cette appli"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml index 2923ef420410..e1b2a7e1eeb5 100644 --- a/libs/WindowManager/Shell/res/values-gl/strings.xml +++ b/libs/WindowManager/Shell/res/values-gl/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover arriba á dereita"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover abaixo á esquerda"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover abaixo á dereita"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"despregar o menú"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"contraer o menú"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Mover cara á esquerda"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Mover cara á dereita"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"despregar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Abrir menú"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Encaixar pantalla"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Non se pode cambiar o tamaño desta aplicación"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml index 588f1fe83fe6..fecce73ef2fd 100644 --- a/libs/WindowManager/Shell/res/values-gu/strings.xml +++ b/libs/WindowManager/Shell/res/values-gu/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ઉપર જમણે ખસેડો"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"નીચે ડાબે ખસેડો"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"નીચે જમણે ખસેડો"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"મેનૂ મોટું કરો"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"મેનૂ નાનું કરો"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ડાબે ખસેડો"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"જમણે ખસેડો"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> મોટું કરો"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> નાનું કરો"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> સેટિંગ"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"મેનૂ ખોલો"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"સ્ક્રીન કરો મોટી કરો"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"સ્ક્રીન સ્નૅપ કરો"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"આ ઍપના કદમાં વધઘટ કરી શકાતો નથી"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml index 2fce7f150713..f889f2091255 100644 --- a/libs/WindowManager/Shell/res/values-hi/strings.xml +++ b/libs/WindowManager/Shell/res/values-hi/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"सबसे ऊपर दाईं ओर ले जाएं"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"बाईं ओर सबसे नीचे ले जाएं"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"सबसे नीचे दाईं ओर ले जाएं"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"मेन्यू बड़ा करें"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"मेन्यू छोटा करें"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"बाईं ओर ले जाएं"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"दाईं ओर ले जाएं"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> को बड़ा करें"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> को छोटा करें"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> की सेटिंग"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"मेन्यू खोलें"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन को बड़ा करें"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्नैप स्क्रीन"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"इस ऐप्लिकेशन का साइज़ नहीं बदला जा सकता"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml index a6b8e5873f34..04053c862e37 100644 --- a/libs/WindowManager/Shell/res/values-hr/strings.xml +++ b/libs/WindowManager/Shell/res/values-hr/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premjesti u gornji desni kut"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premjesti u donji lijevi kut"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premjestite u donji desni kut"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"proširi izbornik"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"sažmi izbornik"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Pomakni ulijevo"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Pomakni udesno"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"proširite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sažmite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Postavke za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Otvaranje izbornika"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimalno povećaj zaslon"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Izradi snimku zaslona"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Nije moguće promijeniti veličinu aplikacije"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml index aecfb844d0f2..bb5264955f97 100644 --- a/libs/WindowManager/Shell/res/values-hu/strings.xml +++ b/libs/WindowManager/Shell/res/values-hu/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Áthelyezés fel és jobbra"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Áthelyezés le és balra"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Áthelyezés le és jobbra"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"menü kibontása"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"menü összecsukása"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Mozgatás balra"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Mozgatás jobbra"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> kibontása"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> összecsukása"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> beállításai"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Menü megnyitása"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Képernyő méretének maximalizálása"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Igazodás a képernyő adott részéhez"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Ezt az alkalmazást nem lehet átméretezni"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml index 3e572252d725..fff5a10a9b1b 100644 --- a/libs/WindowManager/Shell/res/values-hy/strings.xml +++ b/libs/WindowManager/Shell/res/values-hy/strings.xml @@ -67,10 +67,8 @@ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Տեղափոխել ներքև՝ աջ"</string> <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ծավալել ընտրացանկը"</string> <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ծալել ընտրացանկը"</string> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Տեղափոխել ձախ"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Տեղափոխել աջ"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>. ծավալել"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>. ծալել"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – կարգավորումներ"</string> @@ -129,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Բացել ընտրացանկը"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ծավալել էկրանը"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ծալել էկրանը"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Այս հավելվածի չափը հնարավոր չէ փոխել"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml index 50073e157ab0..a957754a7924 100644 --- a/libs/WindowManager/Shell/res/values-in/strings.xml +++ b/libs/WindowManager/Shell/res/values-in/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pindahkan ke kanan atas"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pindahkan ke kiri bawah"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pindahkan ke kanan bawah"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"luaskan menu"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ciutkan menu"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Pindahkan ke kiri"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Pindahkan ke kanan"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"luaskan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ciutkan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Setelan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Buka Menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Perbesar Layar"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gabungkan Layar"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Ukuran aplikasi ini tidak dapat diubah"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml index 50603104bbf6..7b91768d5688 100644 --- a/libs/WindowManager/Shell/res/values-is/strings.xml +++ b/libs/WindowManager/Shell/res/values-is/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Færa efst til hægri"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Færa neðst til vinstri"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Færðu neðst til hægri"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"stækka valmynd"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"draga saman valmynd"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Færa til vinstri"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Færa til hægri"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"stækka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"minnka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Stillingar <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Opna valmynd"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Stækka skjá"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Smelluskjár"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Ekki er hægt að breyta stærð þessa forrits"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml index 36200660c6fe..4ae4b36d34d5 100644 --- a/libs/WindowManager/Shell/res/values-it/strings.xml +++ b/libs/WindowManager/Shell/res/values-it/strings.xml @@ -67,10 +67,8 @@ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sposta in basso a destra"</string> <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"espandi menu"</string> <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"comprimi menu"</string> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Sposta a sinistra"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Sposta a destra"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"espandi <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"comprimi <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Impostazioni <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -129,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Apri menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Massimizza schermo"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Aggancia schermo"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Non è possibile ridimensionare questa app"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml index d9c883d3bc1c..ea73653b39c3 100644 --- a/libs/WindowManager/Shell/res/values-iw/strings.xml +++ b/libs/WindowManager/Shell/res/values-iw/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"העברה לפינה הימנית העליונה"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"העברה לפינה השמאלית התחתונה"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"העברה לפינה הימנית התחתונה"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"הרחבת התפריט"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"כיווץ התפריט"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"הזזה שמאלה"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"הזזה ימינה"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"הרחבה של <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"כיווץ של <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"הגדרות <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"פתיחת התפריט"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"הגדלת המסך"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"כיווץ המסך"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"לא ניתן לשנות את גודל החלון של האפליקציה הזו"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml index 5fa5df947574..0cb921ccf810 100644 --- a/libs/WindowManager/Shell/res/values-ja/strings.xml +++ b/libs/WindowManager/Shell/res/values-ja/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"右上に移動"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"左下に移動"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"右下に移動"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"メニューを開く"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"メニューを閉じる"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"左に移動"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"右に移動"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>を開きます"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>を閉じます"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> の設定"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"メニューを開く"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"画面の最大化"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"画面のスナップ"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"このアプリはサイズ変更できません"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml index c420d03da0f5..16e99ba9d46b 100644 --- a/libs/WindowManager/Shell/res/values-ka/strings.xml +++ b/libs/WindowManager/Shell/res/values-ka/strings.xml @@ -67,10 +67,8 @@ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"გადაანაცვ. ქვემოთ და მარჯვნივ"</string> <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"მენიუს გაფართოება"</string> <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"მენიუს ჩაკეცვა"</string> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"მარცხნივ გადატანა"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"მარჯვნივ გადატანა"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-ის გაფართოება"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-ის ჩაკეცვა"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-ის პარამეტრები"</string> @@ -129,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"მენიუს გახსნა"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"აპლიკაციის გაშლა სრულ ეკრანზე"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"აპლიკაციის დაპატარავება ეკრანზე"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"აპის ზომის შეცვლა შეუძლებელია"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml index ba867c4faa40..c6f558ff83fd 100644 --- a/libs/WindowManager/Shell/res/values-kk/strings.xml +++ b/libs/WindowManager/Shell/res/values-kk/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Жоғары оң жаққа жылжыту"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Төменгі сол жаққа жылжыту"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Төменгі оң жаққа жылжыту"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"мәзірді жаю"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"мәзірді жию"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Солға жылжыту"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Оңға жылжыту"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: жаю"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: жию"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> параметрлері"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Мәзірді ашу"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды ұлғайту"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды бөлу"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Бұл қолданбаның өлшемі өзгертілмейді."</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml index fe9dd7d3b400..508ea489cab4 100644 --- a/libs/WindowManager/Shell/res/values-km/strings.xml +++ b/libs/WindowManager/Shell/res/values-km/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ផ្លាស់ទីទៅផ្នែកខាងលើខាងស្ដាំ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ផ្លាស់ទីទៅផ្នែកខាងក្រោមខាងឆ្វេង"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ផ្លាស់ទីទៅផ្នែកខាងក្រោមខាងស្ដាំ"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ពង្រីកម៉ឺនុយ"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"បង្រួមម៉ឺនុយ"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ផ្លាស់ទីទៅឆ្វេង"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ផ្លាស់ទីទៅស្តាំ"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ពង្រីក <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"បង្រួម <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ការកំណត់ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"បើកម៉ឺនុយ"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ពង្រីកអេក្រង់"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ថតអេក្រង់"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"មិនអាចប្ដូរទំហំកម្មវិធីនេះបានទេ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml index f15288674839..1fc627b0eecf 100644 --- a/libs/WindowManager/Shell/res/values-kn/strings.xml +++ b/libs/WindowManager/Shell/res/values-kn/strings.xml @@ -38,16 +38,16 @@ <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ಸೆಕೆಂಡರಿ ಡಿಸ್ಪ್ಲೇಗಳಲ್ಲಿ ಪ್ರಾರಂಭಿಸುವಿಕೆಯನ್ನು ಅಪ್ಲಿಕೇಶನ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string> <string name="accessibility_divider" msgid="6407584574218956849">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಡಿವೈಡರ್"</string> <string name="divider_title" msgid="1963391955593749442">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಡಿವೈಡರ್"</string> - <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ಎಡ ಪೂರ್ಣ ಪರದೆ"</string> + <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ಎಡ ಫುಲ್ ಸ್ಕ್ರೀನ್"</string> <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70% ಎಡಕ್ಕೆ"</string> <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50% ಎಡಕ್ಕೆ"</string> <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30% ಎಡಕ್ಕೆ"</string> - <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ಬಲ ಪೂರ್ಣ ಪರದೆ"</string> - <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ಮೇಲಿನ ಪೂರ್ಣ ಪರದೆ"</string> + <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ಬಲ ಫುಲ್ ಸ್ಕ್ರೀನ್"</string> + <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ಮೇಲಿನ ಫುಲ್ ಸ್ಕ್ರೀನ್"</string> <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70% ಮೇಲಕ್ಕೆ"</string> <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50% ಮೇಲಕ್ಕೆ"</string> <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30% ಮೇಲಕ್ಕೆ"</string> - <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ಕೆಳಗಿನ ಪೂರ್ಣ ಪರದೆ"</string> + <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ಕೆಳಗಿನ ಫುಲ್ ಸ್ಕ್ರೀನ್"</string> <string name="accessibility_split_left" msgid="1713683765575562458">"ಎಡಕ್ಕೆ ವಿಭಜಿಸಿ"</string> <string name="accessibility_split_right" msgid="8441001008181296837">"ಬಲಕ್ಕೆ ವಿಭಜಿಸಿ"</string> <string name="accessibility_split_top" msgid="2789329702027147146">"ಮೇಲಕ್ಕೆ ವಿಭಜಿಸಿ"</string> @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ಬಲ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ಸ್ಕ್ರೀನ್ನ ಎಡ ಕೆಳಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ಕೆಳಗಿನ ಬಲಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ಮೆನು ವಿಸ್ತರಿಸಿ"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ಮೆನು ಸಂಕುಚಿಸಿ"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ಎಡಕ್ಕೆ ಸರಿಸಿ"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ಬಲಕ್ಕೆ ಸರಿಸಿ"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ಅನ್ನು ವಿಸ್ತೃತಗೊಳಿಸಿ"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ಅನ್ನು ಕುಗ್ಗಿಸಿ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"ಮೆನು ತೆರೆಯಿರಿ"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ಸ್ನ್ಯಾಪ್ ಸ್ಕ್ರೀನ್"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಗಾತ್ರಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml index 3dfe573a6506..efb79306cb3b 100644 --- a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml +++ b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml @@ -20,7 +20,7 @@ <string name="notification_channel_tv_pip" msgid="2576686079160402435">"ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರ"</string> <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ಶೀರ್ಷಿಕೆ ರಹಿತ ಕಾರ್ಯಕ್ರಮ)"</string> <string name="pip_close" msgid="2955969519031223530">"ಮುಚ್ಚಿರಿ"</string> - <string name="pip_fullscreen" msgid="7278047353591302554">"ಪೂರ್ಣ ಪರದೆ"</string> + <string name="pip_fullscreen" msgid="7278047353591302554">"ಫುಲ್ ಸ್ಕ್ರೀನ್"</string> <string name="pip_move" msgid="158770205886688553">"ಸರಿಸಿ"</string> <string name="pip_expand" msgid="1051966011679297308">"ವಿಸ್ತೃತಗೊಳಿಸಿ"</string> <string name="pip_collapse" msgid="3903295106641385962">"ಕುಗ್ಗಿಸಿ"</string> diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml index 20ea0cdabf35..96d360ecb521 100644 --- a/libs/WindowManager/Shell/res/values-ko/strings.xml +++ b/libs/WindowManager/Shell/res/values-ko/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"오른쪽 상단으로 이동"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"왼쪽 하단으로 이동"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"오른쪽 하단으로 이동"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"메뉴 펼치기"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"메뉴 접기"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"왼쪽으로 이동"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"오른쪽으로 이동"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> 펼치기"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> 접기"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> 설정"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"메뉴 열기"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"화면 최대화"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"화면 분할"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"이 앱은 크기를 조절할 수 없습니다."</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml index 0a1f874a9583..662c2eaeed48 100644 --- a/libs/WindowManager/Shell/res/values-ky/strings.xml +++ b/libs/WindowManager/Shell/res/values-ky/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Жогорку оң жакка жылдыруу"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Төмөнкү сол жакка жылдыруу"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Төмөнкү оң жакка жылдыруу"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"менюну жайып көрсөтүү"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"менюну жыйыштыруу"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Солго жылдыруу"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Оңго жылдыруу"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> жайып көрсөтүү"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> жыйыштыруу"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> параметрлери"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Менюну ачуу"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды чоңойтуу"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды сүрөткө тартып алуу"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Бул колдонмонун өлчөмүн өзгөртүүгө болбойт"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml index e37053dd36b9..ed6b378c574a 100644 --- a/libs/WindowManager/Shell/res/values-lo/strings.xml +++ b/libs/WindowManager/Shell/res/values-lo/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ຍ້າຍຂວາເທິງ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ຍ້າຍຊ້າຍລຸ່ມ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ຍ້າຍຂວາລຸ່ມ"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ຂະຫຍາຍເມນູ"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ຫຍໍ້ເມນູລົງ"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ຍ້າຍໄປທາງຊ້າຍ"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ຍ້າຍໄປທາງຂວາ"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ຂະຫຍາຍ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ຫຍໍ້ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ລົງ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"ການຕັ້ງຄ່າ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"ເປີດເມນູ"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ປັບຈໍໃຫຍ່ສຸດ"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ສະແນັບໜ້າຈໍ"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ບໍ່ສາມາດປັບຂະໜາດແອັບນີ້ໄດ້"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml index 7d706f71b444..f71d65047538 100644 --- a/libs/WindowManager/Shell/res/values-lt/strings.xml +++ b/libs/WindowManager/Shell/res/values-lt/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Perkelti į viršų dešinėje"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Perkelti į apačią kairėje"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Perkelti į apačią dešinėje"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"išskleisti meniu"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"sutraukti meniu"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Perkelti kairėn"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Perkelti dešinėn"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"išskleisti „<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>“"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sutraukti „<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>“"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ nustatymai"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Atidaryti meniu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Išskleisti ekraną"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Sutraukti ekraną"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Negalima keisti šios programos dydžio"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml index 2f1647f9ff27..abadef765745 100644 --- a/libs/WindowManager/Shell/res/values-lv/strings.xml +++ b/libs/WindowManager/Shell/res/values-lv/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pārvietot augšpusē pa labi"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pārvietot apakšpusē pa kreisi"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pārvietot apakšpusē pa labi"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"izvērst izvēlni"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"sakļaut izvēlni"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Pārvietot pa kreisi"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Pārvietot pa labi"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"Izvērst “<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Sakļaut “<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Lietotnes <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iestatījumi"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Atvērt izvēlni"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizēt ekrānu"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fiksēt ekrānu"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Šīs lietotnes loga lielumu nevar mainīt."</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml index 485a261c5efb..0576fc0df708 100644 --- a/libs/WindowManager/Shell/res/values-mk/strings.xml +++ b/libs/WindowManager/Shell/res/values-mk/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Премести горе десно"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Премести долу лево"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Премести долу десно"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"го проширува менито"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"го собира менито"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Преместете налево"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Преместете надесно"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"прошири <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"собери <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Поставки за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Отвори го менито"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Максимизирај го екранот"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Подели го екранот на половина"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Не може да се промени големината на апликацијава"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml index bece95092193..6e7ea08b7610 100644 --- a/libs/WindowManager/Shell/res/values-ml/strings.xml +++ b/libs/WindowManager/Shell/res/values-ml/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"മുകളിൽ വലതുഭാഗത്തേക്ക് നീക്കുക"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ചുവടെ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ചുവടെ വലതുഭാഗത്തേക്ക് നീക്കുക"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"മെനു വികസിപ്പിക്കുക"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"മെനു ചുരുക്കുക"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ഇടത്തേക്ക് നീക്കുക"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"വലത്തേക്ക് നീക്കുക"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> വികസിപ്പിക്കുക"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ചുരുക്കുക"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ക്രമീകരണം"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"മെനു തുറക്കുക"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"സ്ക്രീൻ വലുതാക്കുക"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"സ്ക്രീൻ സ്നാപ്പ് ചെയ്യുക"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ഈ ആപ്പിന്റെ വലുപ്പം മാറ്റാനാകില്ല"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml index 4a72559c7cc4..d69ec05ee0b6 100644 --- a/libs/WindowManager/Shell/res/values-mn/strings.xml +++ b/libs/WindowManager/Shell/res/values-mn/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Баруун дээш зөөх"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Зүүн доош зөөх"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Баруун доош зөөх"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"цэсийг дэлгэх"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"цэсийг хураах"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Зүүн тийш зөөх"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Баруун тийш зөөх"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-г дэлгэх"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-г хураах"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-н тохиргоо"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Цэс нээх"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Дэлгэцийг томруулах"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Дэлгэцийг таллах"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Энэ аппын хэмжээг өөрчлөх боломжгүй"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml index 5b9c12d1b8a3..33ba1c2bf76f 100644 --- a/libs/WindowManager/Shell/res/values-mr/strings.xml +++ b/libs/WindowManager/Shell/res/values-mr/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"वर उजवीकडे हलवा"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"तळाशी डावीकडे हलवा"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"तळाशी उजवीकडे हलवा"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"मेनूचा विस्तार करा"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"मेनू कोलॅप्स करा"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"डावीकडे हलवा"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"उजवीकडे हलवा"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> विस्तार करा"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> कोलॅप्स करा"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> सेटिंग्ज"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"मेनू उघडा"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन मोठी करा"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्क्रीन स्नॅप करा"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"या अॅपचा आकार बदलला जाऊ शकत नाही"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml index f46044aa71c9..e024e4bfaf51 100644 --- a/libs/WindowManager/Shell/res/values-ms/strings.xml +++ b/libs/WindowManager/Shell/res/values-ms/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Alihkan ke atas sebelah kanan"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Alihkan ke bawah sebelah kiri"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Alihkan ke bawah sebelah kanan"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"kembangkan menu"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"kuncupkan menu"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Alih ke kiri"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Alih ke kanan"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"kembangkan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"kuncupkan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Tetapan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Buka Menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimumkan Skrin"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tangkap Skrin"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Apl ini tidak boleh diubah saiz"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml index 0dbff44ac551..bd680b4a7a7a 100644 --- a/libs/WindowManager/Shell/res/values-my/strings.xml +++ b/libs/WindowManager/Shell/res/values-my/strings.xml @@ -67,10 +67,8 @@ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ညာအောက်ခြေသို့ ရွှေ့ပါ"</string> <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"မီနူးကို ပိုပြပါ"</string> <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"မီနူးကို လျှော့ပြပါ"</string> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ဘယ်သို့ရွှေ့ရန်"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ညာသို့ရွှေ့ရန်"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ကို ချဲ့ရန်"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ကို ချုံ့ရန်"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ဆက်တင်များ"</string> @@ -129,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"မီနူး ဖွင့်ရန်"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"စခရင်ကို ချဲ့မည်"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"စခရင်ကို ချုံ့မည်"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ဤအက်ပ်ကို အရွယ်ပြင်၍ မရပါ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml index 4ca8998e95cf..896d9fd3df9f 100644 --- a/libs/WindowManager/Shell/res/values-nb/strings.xml +++ b/libs/WindowManager/Shell/res/values-nb/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flytt til øverst til høyre"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flytt til nederst til venstre"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flytt til nederst til høyre"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"vis menyen"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"skjul menyen"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Flytt til venstre"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Flytt til høyre"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"vis <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skjul <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-innstillinger"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Åpne menyen"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimer skjermen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fest skjermen"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Du kan ikke endre størrelse på denne appen"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml index 2b51fdca0d4f..113085e18f2f 100644 --- a/libs/WindowManager/Shell/res/values-ne/strings.xml +++ b/libs/WindowManager/Shell/res/values-ne/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"सिरानमा दायाँतिर सार्नुहोस्"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"पुछारमा बायाँतिर सार्नुहोस्"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"पुछारमा दायाँतिर सार्नुहोस्"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"मेनु एक्स्पान्ड गर्नुहोस्"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"मेनु कोल्याप्स गर्नुहोस्"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"बायाँतिर सार्नुहोस्"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"दायाँतिर सार्नुहोस्"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> एक्स्पान्ड गर्नुहोस्"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> कोल्याप्स गर्नुहोस्"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> का सेटिङहरू"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"मेनु खोल्नुहोस्"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रिन ठुलो बनाउनुहोस्"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्क्रिन स्न्याप गर्नुहोस्"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"यो एपको आकार बदल्न मिल्दैन"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml index a451bb2b4d54..a9c06fba4c4a 100644 --- a/libs/WindowManager/Shell/res/values-nl/strings.xml +++ b/libs/WindowManager/Shell/res/values-nl/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Naar rechtsboven verplaatsen"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Naar linksonder verplaatsen"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Naar rechtsonder verplaatsen"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"menu uitvouwen"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"menu samenvouwen"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Naar links verplaatsen"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Naar rechts verplaatsen"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> uitvouwen"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> samenvouwen"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Instellingen voor <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Menu openen"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Scherm maximaliseren"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Scherm halveren"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Het formaat van deze app kan niet worden aangepast"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml index 798e38a92cf2..a80cfc2dacf2 100644 --- a/libs/WindowManager/Shell/res/values-or/strings.xml +++ b/libs/WindowManager/Shell/res/values-or/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ଉପର-ଡାହାଣକୁ ନିଅନ୍ତୁ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ତଳ ବାମକୁ ନିଅନ୍ତୁ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ତଳ ଡାହାଣକୁ ନିଅନ୍ତୁ"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ମେନୁକୁ ବିସ୍ତାର କରନ୍ତୁ"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ମେନୁକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ବାମକୁ ମୁଭ କରନ୍ତୁ"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ବିସ୍ତାର କରନ୍ତୁ"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ସଙ୍କୁଚିତ କରନ୍ତୁ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ସେଟିଂସ୍"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"ମେନୁ ଖୋଲନ୍ତୁ"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ସ୍କ୍ରିନକୁ ବଡ଼ କରନ୍ତୁ"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ସ୍କ୍ରିନକୁ ସ୍ନାପ କରନ୍ତୁ"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ଏହି ଆପକୁ ରିସାଇଜ କରାଯାଇପାରିବ ନାହିଁ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml index 641b0b2bd63b..7257161fdc2f 100644 --- a/libs/WindowManager/Shell/res/values-pa/strings.xml +++ b/libs/WindowManager/Shell/res/values-pa/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ਉੱਪਰ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ਹੇਠਾਂ ਵੱਲ ਖੱਬੇ ਲਿਜਾਓ"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ਹੇਠਾਂ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ਮੀਨੂ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ਮੀਨੂ ਨੂੰ ਸਮੇਟੋ"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ਖੱਬੇ ਲਿਜਾਓ"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ਸੱਜੇ ਲਿਜਾਓ"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ਨੂੰ ਸਮੇਟੋ"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ਸੈਟਿੰਗਾਂ"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"ਮੀਨੂ ਖੋਲ੍ਹੋ"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ਸਕ੍ਰੀਨ ਦਾ ਆਕਾਰ ਵਧਾਓ"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ਸਕ੍ਰੀਨ ਨੂੰ ਸਨੈਪ ਕਰੋ"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ਇਸ ਐਪ ਦਾ ਆਕਾਰ ਬਦਲਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml index ba6d04c70be9..7600db0acf9f 100644 --- a/libs/WindowManager/Shell/res/values-pl/strings.xml +++ b/libs/WindowManager/Shell/res/values-pl/strings.xml @@ -67,10 +67,8 @@ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Przenieś w prawy dolny róg"</string> <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"rozwiń menu"</string> <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"zwiń menu"</string> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Przenieś w lewo"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Przenieś w prawo"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"rozwiń dymek <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"zwiń dymek <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – ustawienia"</string> @@ -129,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Otwórz menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksymalizuj ekran"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Przyciągnij ekran"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Nie można zmienić rozmiaru tej aplikacji"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml index b8ba9dfd75cb..58c78f314bcb 100644 --- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover para canto superior direito"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover para canto inferior esquerdo"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover para canto inferior direito"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"abrir menu"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"fechar menu"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Mover para a esquerda"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Mover para a direita"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"abrir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"fechar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Configurações de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Abrir o menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Não é possível redimensionar o app"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml index f11625432a4e..f433413c9a9b 100644 --- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml @@ -67,10 +67,8 @@ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover p/ parte inf. direita"</string> <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"expandir menu"</string> <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"reduzir menu"</string> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Mover para a esquerda"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Mover para a direita"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expandir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"reduzir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Definições de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -129,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Abrir menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar ecrã"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Encaixar ecrã"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Não é possível redimensionar esta app"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml index b8ba9dfd75cb..58c78f314bcb 100644 --- a/libs/WindowManager/Shell/res/values-pt/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover para canto superior direito"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover para canto inferior esquerdo"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover para canto inferior direito"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"abrir menu"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"fechar menu"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Mover para a esquerda"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Mover para a direita"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"abrir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"fechar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Configurações de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Abrir o menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Não é possível redimensionar o app"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml index e39ab61be7d8..077503a46576 100644 --- a/libs/WindowManager/Shell/res/values-ro/strings.xml +++ b/libs/WindowManager/Shell/res/values-ro/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mută în dreapta sus"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mută în stânga jos"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mută în dreapta jos"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"extinde meniul"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"restrânge meniul"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Deplasează spre stânga"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Deplasează spre dreapta"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"extinde <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"restrânge <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Setări <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Deschide meniul"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizează fereastra"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Micșorează fereastra și fixeaz-o"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Aplicația nu poate fi redimensionată"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml index a16ccb21c263..547102749d15 100644 --- a/libs/WindowManager/Shell/res/values-ru/strings.xml +++ b/libs/WindowManager/Shell/res/values-ru/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Переместить в правый верхний угол"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Переместить в левый нижний угол"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Переместить в правый нижний угол"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"развернуть меню"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"свернуть меню"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Переместить влево"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Переместить вправо"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"Развернуть <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Свернуть <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: настройки"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Открыть меню"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Развернуть на весь экран"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Свернуть"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Изменить размер приложения нельзя."</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml index 2e898e1a15c4..3f015f606ba6 100644 --- a/libs/WindowManager/Shell/res/values-si/strings.xml +++ b/libs/WindowManager/Shell/res/values-si/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ඉහළ දකුණට ගෙන යන්න"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"පහළ වමට ගෙන යන්න"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"පහළ දකුණට ගෙන යන්න"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"මෙනුව දිග හරින්න"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"මෙනුව හකුළන්න"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"වමට ගෙන යන්න"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"දකුණට ගෙන යන්න"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> දිග හරින්න"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> හකුළන්න"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> සැකසීම්"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"මෙනුව විවෘත කරන්න"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"තිරය උපරිම කරන්න"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ස්නැප් තිරය"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"මෙම යෙදුම ප්රතිප්රමාණ කළ නොහැක"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml index 5bfeb17ea42b..fa376e7d51ce 100644 --- a/libs/WindowManager/Shell/res/values-sk/strings.xml +++ b/libs/WindowManager/Shell/res/values-sk/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Presunúť doprava nahor"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Presunúť doľava nadol"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Presunúť doprava nadol"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"rozbaliť ponuku"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"zbaliť ponuku"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Posunúť doľava"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Posunúť doprava"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"rozbaliť <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"zbaliť <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavenia aplikácie <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Otvoriť ponuku"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovať obrazovku"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zobraziť polovicu obrazovky"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Veľkosť tejto aplikácie sa nedá zmeniť"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml index 0b483c2f7ea4..85386687398d 100644 --- a/libs/WindowManager/Shell/res/values-sl/strings.xml +++ b/libs/WindowManager/Shell/res/values-sl/strings.xml @@ -67,10 +67,8 @@ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premakni spodaj desno"</string> <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"razširi meni"</string> <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"strni meni"</string> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Premakni levo"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Premakni desno"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"razširitev oblačka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"strnitev oblačka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavitve za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -129,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Odpri meni"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiraj zaslon"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Pripni zaslon"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Velikosti te aplikacije ni mogoče spremeniti"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml index e4cb677c9d17..f77a43d5e9fe 100644 --- a/libs/WindowManager/Shell/res/values-sq/strings.xml +++ b/libs/WindowManager/Shell/res/values-sq/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Lëviz lart djathtas"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Zhvendos poshtë majtas"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Lëvize poshtë djathtas"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"zgjero menynë"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"palos menynë"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Lëvize majtas"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Lëvize djathtas"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"zgjero <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"palos <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Cilësimet e <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Hap menynë"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizo ekranin"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Regjistro ekranin"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Përmasat e këtij aplikacioni nuk mund të ndryshohen"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml index edd9fdb08d5e..af7686aabe67 100644 --- a/libs/WindowManager/Shell/res/values-sr/strings.xml +++ b/libs/WindowManager/Shell/res/values-sr/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Премести горе десно"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Премести доле лево"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Премести доле десно"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"прошири мени"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"скупи мени"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Померите налево"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Померите надесно"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"проширите облачић <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"скупите облачић <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Подешавања за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Отворите мени"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Повећај екран"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Уклопи екран"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Величина ове апликације не може да се промени"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml index 7b3e36ece507..0d08d8d5ecde 100644 --- a/libs/WindowManager/Shell/res/values-sv/strings.xml +++ b/libs/WindowManager/Shell/res/values-sv/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flytta högst upp till höger"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flytta längst ned till vänster"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flytta längst ned till höger"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"utöka menyn"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"komprimera menyn"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Flytta åt vänster"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Flytta åt höger"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"utöka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"komprimera <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Inställningar för <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Öppna menyn"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximera skärmen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fäst skärmen"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Det går inte att ändra storlek på appen"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml index dd8aac94fe19..448f62493d6e 100644 --- a/libs/WindowManager/Shell/res/values-sw/strings.xml +++ b/libs/WindowManager/Shell/res/values-sw/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sogeza juu kulia"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sogeza chini kushoto"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sogeza chini kulia"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"panua menyu"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"kunja menyu"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Sogeza kushoto"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Sogeza kulia"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"panua <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"kunja <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Mipangilio ya <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Fungua Menyu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Panua Dirisha kwenye Skrini"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Panga Madirisha kwenye Skrini"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Huwezi kubadilisha ukubwa wa programu hii"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml index 4d4c1ce1ad19..126892984e71 100644 --- a/libs/WindowManager/Shell/res/values-ta/strings.xml +++ b/libs/WindowManager/Shell/res/values-ta/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"மேலே வலப்புறமாக நகர்த்து"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"கீழே இடப்புறமாக நகர்த்து"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"கீழே வலதுபுறமாக நகர்த்து"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"மெனுவை விரிவாக்கு"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"மெனுவைச் சுருக்கு"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"இடப்புறம் நகர்த்து"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"வலப்புறம் நகர்த்து"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ஐ விரிவாக்கும்"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ஐச் சுருக்கும்"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> அமைப்புகள்"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"மெனுவைத் திற"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"திரையைப் பெரிதாக்கு"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"திரையை ஸ்னாப் செய்"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"இந்த ஆப்ஸின் அளவை மாற்ற முடியாது"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml index 5af6c4af98af..524e55864f05 100644 --- a/libs/WindowManager/Shell/res/values-te/strings.xml +++ b/libs/WindowManager/Shell/res/values-te/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ఎగువ కుడివైపునకు జరుపు"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"దిగువ ఎడమవైపునకు తరలించు"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"దిగవు కుడివైపునకు జరుపు"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"మెనూను విస్తరించండి"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"మెనూను కుదించండి"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ఎడమ వైపుగా జరపండి"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"కుడి వైపుగా జరపండి"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> విస్తరించండి"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ను కుదించండి"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> సెట్టింగ్లు"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"మెనూను తెరవండి"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"స్క్రీన్ సైజ్ను పెంచండి"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"స్క్రీన్ను స్నాప్ చేయండి"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ఈ యాప్ సైజ్ను మార్చడం సాధ్యపడదు"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml index c5a6cb3d4ed1..00a395f953ef 100644 --- a/libs/WindowManager/Shell/res/values-th/strings.xml +++ b/libs/WindowManager/Shell/res/values-th/strings.xml @@ -67,10 +67,8 @@ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ย้ายไปด้านขวาล่าง"</string> <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ขยายเมนู"</string> <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ยุบเมนู"</string> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ย้ายไปทางซ้าย"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ย้ายไปทางขวา"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ขยาย <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ยุบ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"การตั้งค่า <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -129,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"เปิดเมนู"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ขยายหน้าจอให้ใหญ่สุด"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"สแนปหน้าจอ"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ปรับขนาดแอปนี้ไม่ได้"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml index f7d121ec729f..50a9211f3122 100644 --- a/libs/WindowManager/Shell/res/values-tl/strings.xml +++ b/libs/WindowManager/Shell/res/values-tl/strings.xml @@ -67,10 +67,8 @@ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ilipat sa kanan sa ibaba"</string> <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"i-expand ang menu"</string> <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"i-collapse ang menu"</string> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Ilipat pakaliwa"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Ilipat pakanan"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"I-expand ang <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"i-collapse ang <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Mga setting ng <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -129,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Buksan ang Menu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"I-maximize ang Screen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"I-snap ang Screen"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Hindi nare-resize ang app na ito"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml index 2bd13d4b8542..ddd420608e80 100644 --- a/libs/WindowManager/Shell/res/values-tr/strings.xml +++ b/libs/WindowManager/Shell/res/values-tr/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sağ üste taşı"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sol alta taşı"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sağ alta taşı"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"menüyü genişlet"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"menüyü daralt"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Sola taşı"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Sağa taşı"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"genişlet: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"daralt: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ayarları"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Menüyü Aç"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı Büyüt"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranın Yarısına Tuttur"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Bu uygulama yeniden boyutlandırılamaz"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml index 81117b4348b0..1dcdfe6a2ffe 100644 --- a/libs/WindowManager/Shell/res/values-uk/strings.xml +++ b/libs/WindowManager/Shell/res/values-uk/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Перемістити праворуч угору"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Перемістити ліворуч униз"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Перемістити праворуч униз"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"розгорнути меню"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"згорнути меню"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Перемістити ліворуч"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Перемістити праворуч"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"розгорнути \"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>\""</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"згорнути \"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>\""</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Налаштування параметра \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\""</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Відкрити меню"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Розгорнути екран"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Зафіксувати екран"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Розмір вікна цього додатка не можна змінити"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml index e6f8d39a49c0..26ece5ca2fb6 100644 --- a/libs/WindowManager/Shell/res/values-ur/strings.xml +++ b/libs/WindowManager/Shell/res/values-ur/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"اوپر دائیں جانب لے جائيں"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"نیچے بائیں جانب لے جائیں"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"نیچے دائیں جانب لے جائیں"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"مینو کو پھیلائیں"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"مینو کو سکیڑیں"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"بائیں منتقل کریں"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"دائیں منتقل کریں"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> کو پھیلائیں"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> کو سکیڑیں"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ترتیبات"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"مینو کھولیں"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"اسکرین کو بڑا کریں"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"اسکرین کا اسناپ شاٹ لیں"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"اس ایپ کا سائز تبدیل نہیں کیا جا سکتا"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml index 482919ad4036..90b9a3f2f15e 100644 --- a/libs/WindowManager/Shell/res/values-uz/strings.xml +++ b/libs/WindowManager/Shell/res/values-uz/strings.xml @@ -67,10 +67,8 @@ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Quyi oʻngga surish"</string> <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"menyuni ochish"</string> <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"menyuni yopish"</string> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Chapga siljitish"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Oʻngga siljitish"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ni yoyish"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ni yopish"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> sozlamalari"</string> @@ -129,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Menyuni ochish"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranni yoyish"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranni biriktirish"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Bu ilova hajmini oʻzgartirish imkonsiz"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml index 7bc68efbd05a..90471f9a6a33 100644 --- a/libs/WindowManager/Shell/res/values-vi/strings.xml +++ b/libs/WindowManager/Shell/res/values-vi/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Chuyển lên trên cùng bên phải"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Chuyển tới dưới cùng bên trái"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Chuyển tới dưới cùng bên phải"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"mở rộng trình đơn"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"thu gọn trình đơn"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Di chuyển sang trái"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Di chuyển sang phải"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"mở rộng <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"thu gọn <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"Cài đặt <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Mở Trình đơn"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mở rộng màn hình"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Điều chỉnh kích thước màn hình"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Không thể đổi kích thước của ứng dụng này"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml index 169dea7168a8..0aa52ac64da7 100644 --- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移至右上角"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"移至左下角"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"移至右下角"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"展开菜单"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"收起菜单"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"左移"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"右移"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"展开“<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"收起“<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>设置"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"打开菜单"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"最大化屏幕"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"屏幕快照"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"无法调整此应用的大小"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml index 0d997c07e455..8a5be6a74d31 100644 --- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移去右上角"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"移去左下角"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"移去右下角"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"展開選單"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"收合選單"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"向左移"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"向右移"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"打開<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"收埋<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"「<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>」設定"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"打開選單"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"此應用程式無法調整大小"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml index bc1bbc254393..d1cc4bb961bf 100644 --- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移至右上方"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"移至左下方"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"移至右下方"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"展開選單"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"收合選單"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"向左移"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"向右移"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"展開「<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>」"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"收合「<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>」"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"「<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>」設定"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"開啟選單"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"這個應用程式無法調整大小"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml index 7703d3352a75..6163a9794d6c 100644 --- a/libs/WindowManager/Shell/res/values-zu/strings.xml +++ b/libs/WindowManager/Shell/res/values-zu/strings.xml @@ -65,14 +65,10 @@ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Hambisa phezulu ngakwesokudla"</string> <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Hambisa inkinobho ngakwesokunxele"</string> <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Hambisa inkinobho ngakwesokudla"</string> - <!-- no translation found for bubble_accessibility_action_expand_menu (8637233525952938845) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_collapse_menu (2975310870146231463) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_left (4803535120353716759) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bar_right (7686542531917510421) --> - <skip /> + <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"nweba imenyu"</string> + <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"goqa imenyu"</string> + <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Iya kwesokunxele"</string> + <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Iya kwesokudla"</string> <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"nweba <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"goqa <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string> <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> izilungiselelo"</string> @@ -131,4 +127,5 @@ <string name="expand_menu_text" msgid="3847736164494181168">"Vula Imenyu"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Khulisa Isikrini Sifike Ekugcineni"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Thwebula Isikrini"</string> + <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Le app ayikwazi ukushintshwa usayizi"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index 39f6d8c46a24..fe8b81885367 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -183,4 +183,7 @@ <!-- This is to be overridden to define a list of packages mapped to web links which will be parsed and utilized for desktop windowing's app-to-web feature. --> <string name="generic_links_list" translatable="false"/> + + <!-- Apps that can trigger Desktop Windowing App handle Education --> + <string-array name="desktop_windowing_app_handle_education_allowlist_apps"></string-array> </resources> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 53ab2d56f6e6..2d98a2b675a3 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -92,8 +92,11 @@ <dimen name="docked_divider_handle_width">16dp</dimen> <dimen name="docked_divider_handle_height">2dp</dimen> - <dimen name="split_divider_handle_region_width">96dp</dimen> + <dimen name="split_divider_handle_region_width">80dp</dimen> <dimen name="split_divider_handle_region_height">48dp</dimen> + <!-- The divider touch zone height is intentionally halved in portrait to avoid colliding + with the app handle.--> + <dimen name="desktop_mode_portrait_split_divider_handle_region_height">24dp</dimen> <!-- Divider handle size for split screen --> <dimen name="split_divider_handle_width">40dp</dimen> @@ -457,6 +460,11 @@ start of this area. --> <dimen name="desktop_mode_customizable_caption_margin_end">152dp</dimen> + <!-- The width of the right-aligned region that is taken up by caption elements and extra + margins when the caption has the minimize button. This will be merged with the above value + once the minimize button becomes default. --> + <dimen name="desktop_mode_customizable_caption_with_minimize_button_margin_end">204dp</dimen> + <!-- The default minimum allowed window width when resizing a window in desktop mode. --> <dimen name="desktop_mode_minimum_window_width">386dp</dimen> @@ -576,6 +584,13 @@ <!-- The vertical inset to apply to the app chip's ripple drawable --> <dimen name="desktop_mode_header_app_chip_ripple_inset_vertical">4dp</dimen> + <!-- The corner radius of the minimize button's ripple drawable --> + <dimen name="desktop_mode_header_minimize_ripple_radius">18dp</dimen> + <!-- The vertical inset to apply to the minimize button's ripple drawable --> + <dimen name="desktop_mode_header_minimize_ripple_inset_vertical">4dp</dimen> + <!-- The horizontal inset to apply to the minimize button's ripple drawable --> + <dimen name="desktop_mode_header_minimize_ripple_inset_horizontal">6dp</dimen> + <!-- The corner radius of the maximize button's ripple drawable --> <dimen name="desktop_mode_header_maximize_ripple_radius">18dp</dimen> <!-- The vertical inset to apply to the maximize button's ripple drawable --> diff --git a/libs/WindowManager/Shell/res/values/integers.xml b/libs/WindowManager/Shell/res/values/integers.xml index 583bf3341a69..300baeabae83 100644 --- a/libs/WindowManager/Shell/res/values/integers.xml +++ b/libs/WindowManager/Shell/res/values/integers.xml @@ -22,4 +22,16 @@ <integer name="bubbles_overflow_columns">4</integer> <!-- Maximum number of bubbles we allow in overflow before we dismiss the oldest one. --> <integer name="bubbles_max_overflow">16</integer> + <!-- App Handle Education - Minimum number of times an app should have been launched, in order + to be eligible to show education in it --> + <integer name="desktop_windowing_education_min_app_launch_count">3</integer> + <!-- App Handle Education - Interval at which app usage stats should be queried and updated in + cache periodically --> + <integer name="desktop_windowing_education_app_usage_cache_interval_seconds">86400</integer> + <!-- App Handle Education - Time interval in seconds for which we'll analyze app usage + stats to determine if minimum usage requirements are met. --> + <integer name="desktop_windowing_education_app_launch_interval_seconds">2592000</integer> + <!-- App Handle Education - Required time passed in seconds since device has been setup + in order to be eligible to show education --> + <integer name="desktop_windowing_education_required_time_since_setup_seconds">604800</integer> </resources>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index 36d0a3c63b03..a353db72b914 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -155,6 +155,8 @@ <string name="bubbles_app_settings"><xliff:g id="notification_title" example="Android Messages">%1$s</xliff:g> settings</string> <!-- Text used for the bubble dismiss area. Bubbles dragged to, or flung towards, this area will go away. [CHAR LIMIT=30] --> <string name="bubble_dismiss_text">Dismiss bubble</string> + <!-- Text used to move the bubble to fullscreen. [CHAR LIMIT=30] --> + <string name="bubble_fullscreen_text">Move to fullscreen</string> <!-- Button text to stop a conversation from bubbling [CHAR LIMIT=60]--> <string name="bubbles_dont_bubble_conversation">Don\u2019t bubble conversation</string> <!-- Title text for the bubbles feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=60]--> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.aidl index 15797cdb9aba..e21bf8fb723c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.aidl +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2024 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package com.android.wm.shell.util; +package com.android.wm.shell.shared; parcelable GroupedRecentTaskInfo;
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.java index a2d2b9aff597..65e079ef4f72 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.util; +package com.android.wm.shell.shared; import android.annotation.IntDef; import android.app.ActivityManager; @@ -25,6 +25,8 @@ import android.os.Parcelable; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.wm.shell.shared.split.SplitBounds; + import java.util.Arrays; import java.util.List; import java.util.Set; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt index eec24683db8a..7086691e7431 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.common.bubbles +package com.android.wm.shell.shared.bubbles import android.graphics.Point import android.graphics.RectF @@ -23,9 +23,9 @@ import androidx.annotation.VisibleForTesting import androidx.core.animation.Animator import androidx.core.animation.AnimatorListenerAdapter import androidx.core.animation.ObjectAnimator -import com.android.wm.shell.common.bubbles.BaseBubblePinController.LocationChangeListener -import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT -import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT +import com.android.wm.shell.shared.bubbles.BaseBubblePinController.LocationChangeListener +import com.android.wm.shell.shared.bubbles.BubbleBarLocation.LEFT +import com.android.wm.shell.shared.bubbles.BubbleBarLocation.RIGHT /** * Base class for common logic shared between different bubble views to support pinning bubble bar diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.aidl index 3c5beeb48806..4fe76115fa0b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package com.android.wm.shell.common.bubbles; +package com.android.wm.shell.shared.bubbles; parcelable BubbleBarLocation;
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt index f0bdfdef1073..191875d38daf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.wm.shell.common.bubbles +package com.android.wm.shell.shared.bubbles import android.os.Parcel import android.os.Parcelable diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarUpdate.java index ec3c6013e544..5bde1e8fae3b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarUpdate.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.common.bubbles; +package com.android.wm.shell.shared.bubbles; import android.annotation.NonNull; import android.annotation.Nullable; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleConstants.java index 0329b8df7544..3396bc441467 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.common.bubbles; +package com.android.wm.shell.shared.bubbles; /** * Constants shared between bubbles in shell & things we have to do for bubbles in launcher. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java index 829af08e612a..58766826bd3b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.common.bubbles; +package com.android.wm.shell.shared.bubbles; import android.annotation.NonNull; import android.annotation.Nullable; @@ -48,10 +48,11 @@ public class BubbleInfo implements Parcelable { @Nullable private String mAppName; private boolean mIsImportantConversation; + private boolean mShowAppBadge; public BubbleInfo(String key, int flags, @Nullable String shortcutId, @Nullable Icon icon, int userId, String packageName, @Nullable String title, @Nullable String appName, - boolean isImportantConversation) { + boolean isImportantConversation, boolean showAppBadge) { mKey = key; mFlags = flags; mShortcutId = shortcutId; @@ -61,6 +62,7 @@ public class BubbleInfo implements Parcelable { mTitle = title; mAppName = appName; mIsImportantConversation = isImportantConversation; + mShowAppBadge = showAppBadge; } private BubbleInfo(Parcel source) { @@ -73,6 +75,7 @@ public class BubbleInfo implements Parcelable { mTitle = source.readString(); mAppName = source.readString(); mIsImportantConversation = source.readBoolean(); + mShowAppBadge = source.readBoolean(); } public String getKey() { @@ -115,6 +118,10 @@ public class BubbleInfo implements Parcelable { return mIsImportantConversation; } + public boolean showAppBadge() { + return mShowAppBadge; + } + /** * Whether this bubble is currently being hidden from the stack. */ @@ -172,6 +179,7 @@ public class BubbleInfo implements Parcelable { parcel.writeString(mTitle); parcel.writeString(mAppName); parcel.writeBoolean(mIsImportantConversation); + parcel.writeBoolean(mShowAppBadge); } @NonNull diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupDrawable.kt index 887af17c9653..8681acf93ab3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupDrawable.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 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. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.wm.shell.common.bubbles +package com.android.wm.shell.shared.bubbles import android.annotation.ColorInt import android.graphics.Canvas diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupView.kt index 444fbf7884be..802d7d131d95 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupView.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 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. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.wm.shell.common.bubbles +package com.android.wm.shell.shared.bubbles import android.content.Context import android.graphics.Rect diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissCircleView.java index 7c5bb211a4cc..0c051560f714 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissCircleView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.common.bubbles; +package com.android.wm.shell.shared.bubbles; import android.content.Context; import android.content.res.Configuration; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissView.kt index e06de9e9353c..2bb66b0bbcd3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissView.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.common.bubbles +package com.android.wm.shell.shared.bubbles import android.animation.ObjectAnimator import android.content.Context diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/OWNERS b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/OWNERS index 08c70314973e..08c70314973e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/OWNERS +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/OWNERS diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RelativeTouchListener.kt index 4e55ba23407b..b1f4e331a98d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RelativeTouchListener.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.common.bubbles +package com.android.wm.shell.shared.bubbles import android.graphics.PointF import android.view.MotionEvent diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RemovedBubble.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RemovedBubble.java index f90591b84b7e..c83696c01613 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RemovedBubble.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RemovedBubble.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.common.bubbles; +package com.android.wm.shell.shared.bubbles; import android.annotation.NonNull; import android.os.Parcel; diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt index 434885f1089d..424d4bf5c6e8 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt @@ -22,10 +22,14 @@ import android.util.Log import com.android.window.flags.Flags /* - * A shared class to check desktop mode flags state. + * An enum to check desktop mode flags state. * - * The class computes whether a Desktop Windowing flag should be enabled by using the aconfig flag - * value and the developer option override state (if applicable). + * This enum provides a centralized way to control the behavior of flags related to desktop + * windowing features which are aiming for developer preview before their release. It allows + * developer option to override the default behavior of these flags. + * + * NOTE: Flags should only be added to this enum when they have received Product and UX + * alignment that the feature is ready for developer preview, otherwise just do a flag check. */ enum class DesktopModeFlags( // Function called to obtain aconfig flag value. @@ -44,7 +48,7 @@ enum class DesktopModeFlags( TASK_STACK_OBSERVER_IN_SHELL(Flags::enableTaskStackObserverInShell, true), SIZE_CONSTRAINTS(Flags::enableDesktopWindowingSizeConstraints, true), DISABLE_SNAP_RESIZE(Flags::disableNonResizableAppSnapResizing, true), - DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, true), + DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, false), ENABLE_DESKTOP_WINDOWING_TASK_LIMIT(Flags::enableDesktopWindowingTaskLimit, true), BACK_NAVIGATION(Flags::enableDesktopWindowingBackNavigation, true), EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/desktopmode/DesktopModeTransitionSource.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeTransitionSource.aidl index c968e809bf61..f7ddf71245e1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/desktopmode/DesktopModeTransitionSource.aidl +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeTransitionSource.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package com.android.wm.shell.common.desktopmode; +package com.android.wm.shell.shared.desktopmode; parcelable DesktopModeTransitionSource;
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/desktopmode/DesktopModeTransitionSource.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeTransitionSource.kt index dbbf178613b5..d15fbed409b8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/desktopmode/DesktopModeTransitionSource.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeTransitionSource.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.common.desktopmode +package com.android.wm.shell.shared.desktopmode import android.os.Parcel import android.os.Parcelable diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java new file mode 100644 index 000000000000..b92b8ef657a3 --- /dev/null +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2020 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.shared.handles; + +import static android.view.Display.DEFAULT_DISPLAY; + +import android.annotation.TargetApi; +import android.graphics.Rect; +import android.os.Build; +import android.os.Handler; +import android.view.CompositionSamplingListener; +import android.view.SurfaceControl; +import android.view.View; +import android.view.ViewRootImpl; +import android.view.ViewTreeObserver; + +import androidx.annotation.VisibleForTesting; + +import java.io.PrintWriter; +import java.util.concurrent.Executor; + +/** + * A helper class to sample regions on the screen and inspect its luminosity. + */ +@TargetApi(Build.VERSION_CODES.Q) +public class RegionSamplingHelper implements View.OnAttachStateChangeListener, + View.OnLayoutChangeListener { + + // Luminance threshold to determine black/white contrast for the navigation affordances. + // Passing the threshold of this luminance value will make the button black otherwise white + private static final float NAVIGATION_LUMINANCE_THRESHOLD = 0.5f; + // Luminance change threshold that allows applying new value if difference was exceeded + private static final float NAVIGATION_LUMINANCE_CHANGE_THRESHOLD = 0.05f; + + private final Handler mHandler = new Handler(); + private final View mSampledView; + + private final CompositionSamplingListener mSamplingListener; + + /** + * The requested sampling bounds that we want to sample from + */ + private final Rect mSamplingRequestBounds = new Rect(); + + /** + * The sampling bounds that are currently registered. + */ + private final Rect mRegisteredSamplingBounds = new Rect(); + private final SamplingCallback mCallback; + private final Executor mBackgroundExecutor; + private final SysuiCompositionSamplingListener mCompositionSamplingListener; + private boolean mSamplingEnabled = false; + private boolean mSamplingListenerRegistered = false; + + private float mLastMedianLuma; + private float mCurrentMedianLuma; + private boolean mWaitingOnDraw; + private boolean mIsDestroyed; + + private boolean mFirstSamplingAfterStart; + private boolean mWindowVisible; + private boolean mWindowHasBlurs; + private SurfaceControl mRegisteredStopLayer = null; + // A copy of mRegisteredStopLayer where we own the life cycle and can access from a bg thread. + private SurfaceControl mWrappedStopLayer = null; + private ViewTreeObserver.OnDrawListener mUpdateOnDraw = new ViewTreeObserver.OnDrawListener() { + @Override + public void onDraw() { + // We need to post the remove runnable, since it's not allowed to remove in onDraw + mHandler.post(mRemoveDrawRunnable); + RegionSamplingHelper.this.onDraw(); + } + }; + private Runnable mRemoveDrawRunnable = new Runnable() { + @Override + public void run() { + mSampledView.getViewTreeObserver().removeOnDrawListener(mUpdateOnDraw); + } + }; + + /** + * @deprecated Pass a main executor. + */ + public RegionSamplingHelper(View sampledView, SamplingCallback samplingCallback, + Executor backgroundExecutor) { + this(sampledView, samplingCallback, sampledView.getContext().getMainExecutor(), + backgroundExecutor); + } + + public RegionSamplingHelper(View sampledView, SamplingCallback samplingCallback, + Executor mainExecutor, Executor backgroundExecutor) { + this(sampledView, samplingCallback, mainExecutor, + backgroundExecutor, new SysuiCompositionSamplingListener()); + } + + @VisibleForTesting + RegionSamplingHelper(View sampledView, SamplingCallback samplingCallback, + Executor mainExecutor, Executor backgroundExecutor, + SysuiCompositionSamplingListener compositionSamplingListener) { + mBackgroundExecutor = backgroundExecutor; + mCompositionSamplingListener = compositionSamplingListener; + mSamplingListener = new CompositionSamplingListener(mainExecutor) { + @Override + public void onSampleCollected(float medianLuma) { + if (mSamplingEnabled) { + updateMedianLuma(medianLuma); + } + } + }; + mSampledView = sampledView; + mSampledView.addOnAttachStateChangeListener(this); + mSampledView.addOnLayoutChangeListener(this); + + mCallback = samplingCallback; + } + + /** + * Make callback accessible + */ + @VisibleForTesting + public SamplingCallback getCallback() { + return mCallback; + } + + private void onDraw() { + if (mWaitingOnDraw) { + mWaitingOnDraw = false; + updateSamplingListener(); + } + } + + public void start(Rect initialSamplingBounds) { + if (!mCallback.isSamplingEnabled()) { + return; + } + if (initialSamplingBounds != null) { + mSamplingRequestBounds.set(initialSamplingBounds); + } + mSamplingEnabled = true; + // make sure we notify once + mLastMedianLuma = -1; + mFirstSamplingAfterStart = true; + updateSamplingListener(); + } + + public void stop() { + mSamplingEnabled = false; + updateSamplingListener(); + } + + public void stopAndDestroy() { + stop(); + mBackgroundExecutor.execute(mSamplingListener::destroy); + mIsDestroyed = true; + } + + @Override + public void onViewAttachedToWindow(View view) { + updateSamplingListener(); + } + + @Override + public void onViewDetachedFromWindow(View view) { + stopAndDestroy(); + } + + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + updateSamplingRect(); + } + + private void updateSamplingListener() { + boolean isSamplingEnabled = mSamplingEnabled + && !mSamplingRequestBounds.isEmpty() + && mWindowVisible + && !mWindowHasBlurs + && (mSampledView.isAttachedToWindow() || mFirstSamplingAfterStart); + if (isSamplingEnabled) { + ViewRootImpl viewRootImpl = mSampledView.getViewRootImpl(); + SurfaceControl stopLayerControl = null; + if (viewRootImpl != null) { + stopLayerControl = viewRootImpl.getSurfaceControl(); + } + if (stopLayerControl == null || !stopLayerControl.isValid()) { + if (!mWaitingOnDraw) { + mWaitingOnDraw = true; + // The view might be attached but we haven't drawn yet, so wait until the + // next draw to update the listener again with the stop layer, such that our + // own drawing doesn't affect the sampling. + if (mHandler.hasCallbacks(mRemoveDrawRunnable)) { + mHandler.removeCallbacks(mRemoveDrawRunnable); + } else { + mSampledView.getViewTreeObserver().addOnDrawListener(mUpdateOnDraw); + } + } + // If there's no valid surface, let's just sample without a stop layer, so we + // don't have to delay + stopLayerControl = null; + } + if (!mSamplingRequestBounds.equals(mRegisteredSamplingBounds) + || mRegisteredStopLayer != stopLayerControl) { + // We only want to re-register if something actually changed + unregisterSamplingListener(); + mSamplingListenerRegistered = true; + SurfaceControl wrappedStopLayer = wrap(stopLayerControl); + + // pass this to background thread to avoid empty Rect race condition + final Rect boundsCopy = new Rect(mSamplingRequestBounds); + + mBackgroundExecutor.execute(() -> { + if (wrappedStopLayer != null && !wrappedStopLayer.isValid()) { + return; + } + mCompositionSamplingListener.register(mSamplingListener, DEFAULT_DISPLAY, + wrappedStopLayer, boundsCopy); + }); + mRegisteredSamplingBounds.set(mSamplingRequestBounds); + mRegisteredStopLayer = stopLayerControl; + mWrappedStopLayer = wrappedStopLayer; + } + mFirstSamplingAfterStart = false; + } else { + unregisterSamplingListener(); + } + } + + @VisibleForTesting + protected SurfaceControl wrap(SurfaceControl stopLayerControl) { + return stopLayerControl == null ? null : new SurfaceControl(stopLayerControl, + "regionSampling"); + } + + private void unregisterSamplingListener() { + if (mSamplingListenerRegistered) { + mSamplingListenerRegistered = false; + SurfaceControl wrappedStopLayer = mWrappedStopLayer; + mRegisteredStopLayer = null; + mWrappedStopLayer = null; + mRegisteredSamplingBounds.setEmpty(); + mBackgroundExecutor.execute(() -> { + mCompositionSamplingListener.unregister(mSamplingListener); + if (wrappedStopLayer != null && wrappedStopLayer.isValid()) { + wrappedStopLayer.release(); + } + }); + } + } + + private void updateMedianLuma(float medianLuma) { + mCurrentMedianLuma = medianLuma; + + // If the difference between the new luma and the current luma is larger than threshold + // then apply the current luma, this is to prevent small changes causing colors to flicker + if (Math.abs(mCurrentMedianLuma - mLastMedianLuma) + > NAVIGATION_LUMINANCE_CHANGE_THRESHOLD) { + mCallback.onRegionDarknessChanged( + medianLuma < NAVIGATION_LUMINANCE_THRESHOLD /* isRegionDark */); + mLastMedianLuma = medianLuma; + } + } + + public void updateSamplingRect() { + Rect sampledRegion = mCallback.getSampledRegion(mSampledView); + if (!mSamplingRequestBounds.equals(sampledRegion)) { + mSamplingRequestBounds.set(sampledRegion); + updateSamplingListener(); + } + } + + public void setWindowVisible(boolean visible) { + mWindowVisible = visible; + updateSamplingListener(); + } + + /** + * If we're blurring the shade window. + */ + public void setWindowHasBlurs(boolean hasBlurs) { + mWindowHasBlurs = hasBlurs; + updateSamplingListener(); + } + + public void dump(PrintWriter pw) { + dump("", pw); + } + + public void dump(String prefix, PrintWriter pw) { + pw.println(prefix + "RegionSamplingHelper:"); + pw.println(prefix + "\tsampleView isAttached: " + mSampledView.isAttachedToWindow()); + pw.println(prefix + "\tsampleView isScValid: " + (mSampledView.isAttachedToWindow() + ? mSampledView.getViewRootImpl().getSurfaceControl().isValid() + : "notAttached")); + pw.println(prefix + "\tmSamplingEnabled: " + mSamplingEnabled); + pw.println(prefix + "\tmSamplingListenerRegistered: " + mSamplingListenerRegistered); + pw.println(prefix + "\tmSamplingRequestBounds: " + mSamplingRequestBounds); + pw.println(prefix + "\tmRegisteredSamplingBounds: " + mRegisteredSamplingBounds); + pw.println(prefix + "\tmLastMedianLuma: " + mLastMedianLuma); + pw.println(prefix + "\tmCurrentMedianLuma: " + mCurrentMedianLuma); + pw.println(prefix + "\tmWindowVisible: " + mWindowVisible); + pw.println(prefix + "\tmWindowHasBlurs: " + mWindowHasBlurs); + pw.println(prefix + "\tmWaitingOnDraw: " + mWaitingOnDraw); + pw.println(prefix + "\tmRegisteredStopLayer: " + mRegisteredStopLayer); + pw.println(prefix + "\tmWrappedStopLayer: " + mWrappedStopLayer); + pw.println(prefix + "\tmIsDestroyed: " + mIsDestroyed); + } + + public interface SamplingCallback { + /** + * Called when the darkness of the sampled region changes + * @param isRegionDark true if the sampled luminance is below the luminance threshold + */ + void onRegionDarknessChanged(boolean isRegionDark); + + /** + * Get the sampled region of interest from the sampled view + * @param sampledView The view that this helper is attached to for convenience + * @return the region to be sampled in sceen coordinates. Return {@code null} to avoid + * sampling in this frame + */ + Rect getSampledRegion(View sampledView); + + /** + * @return if sampling should be enabled in the current configuration + */ + default boolean isSamplingEnabled() { + return true; + } + } + + @VisibleForTesting + public static class SysuiCompositionSamplingListener { + public void register(CompositionSamplingListener listener, + int displayId, SurfaceControl stopLayer, Rect samplingArea) { + CompositionSamplingListener.register(listener, displayId, stopLayer, samplingArea); + } + + /** + * Unregisters a sampling listener. + */ + public void unregister(CompositionSamplingListener listener) { + CompositionSamplingListener.unregister(listener); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java index ff2d46e11107..cf39415b3fe6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.pip; +package com.android.wm.shell.shared.pip; import static android.util.TypedValue.COMPLEX_UNIT_DIP; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitBounds.java index 88b752822a20..7c1faa667d9a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitBounds.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2024 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. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.wm.shell.util; +package com.android.wm.shell.shared.split; import android.graphics.Rect; import android.os.Parcel; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index d7da0515f228..ef679dae0157 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -74,6 +74,7 @@ import android.window.IBackAnimationRunner; import android.window.IOnBackInvokedCallback; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; +import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; @@ -1066,14 +1067,30 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont return true; } + private void kickStartAnimation() { + startSystemAnimation(); + + // Dispatch the first progress after animation start for + // smoothing the initial animation, instead of waiting for next + // onMove. + final BackMotionEvent backFinish = mCurrentTracker + .createProgressEvent(); + dispatchOnBackProgressed(mActiveCallback, backFinish); + if (!mBackGestureStarted) { + // if the down -> up gesture happened before animation + // start, we have to trigger the uninterruptible transition + // to finish the back animation. + startPostCommitAnimation(); + } + } + private void createAdapter() { IBackAnimationRunner runner = new IBackAnimationRunner.Stub() { @Override public void onAnimationStart( RemoteAnimationTarget[] apps, - RemoteAnimationTarget[] wallpapers, - RemoteAnimationTarget[] nonApps, + IBinder token, IBackAnimationFinishedCallback finishedCallback) { mShellExecutor.execute( () -> { @@ -1085,21 +1102,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } mBackAnimationFinishedCallback = finishedCallback; mApps = apps; - startSystemAnimation(); - mBackTransitionHandler.consumeQueuedTransitionIfNeeded(); - - // Dispatch the first progress after animation start for - // smoothing the initial animation, instead of waiting for next - // onMove. - final BackMotionEvent backFinish = mCurrentTracker - .createProgressEvent(); - dispatchOnBackProgressed(mActiveCallback, backFinish); - if (!mBackGestureStarted) { - // if the down -> up gesture happened before animation - // start, we have to trigger the uninterruptible transition - // to finish the back animation. - startPostCommitAnimation(); + // app only visible after transition ready, break for now. + if (token != null) { + return; } + kickStartAnimation(); + mBackTransitionHandler.consumeQueuedTransitionIfNeeded(); }); } @@ -1199,6 +1207,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont @NonNull SurfaceControl.Transaction st, @NonNull SurfaceControl.Transaction ft, @NonNull Transitions.TransitionFinishCallback finishCallback) { + if (info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) { + kickStartAnimation(); + } // Both mShellExecutor and Transitions#mMainExecutor are ShellMainThread, so we don't // need to post to ShellExecutor when called. if (info.getType() == WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION) { @@ -1262,19 +1273,24 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont ComponentName openComponent = null; int tmpSize; int openTaskId = INVALID_TASK_ID; + WindowContainerToken openToken = null; for (int j = init.getChanges().size() - 1; j >= 0; --j) { final TransitionInfo.Change change = init.getChanges().get(j); if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) { openComponent = findComponentName(change); openTaskId = findTaskId(change); + openToken = findToken(change); if (change.hasFlags(FLAG_SHOW_WALLPAPER)) { openShowWallpaper = true; } break; } } - if (openComponent == null && openTaskId == INVALID_TASK_ID) { - // shouldn't happen. + if (openComponent == null && openTaskId == INVALID_TASK_ID && openToken == null) { + // This shouldn't happen, but if that happen, consume the initial transition anyway. + Log.e(TAG, "Unable to merge following transition, cannot find the gesture " + + "animated target from the open transition=" + mOpenTransitionInfo); + mOpenTransitionInfo = null; return; } // find first non-prepare open target @@ -1305,7 +1321,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont boolean moveToTop = false; for (int j = info.getChanges().size() - 1; j >= 0; --j) { final TransitionInfo.Change change = info.getChanges().get(j); - if (isSameChangeTarget(openComponent, openTaskId, change)) { + if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) { moveToTop = change.hasFlags(FLAG_MOVED_TO_TOP); info.getChanges().remove(j); } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER)) @@ -1319,7 +1335,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont for (int i = 0; i < tmpSize; ++i) { final TransitionInfo.Change change = init.getChanges().get(i); if (moveToTop) { - if (isSameChangeTarget(openComponent, openTaskId, change)) { + if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) { change.setFlags(change.getFlags() | FLAG_MOVED_TO_TOP); } } @@ -1348,7 +1364,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont if (nonBackClose && nonBackOpen) { for (int j = info.getChanges().size() - 1; j >= 0; --j) { final TransitionInfo.Change change = info.getChanges().get(j); - if (isSameChangeTarget(openComponent, openTaskId, change)) { + if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) { info.getChanges().remove(j); } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))) { info.getChanges().remove(j); @@ -1358,6 +1374,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation transition, merge pending " + "transitions result=%s", info); + // Only handle one merge transition request. + mOpenTransitionInfo = null; } @Override @@ -1368,7 +1386,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mClosePrepareTransition = null; } // try to handle unexpected transition - mergePendingTransitions(info); + if (mOpenTransitionInfo != null) { + mergePendingTransitions(info); + } if (isNotGestureBackTransition(info) || shouldCancelAnimation(info) || !mCloseTransitionRequested) { @@ -1382,6 +1402,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } // Handle the commit transition if this handler is running the open transition. finishCallback.onTransitionFinished(null); + t.apply(); if (mCloseTransitionRequested) { if (mApps == null || mApps.length == 0) { if (mQueuedTransition == null) { @@ -1617,6 +1638,10 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont return false; } + private static WindowContainerToken findToken(TransitionInfo.Change change) { + return change.getContainer(); + } + private static ComponentName findComponentName(TransitionInfo.Change change) { final ComponentName componentName = change.getActivityComponent(); if (componentName != null) { @@ -1638,11 +1663,13 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } private static boolean isSameChangeTarget(ComponentName topActivity, int taskId, - TransitionInfo.Change change) { + WindowContainerToken token, TransitionInfo.Change change) { final ComponentName openChange = findComponentName(change); final int firstTaskId = findTaskId(change); + final WindowContainerToken openToken = findToken(change); return (openChange != null && openChange == topActivity) - || (firstTaskId != INVALID_TASK_ID && firstTaskId == taskId); + || (firstTaskId != INVALID_TASK_ID && firstTaskId == taskId) + || (openToken != null && token == openToken); } private static boolean canBeTransitionTarget(TransitionInfo.Change change) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index 021d3c32fd63..0c95934abf93 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -53,9 +53,9 @@ import com.android.launcher3.icons.BubbleIconFactory; import com.android.wm.shell.Flags; import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView; import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; -import com.android.wm.shell.common.bubbles.BubbleInfo; import com.android.wm.shell.shared.annotations.ShellBackgroundThread; import com.android.wm.shell.shared.annotations.ShellMainThread; +import com.android.wm.shell.shared.bubbles.BubbleInfo; import java.io.PrintWriter; import java.util.List; @@ -349,7 +349,8 @@ public class Bubble implements BubbleViewProvider { getPackageName(), getTitle(), getAppName(), - isImportantConversation()); + isImportantConversation(), + !isAppLaunchIntent()); } @Override @@ -568,11 +569,11 @@ public class Bubble implements BubbleViewProvider { @Nullable BubbleBarLayerView layerView, BubbleIconFactory iconFactory, boolean skipInflation) { + ProtoLog.v(WM_SHELL_BUBBLES, "Inflate bubble key=%s", getKey()); if (Flags.bubbleViewInfoExecutors()) { - if (mInflationTask != null && mInflationTask.getStatus() != FINISHED) { - mInflationTask.cancel(true /* mayInterruptIfRunning */); + if (mInflationTask != null && !mInflationTask.isFinished()) { + mInflationTask.cancel(); } - // TODO(b/353894869): switch to executors mInflationTask = new BubbleViewInfoTask(this, context, expandedViewManager, @@ -583,11 +584,12 @@ public class Bubble implements BubbleViewProvider { iconFactory, skipInflation, callback, - mMainExecutor); + mMainExecutor, + mBgExecutor); if (mInflateSynchronously) { - mInflationTask.onPostExecute(mInflationTask.doInBackground()); + mInflationTask.startSync(); } else { - mInflationTask.execute(); + mInflationTask.start(); } } else { if (mInflationTaskLegacy != null && mInflationTaskLegacy.getStatus() != FINISHED) { @@ -625,7 +627,7 @@ public class Bubble implements BubbleViewProvider { if (mInflationTask == null) { return; } - mInflationTask.cancel(true /* mayInterruptIfRunning */); + mInflationTask.cancel(); } else { if (mInflationTaskLegacy == null) { return; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index dabfeebf5cce..c545d73734f0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -104,14 +104,14 @@ import com.android.wm.shell.common.SingleInstanceRemoteListener; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; -import com.android.wm.shell.common.bubbles.BubbleBarLocation; -import com.android.wm.shell.common.bubbles.BubbleBarUpdate; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; import com.android.wm.shell.pip.PinnedStackListenerForwarder; import com.android.wm.shell.shared.annotations.ShellBackgroundThread; import com.android.wm.shell.shared.annotations.ShellMainThread; +import com.android.wm.shell.shared.bubbles.BubbleBarLocation; +import com.android.wm.shell.shared.bubbles.BubbleBarUpdate; import com.android.wm.shell.sysui.ConfigurationChangeListener; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; @@ -1225,7 +1225,7 @@ public class BubbleController implements ConfigurationChangeListener, mBubblePositioner.setBubbleBarLocation(location); mBubblePositioner.setBubbleBarTopOnScreen(topOnScreen); if (mBubbleData.getSelectedBubble() != null) { - mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ true); + showExpandedViewForBubbleBar(); } } @@ -1243,7 +1243,7 @@ public class BubbleController implements ConfigurationChangeListener, } if (selectedBubbleKey != null && !selectedBubbleKey.equals(bubbleKey)) { // We did not remove the selected bubble. Expand it again - mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ true); + showExpandedViewForBubbleBar(); } } @@ -1997,15 +1997,10 @@ public class BubbleController implements ConfigurationChangeListener, @Override public void expansionChanged(boolean isExpanded) { - if (mLayerView != null) { - if (!isExpanded) { - mLayerView.collapse(); - } else { - BubbleViewProvider selectedBubble = mBubbleData.getSelectedBubble(); - if (selectedBubble != null) { - mLayerView.showExpandedView(selectedBubble); - } - } + // in bubble bar mode, let the request to show the expanded view come from launcher. + // only collapse here if we're collapsing. + if (mLayerView != null && !isExpanded) { + mLayerView.collapse(); } } @@ -2151,6 +2146,13 @@ public class BubbleController implements ConfigurationChangeListener, } }; + private void showExpandedViewForBubbleBar() { + BubbleViewProvider selectedBubble = mBubbleData.getSelectedBubble(); + if (selectedBubble != null && mLayerView != null) { + mLayerView.showExpandedView(selectedBubble); + } + } + private void updateOverflowButtonDot() { BubbleOverflow overflow = mBubbleData.getOverflow(); if (overflow == null) return; @@ -2217,7 +2219,6 @@ public class BubbleController implements ConfigurationChangeListener, // And since all children are removed, remove the summary. removeCallback.accept(-1); - // TODO: (b/145659174) remove references to mSuppressedGroupKeys once fully migrated mBubbleData.addSummaryToSuppress(summary.getStatusBarNotification().getGroupKey(), summary.getKey()); } @@ -2533,6 +2534,15 @@ public class BubbleController implements ConfigurationChangeListener, if (mLayerView != null) mLayerView.updateExpandedView(); }); } + + @Override + public void showExpandedView() { + mMainExecutor.execute(() -> { + if (mLayerView != null) { + showExpandedViewForBubbleBar(); + } + }); + } } private class BubblesImpl implements Bubbles { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index 4ad1802cba7f..709a7bdc61f2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -41,10 +41,10 @@ import com.android.internal.protolog.ProtoLog; import com.android.internal.util.FrameworkStatsLog; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.Bubbles.DismissReason; -import com.android.wm.shell.common.bubbles.BubbleBarUpdate; -import com.android.wm.shell.common.bubbles.RemovedBubble; import com.android.wm.shell.shared.annotations.ShellBackgroundThread; import com.android.wm.shell.shared.annotations.ShellMainThread; +import com.android.wm.shell.shared.bubbles.BubbleBarUpdate; +import com.android.wm.shell.shared.bubbles.RemovedBubble; import java.io.PrintWriter; import java.util.ArrayList; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt index 4e80e903b522..ec4854b47aff 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt @@ -16,7 +16,7 @@ package com.android.wm.shell.bubbles -import com.android.wm.shell.common.bubbles.BubbleBarLocation +import com.android.wm.shell.shared.bubbles.BubbleBarLocation /** Manager interface for bubble expanded views. */ interface BubbleExpandedViewManager { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt index bdb09e11d5ad..fd110a276826 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt @@ -17,8 +17,8 @@ package com.android.wm.shell.bubbles import android.graphics.Color import com.android.wm.shell.R -import com.android.wm.shell.common.bubbles.BubblePopupDrawable -import com.android.wm.shell.common.bubbles.BubblePopupView +import com.android.wm.shell.shared.bubbles.BubblePopupDrawable +import com.android.wm.shell.shared.bubbles.BubblePopupView /** * A convenience method to setup the [BubblePopupView] with the correct config using local resources diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index 0cf187bd9c0f..c386c9398624 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -32,7 +32,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.protolog.ProtoLog; import com.android.launcher3.icons.IconNormalizer; import com.android.wm.shell.R; -import com.android.wm.shell.common.bubbles.BubbleBarLocation; +import com.android.wm.shell.shared.bubbles.BubbleBarLocation; /** * Keeps track of display size, configuration, and specific bubble sizes. One place for all diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 53bbf888df5a..2795881f0938 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -24,10 +24,10 @@ import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING; import static com.android.wm.shell.bubbles.BubblePositioner.StackPinnedEdge.LEFT; import static com.android.wm.shell.bubbles.BubblePositioner.StackPinnedEdge.RIGHT; -import static com.android.wm.shell.common.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES; import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_IN; import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_OUT; +import static com.android.wm.shell.shared.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -91,8 +91,8 @@ import com.android.wm.shell.bubbles.animation.PhysicsAnimationLayout; import com.android.wm.shell.bubbles.animation.StackAnimationController; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.bubbles.DismissView; -import com.android.wm.shell.common.bubbles.RelativeTouchListener; +import com.android.wm.shell.shared.bubbles.DismissView; +import com.android.wm.shell.shared.bubbles.RelativeTouchListener; import com.android.wm.shell.shared.animation.Interpolators; import com.android.wm.shell.shared.animation.PhysicsAnimator; import com.android.wm.shell.shared.magnetictarget.MagnetizedObject; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java index 03a2efd902f9..13855f73fb4a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java @@ -20,6 +20,7 @@ import static com.android.wm.shell.bubbles.BadgedImageView.DEFAULT_PATH_SIZE; import static com.android.wm.shell.bubbles.BadgedImageView.WHITE_SCRIM_ALPHA; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES; import android.annotation.NonNull; import android.annotation.Nullable; @@ -34,13 +35,13 @@ import android.graphics.Matrix; import android.graphics.Path; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; -import android.os.AsyncTask; import android.util.Log; import android.util.PathParser; import android.view.LayoutInflater; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; +import com.android.internal.protolog.ProtoLog; import com.android.launcher3.icons.BitmapInfo; import com.android.launcher3.icons.BubbleIconFactory; import com.android.wm.shell.R; @@ -50,15 +51,14 @@ import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; import java.lang.ref.WeakReference; import java.util.Objects; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; /** * Simple task to inflate views & load necessary info to display a bubble. */ -// TODO(b/353894869): switch to executors -public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask.BubbleViewInfo> { +public class BubbleViewInfoTask { private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleViewInfoTask" : TAG_BUBBLES; - /** * Callback to find out when the bubble has been inflated & necessary data loaded. */ @@ -69,17 +69,22 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask void onBubbleViewsReady(Bubble bubble); } - private Bubble mBubble; - private WeakReference<Context> mContext; - private WeakReference<BubbleExpandedViewManager> mExpandedViewManager; - private WeakReference<BubbleTaskViewFactory> mTaskViewFactory; - private WeakReference<BubblePositioner> mPositioner; - private WeakReference<BubbleStackView> mStackView; - private WeakReference<BubbleBarLayerView> mLayerView; - private BubbleIconFactory mIconFactory; - private boolean mSkipInflation; - private Callback mCallback; - private Executor mMainExecutor; + private final Bubble mBubble; + private final WeakReference<Context> mContext; + private final WeakReference<BubbleExpandedViewManager> mExpandedViewManager; + private final WeakReference<BubbleTaskViewFactory> mTaskViewFactory; + private final WeakReference<BubblePositioner> mPositioner; + private final WeakReference<BubbleStackView> mStackView; + private final WeakReference<BubbleBarLayerView> mLayerView; + private final BubbleIconFactory mIconFactory; + private final boolean mSkipInflation; + private final Callback mCallback; + private final Executor mMainExecutor; + private final Executor mBgExecutor; + + private final AtomicBoolean mStarted = new AtomicBoolean(); + private final AtomicBoolean mCancelled = new AtomicBoolean(); + private final AtomicBoolean mFinished = new AtomicBoolean(); /** * Creates a task to load information for the provided {@link Bubble}. Once all info @@ -95,7 +100,8 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask BubbleIconFactory factory, boolean skipInflation, Callback c, - Executor mainExecutor) { + Executor mainExecutor, + Executor bgExecutor) { mBubble = b; mContext = new WeakReference<>(context); mExpandedViewManager = new WeakReference<>(expandedViewManager); @@ -107,40 +113,123 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask mSkipInflation = skipInflation; mCallback = c; mMainExecutor = mainExecutor; + mBgExecutor = bgExecutor; } - @Override - protected BubbleViewInfo doInBackground(Void... voids) { + /** + * Load bubble view info in background using {@code bgExecutor} specified in constructor. + * <br> + * Use {@link #cancel()} to stop the task. + * + * @throws IllegalStateException if the task is already started + */ + public void start() { + verifyCanStart(); + if (mCancelled.get()) { + // We got cancelled even before start was called. Exit early + mFinished.set(true); + return; + } + mBgExecutor.execute(() -> { + if (mCancelled.get()) { + // We got cancelled while background executor was busy and this was waiting + mFinished.set(true); + return; + } + BubbleViewInfo viewInfo = loadViewInfo(); + if (mCancelled.get()) { + // Do not schedule anything on main executor if we got cancelled. + // Loading view info involves inflating views and it is possible we get cancelled + // during it. + mFinished.set(true); + return; + } + mMainExecutor.execute(() -> { + // Before updating view info check that we did not get cancelled while waiting + // main executor to pick up the work + if (!mCancelled.get()) { + updateViewInfo(viewInfo); + } + mFinished.set(true); + }); + }); + } + + private void verifyCanStart() { + if (mStarted.getAndSet(true)) { + throw new IllegalStateException("Task already started"); + } + } + + /** + * Load bubble view info synchronously. + * + * @throws IllegalStateException if the task is already started + */ + public void startSync() { + verifyCanStart(); + if (mCancelled.get()) { + mFinished.set(true); + return; + } + updateViewInfo(loadViewInfo()); + mFinished.set(true); + } + + /** + * Cancel the task. Stops the task from running if called before {@link #start()} or + * {@link #startSync()} + */ + public void cancel() { + mCancelled.set(true); + } + + /** + * Return {@code true} when the task has completed loading the view info. + */ + public boolean isFinished() { + return mFinished.get(); + } + + @Nullable + private BubbleViewInfo loadViewInfo() { if (!verifyState()) { // If we're in an inconsistent state, then switched modes and should just bail now. return null; } + ProtoLog.v(WM_SHELL_BUBBLES, "Task loading bubble view info key=%s", mBubble.getKey()); if (mLayerView.get() != null) { - return BubbleViewInfo.populateForBubbleBar(mContext.get(), mExpandedViewManager.get(), - mTaskViewFactory.get(), mPositioner.get(), mLayerView.get(), mIconFactory, - mBubble, mSkipInflation); + return BubbleViewInfo.populateForBubbleBar(mContext.get(), mTaskViewFactory.get(), + mLayerView.get(), mIconFactory, mBubble, mSkipInflation); } else { - return BubbleViewInfo.populate(mContext.get(), mExpandedViewManager.get(), - mTaskViewFactory.get(), mPositioner.get(), mStackView.get(), mIconFactory, - mBubble, mSkipInflation); + return BubbleViewInfo.populate(mContext.get(), mTaskViewFactory.get(), + mPositioner.get(), mStackView.get(), mIconFactory, mBubble, mSkipInflation); } } - @Override - protected void onPostExecute(BubbleViewInfo viewInfo) { - if (isCancelled() || viewInfo == null) { + private void updateViewInfo(@Nullable BubbleViewInfo viewInfo) { + if (viewInfo == null || !verifyState()) { return; } - - mMainExecutor.execute(() -> { - if (!verifyState()) { - return; + ProtoLog.v(WM_SHELL_BUBBLES, "Task updating bubble view info key=%s", mBubble.getKey()); + if (!mBubble.isInflated()) { + if (viewInfo.expandedView != null) { + ProtoLog.v(WM_SHELL_BUBBLES, "Task initializing expanded view key=%s", + mBubble.getKey()); + viewInfo.expandedView.initialize(mExpandedViewManager.get(), mStackView.get(), + mPositioner.get(), false /* isOverflow */, viewInfo.taskView); + } else if (viewInfo.bubbleBarExpandedView != null) { + ProtoLog.v(WM_SHELL_BUBBLES, "Task initializing bubble bar expanded view key=%s", + mBubble.getKey()); + viewInfo.bubbleBarExpandedView.initialize(mExpandedViewManager.get(), + mPositioner.get(), false /* isOverflow */, viewInfo.taskView); } - mBubble.setViewInfo(viewInfo); - if (mCallback != null) { - mCallback.onBubbleViewsReady(mBubble); - } - }); + } + + mBubble.setViewInfo(viewInfo); + if (mCallback != null) { + mCallback.onBubbleViewsReady(mBubble); + } } private boolean verifyState() { @@ -158,6 +247,9 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask public static class BubbleViewInfo { // TODO(b/273312602): for foldables it might make sense to populate all of the views + // Only set if views where inflated as part of the task + @Nullable BubbleTaskView taskView; + // Always populated ShortcutInfo shortcutInfo; String appName; @@ -177,9 +269,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask @Nullable public static BubbleViewInfo populateForBubbleBar(Context c, - BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory taskViewFactory, - BubblePositioner positioner, BubbleBarLayerView layerView, BubbleIconFactory iconFactory, Bubble b, @@ -187,12 +277,11 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask BubbleViewInfo info = new BubbleViewInfo(); if (!skipInflation && !b.isInflated()) { - BubbleTaskView bubbleTaskView = b.getOrCreateBubbleTaskView(taskViewFactory); + ProtoLog.v(WM_SHELL_BUBBLES, "Task inflating bubble bar views key=%s", b.getKey()); + info.taskView = b.getOrCreateBubbleTaskView(taskViewFactory); LayoutInflater inflater = LayoutInflater.from(c); info.bubbleBarExpandedView = (BubbleBarExpandedView) inflater.inflate( R.layout.bubble_bar_expanded_view, layerView, false /* attachToRoot */); - info.bubbleBarExpandedView.initialize( - expandedViewManager, positioner, false /* isOverflow */, bubbleTaskView); } if (!populateCommonInfo(info, c, b, iconFactory)) { @@ -206,7 +295,6 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask @VisibleForTesting @Nullable public static BubbleViewInfo populate(Context c, - BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory taskViewFactory, BubblePositioner positioner, BubbleStackView stackView, @@ -217,17 +305,15 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask // View inflation: only should do this once per bubble if (!skipInflation && !b.isInflated()) { + ProtoLog.v(WM_SHELL_BUBBLES, "Task inflating bubble views key=%s", b.getKey()); LayoutInflater inflater = LayoutInflater.from(c); info.imageView = (BadgedImageView) inflater.inflate( R.layout.bubble_view, stackView, false /* attachToRoot */); info.imageView.initialize(positioner); - BubbleTaskView bubbleTaskView = b.getOrCreateBubbleTaskView(taskViewFactory); + info.taskView = b.getOrCreateBubbleTaskView(taskViewFactory); info.expandedView = (BubbleExpandedView) inflater.inflate( R.layout.bubble_expanded_view, stackView, false /* attachToRoot */); - info.expandedView.initialize( - expandedViewManager, stackView, positioner, false /* isOverflow */, - bubbleTaskView); } if (!populateCommonInfo(info, c, b, iconFactory)) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java index 9a27fb65ac2c..62895fe7c7cc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java @@ -38,9 +38,9 @@ import android.window.ScreenCapture.SynchronousScreenCaptureListener; import androidx.annotation.IntDef; import androidx.annotation.Nullable; -import com.android.wm.shell.common.bubbles.BubbleBarLocation; -import com.android.wm.shell.common.bubbles.BubbleBarUpdate; import com.android.wm.shell.shared.annotations.ExternalThread; +import com.android.wm.shell.shared.bubbles.BubbleBarLocation; +import com.android.wm.shell.shared.bubbles.BubbleBarUpdate; import java.lang.annotation.Retention; import java.lang.annotation.Target; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt index 48692d41016e..00a81727a9ac 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt @@ -18,7 +18,7 @@ package com.android.wm.shell.bubbles import com.android.wm.shell.R -import com.android.wm.shell.common.bubbles.DismissView +import com.android.wm.shell.shared.bubbles.DismissView fun DismissView.setup() { setup(DismissView.Config( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl index 5c789749412c..1855b938f48e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl @@ -20,7 +20,7 @@ import android.content.Intent; import android.graphics.Rect; import android.content.pm.ShortcutInfo; import com.android.wm.shell.bubbles.IBubblesListener; -import com.android.wm.shell.common.bubbles.BubbleBarLocation; +import com.android.wm.shell.shared.bubbles.BubbleBarLocation; /** * Interface that is exposed to remote callers (launcher) to manipulate the bubbles feature when @@ -53,4 +53,6 @@ interface IBubbles { oneway void showShortcutBubble(in ShortcutInfo info) = 12; oneway void showAppBubble(in Intent intent) = 13; + + oneway void showExpandedView() = 14; }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl index 14d29cd887bb..eb907dbb6597 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl @@ -17,7 +17,7 @@ package com.android.wm.shell.bubbles; import android.os.Bundle; -import com.android.wm.shell.common.bubbles.BubbleBarLocation; +import com.android.wm.shell.shared.bubbles.BubbleBarLocation; /** * Listener interface that Launcher attaches to SystemUI to get bubbles callbacks. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index 6d868d215482..f90b2aa95555 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -46,7 +46,7 @@ import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.BubbleTaskView; import com.android.wm.shell.bubbles.BubbleTaskViewHelper; import com.android.wm.shell.bubbles.Bubbles; -import com.android.wm.shell.common.bubbles.BubbleBarLocation; +import com.android.wm.shell.shared.bubbles.BubbleBarLocation; import com.android.wm.shell.taskview.TaskView; import java.util.function.Supplier; @@ -228,6 +228,13 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView public void onDismissBubble(Bubble bubble) { mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_GESTURE); } + + @Override + public void onMoveToFullscreen(Bubble bubble) { + if (mTaskView != null) { + mTaskView.moveToFullscreen(); + } + } }); mHandleView.setOnClickListener(view -> { mMenuViewController.showMenu(true /* animated */); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt index eeb5c94c8f81..07463bb024a2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt @@ -20,8 +20,8 @@ import android.annotation.SuppressLint import android.view.MotionEvent import android.view.View import com.android.wm.shell.bubbles.BubblePositioner -import com.android.wm.shell.common.bubbles.DismissView -import com.android.wm.shell.common.bubbles.RelativeTouchListener +import com.android.wm.shell.shared.bubbles.DismissView +import com.android.wm.shell.shared.bubbles.RelativeTouchListener import com.android.wm.shell.shared.magnetictarget.MagnetizedObject /** Controller for handling drag interactions with [BubbleBarExpandedView] */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java index ac424532e87b..1c9c195cf718 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java @@ -44,9 +44,9 @@ import com.android.wm.shell.bubbles.BubbleViewProvider; import com.android.wm.shell.bubbles.DeviceConfig; import com.android.wm.shell.bubbles.DismissViewUtils; import com.android.wm.shell.bubbles.bar.BubbleBarExpandedViewDragController.DragListener; -import com.android.wm.shell.common.bubbles.BaseBubblePinController; -import com.android.wm.shell.common.bubbles.BubbleBarLocation; -import com.android.wm.shell.common.bubbles.DismissView; +import com.android.wm.shell.shared.bubbles.BaseBubblePinController; +import com.android.wm.shell.shared.bubbles.BubbleBarLocation; +import com.android.wm.shell.shared.bubbles.DismissView; import kotlin.Unit; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java index 0d72998eb2e8..514810745e10 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java @@ -29,6 +29,7 @@ import android.view.ViewGroup; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringForce; +import com.android.wm.shell.Flags; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.Bubble; import com.android.wm.shell.shared.animation.PhysicsAnimator; @@ -219,6 +220,21 @@ class BubbleBarMenuViewController { } )); + if (Flags.enableBubbleAnything() || Flags.enableBubbleToFullscreen()) { + menuActions.add(new BubbleBarMenuView.MenuAction( + Icon.createWithResource(resources, + R.drawable.desktop_mode_ic_handle_menu_fullscreen), + resources.getString(R.string.bubble_fullscreen_text), + tintColor, + view -> { + hideMenu(true /* animated */); + if (mListener != null) { + mListener.onMoveToFullscreen(bubble); + } + } + )); + } + return menuActions; } @@ -249,5 +265,10 @@ class BubbleBarMenuViewController { * Dismiss bubble and remove it from the bubble stack */ void onDismissBubble(Bubble bubble); + + /** + * Move the bubble to fullscreen. + */ + void onMoveToFullscreen(Bubble bubble); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt index e108f7be48c7..9fd255ded0ad 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt @@ -34,9 +34,9 @@ import com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME import com.android.wm.shell.bubbles.BubbleEducationController import com.android.wm.shell.bubbles.BubbleViewProvider import com.android.wm.shell.bubbles.setup -import com.android.wm.shell.common.bubbles.BubblePopupDrawable -import com.android.wm.shell.common.bubbles.BubblePopupView import com.android.wm.shell.shared.animation.PhysicsAnimator +import com.android.wm.shell.shared.bubbles.BubblePopupDrawable +import com.android.wm.shell.shared.bubbles.BubblePopupView import kotlin.math.roundToInt /** Manages bubble education presentation and animation */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt index 651bf022e07d..23ba2bff5ebc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt @@ -25,8 +25,8 @@ import android.widget.FrameLayout import androidx.core.view.updateLayoutParams import com.android.wm.shell.R import com.android.wm.shell.bubbles.BubblePositioner -import com.android.wm.shell.common.bubbles.BaseBubblePinController -import com.android.wm.shell.common.bubbles.BubbleBarLocation +import com.android.wm.shell.shared.bubbles.BaseBubblePinController +import com.android.wm.shell.shared.bubbles.BubbleBarLocation /** * Controller to manage pinning bubble bar to left or right when dragging starts from the bubble bar diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java index dcbc72ab0d32..f532be6b8277 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java @@ -23,6 +23,7 @@ import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.RemoteException; import android.util.ArraySet; +import android.util.Size; import android.util.Slog; import android.util.SparseArray; import android.view.Display; @@ -193,8 +194,8 @@ public class DisplayController { /** Called when a display rotate requested. */ - public void onDisplayRotateRequested(WindowContainerTransaction wct, int displayId, - int fromRotation, int toRotation) { + public void onDisplayChangeRequested(WindowContainerTransaction wct, int displayId, + Rect startAbsBounds, Rect endAbsBounds, int fromRotation, int toRotation) { synchronized (mDisplays) { final DisplayRecord dr = mDisplays.get(displayId); if (dr == null) { @@ -203,7 +204,16 @@ public class DisplayController { } if (dr.mDisplayLayout != null) { - dr.mDisplayLayout.rotateTo(dr.mContext.getResources(), toRotation); + if (endAbsBounds != null) { + // If there is a change in the display dimensions update the layout as well; + // note that endAbsBounds should ignore any potential rotation changes, so + // we still need to rotate the layout after if needed. + dr.mDisplayLayout.resizeTo(dr.mContext.getResources(), + new Size(endAbsBounds.width(), endAbsBounds.height())); + } + if (fromRotation != toRotation) { + dr.mDisplayLayout.rotateTo(dr.mContext.getResources(), toRotation); + } } mChangeController.dispatchOnDisplayChange( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java index 84e32a229f9e..b6a1686bd087 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java @@ -35,6 +35,7 @@ import android.graphics.Rect; import android.os.SystemProperties; import android.provider.Settings; import android.util.DisplayMetrics; +import android.util.Size; import android.view.Display; import android.view.DisplayCutout; import android.view.DisplayInfo; @@ -244,6 +245,16 @@ public class DisplayLayout { recalcInsets(res); } + /** + * Update the dimensions of this layout. + */ + public void resizeTo(Resources res, Size displaySize) { + mWidth = displaySize.getWidth(); + mHeight = displaySize.getHeight(); + + recalcInsets(res); + } + /** Get this layout's non-decor insets. */ public Rect nonDecorInsets() { return mNonDecorInsets; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java index fad3dee1f927..1929729eb1ad 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java @@ -42,6 +42,7 @@ public class ScreenshotUtils { .setSourceCrop(crop) .setCaptureSecureLayers(true) .setAllowProtected(true) + .setHintForSeamlessTransition(true) .build())); } @@ -78,6 +79,9 @@ public class ScreenshotUtils { mTransaction.setColorSpace(mScreenshot, buffer.getColorSpace()); mTransaction.reparent(mScreenshot, mParentSurfaceControl); mTransaction.setLayer(mScreenshot, mLayer); + if (buffer.containsHdrLayers()) { + mTransaction.setDimmingEnabled(mScreenshot, false); + } mTransaction.show(mScreenshot); mTransaction.apply(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index 442036ff5781..e7848e27d7ed 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -58,6 +58,7 @@ import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.shared.animation.Interpolators; +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; /** * Divider for multi window splits. @@ -228,7 +229,9 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { : R.dimen.split_divider_handle_region_width); mHandleRegionHeight = getResources().getDimensionPixelSize(isLeftRightSplit ? R.dimen.split_divider_handle_region_width - : R.dimen.split_divider_handle_region_height); + : DesktopModeStatus.canEnterDesktopMode(mContext) + ? R.dimen.desktop_mode_portrait_split_divider_handle_region_height + : R.dimen.split_divider_handle_region_height); } void onInsetsChanged(InsetsState insetsState, boolean animate) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java index c2ee223b916a..972b78f6ca9a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java @@ -39,6 +39,7 @@ import android.view.InsetsState; import android.view.accessibility.AccessibilityManager; import com.android.internal.annotations.VisibleForTesting; +import com.android.window.flags.Flags; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener; @@ -67,6 +68,7 @@ import java.util.List; import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.IntPredicate; import java.util.function.Predicate; /** @@ -189,6 +191,9 @@ public class CompatUIController implements OnDisplaysChangedListener, @NonNull private final CompatUIStatusManager mCompatUIStatusManager; + @NonNull + private final IntPredicate mInDesktopModePredicate; + public CompatUIController(@NonNull Context context, @NonNull ShellInit shellInit, @NonNull ShellController shellController, @@ -202,7 +207,8 @@ public class CompatUIController implements OnDisplaysChangedListener, @NonNull CompatUIConfiguration compatUIConfiguration, @NonNull CompatUIShellCommandHandler compatUIShellCommandHandler, @NonNull AccessibilityManager accessibilityManager, - @NonNull CompatUIStatusManager compatUIStatusManager) { + @NonNull CompatUIStatusManager compatUIStatusManager, + @NonNull IntPredicate isDesktopModeEnablePredicate) { mContext = context; mShellController = shellController; mDisplayController = displayController; @@ -218,6 +224,7 @@ public class CompatUIController implements OnDisplaysChangedListener, mDisappearTimeSupplier = flags -> accessibilityManager.getRecommendedTimeoutMillis( DISAPPEAR_DELAY_MS, flags); mCompatUIStatusManager = compatUIStatusManager; + mInDesktopModePredicate = isDesktopModeEnablePredicate; shellInit.addInitCallback(this::onInit, this); } @@ -251,7 +258,9 @@ public class CompatUIController implements OnDisplaysChangedListener, updateActiveTaskInfo(taskInfo); } - if (taskInfo.configuration == null || taskListener == null) { + // We close all the Compat UI educations in case we're in desktop mode. + if (taskInfo.configuration == null || taskListener == null + || isInDesktopMode(taskInfo.displayId)) { // Null token means the current foreground activity is not in compatibility mode. removeLayouts(taskInfo.taskId); return; @@ -350,7 +359,6 @@ public class CompatUIController implements OnDisplaysChangedListener, mOnInsetsChangedListeners.remove(displayId); } - @Override public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { updateDisplayLayout(displayId); @@ -692,7 +700,8 @@ public class CompatUIController implements OnDisplaysChangedListener, mContext.startActivityAsUser(intent, userHandle); } - private void removeLayouts(int taskId) { + @VisibleForTesting + void removeLayouts(int taskId) { final CompatUIWindowManager compatLayout = mActiveCompatLayouts.get(taskId); if (compatLayout != null) { compatLayout.release(); @@ -825,4 +834,9 @@ public class CompatUIController implements OnDisplaysChangedListener, boolean mHasShownCameraCompatHint; boolean mHasShownUserAspectRatioSettingsButtonHint; } + + private boolean isInDesktopMode(int displayId) { + return Flags.skipCompatUiEducationInDesktopMode() + && mInDesktopModePredicate.test(displayId); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java index 8ce7837e451f..17869e918d67 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java @@ -19,8 +19,6 @@ package com.android.wm.shell.compatui; import static android.view.WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP; import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI; -import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.DESKTOP_WINDOWING_MODE; - import android.annotation.NonNull; import android.annotation.Nullable; import android.app.TaskInfo; @@ -40,6 +38,7 @@ import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState; import com.android.wm.shell.compatui.api.CompatUIEvent; import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonAppeared; import com.android.wm.shell.shared.desktopmode.DesktopModeFlags; +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import java.util.function.Consumer; @@ -83,7 +82,7 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract { super(context, taskInfo, syncQueue, taskListener, displayLayout); mCallback = callback; mHasSizeCompat = taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat(); - if (DESKTOP_WINDOWING_MODE.isEnabled(mContext) + if (DesktopModeStatus.canEnterDesktopMode(context) && DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(context)) { // Don't show the SCM button for freeform tasks mHasSizeCompat &= !taskInfo.isFreeform(); @@ -139,7 +138,7 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract { boolean canShow) { final boolean prevHasSizeCompat = mHasSizeCompat; mHasSizeCompat = taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat(); - if (DESKTOP_WINDOWING_MODE.isEnabled(mContext) + if (DesktopModeStatus.canEnterDesktopMode(mContext) && DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)) { // Don't show the SCM button for freeform tasks mHasSizeCompat &= !taskInfo.isFreeform(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 98536bf98f0b..4adea233b734 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -137,6 +137,7 @@ import dagger.Module; import dagger.Provides; import java.util.Optional; +import java.util.function.IntPredicate; /** * Provides basic dependencies from {@link com.android.wm.shell}, these dependencies are only @@ -147,7 +148,11 @@ import java.util.Optional; * dependencies that are device/form factor SystemUI implementation specific should go into their * respective modules (ie. {@link WMShellModule} for handheld, {@link TvWMShellModule} for tv, etc.) */ -@Module(includes = WMShellConcurrencyModule.class) +@Module( + includes = { + WMShellConcurrencyModule.class, + WMShellCoroutinesModule.class + }) public abstract class WMShellBaseModule { // @@ -261,6 +266,7 @@ public abstract class WMShellBaseModule { Lazy<CompatUIShellCommandHandler> compatUIShellCommandHandler, Lazy<AccessibilityManager> accessibilityManager, CompatUIRepository compatUIRepository, + Optional<DesktopModeTaskRepository> desktopModeTaskRepository, @NonNull CompatUIState compatUIState, @NonNull CompatUIComponentIdGenerator componentIdGenerator, @NonNull CompatUIComponentFactory compatUIComponentFactory, @@ -273,6 +279,10 @@ public abstract class WMShellBaseModule { new DefaultCompatUIHandler(compatUIRepository, compatUIState, componentIdGenerator, compatUIComponentFactory, mainExecutor)); } + final IntPredicate inDesktopModePredicate = + desktopModeTaskRepository.<IntPredicate>map(modeTaskRepository -> displayId -> + modeTaskRepository.getVisibleTaskCount(displayId) > 0) + .orElseGet(() -> displayId -> false); return Optional.of( new CompatUIController( context, @@ -288,7 +298,8 @@ public abstract class WMShellBaseModule { compatUIConfiguration.get(), compatUIShellCommandHandler.get(), accessibilityManager.get(), - compatUIStatusManager)); + compatUIStatusManager, + inDesktopModePredicate)); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt new file mode 100644 index 000000000000..cc47dbb78af2 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2024 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.dagger + +import android.os.Handler +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.shared.annotations.ShellBackgroundThread +import com.android.wm.shell.shared.annotations.ShellMainThread +import dagger.Module +import dagger.Provides +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainCoroutineDispatcher +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.android.asCoroutineDispatcher +import kotlinx.coroutines.asCoroutineDispatcher + +/** + * Providers for various WmShell-specific coroutines-related constructs. + * + * Providers of [MainCoroutineDispatcher] intentionally creates the dispatcher with a [Handler] + * backing it instead of a [ShellExecutor] because [ShellExecutor.asCoroutineDispatcher] will + * create a [CoroutineDispatcher] whose [CoroutineDispatcher.isDispatchNeeded] is effectively never + * dispatching. This is because even if dispatched, the backing [ShellExecutor.execute] always runs + * the [Runnable] immediately if called from the same thread, whereas + * [Handler.asCoroutineDispatcher] will create a [MainCoroutineDispatcher] that correctly + * dispatches (queues) when [CoroutineDispatcher.isDispatchNeeded] is true using [Handler.post]. + * For callers that do need a non-dispatching version, [MainCoroutineDispatcher.immediate] is + * available. + */ +@Module +class WMShellCoroutinesModule { + @Provides + @ShellMainThread + fun provideMainDispatcher( + @ShellMainThread mainHandler: Handler + ): MainCoroutineDispatcher = mainHandler.asCoroutineDispatcher() + + @Provides + @ShellBackgroundThread + fun provideBackgroundDispatcher( + @ShellBackgroundThread backgroundHandler: Handler + ): MainCoroutineDispatcher = backgroundHandler.asCoroutineDispatcher() + + @Provides + @WMSingleton + @ShellMainThread + fun provideApplicationScope( + @ShellMainThread applicationDispatcher: MainCoroutineDispatcher, + ): CoroutineScope = CoroutineScope(applicationDispatcher) + + @Provides + @WMSingleton + @ShellBackgroundThread + fun provideBackgroundCoroutineScope( + @ShellBackgroundThread backgroundDispatcher: MainCoroutineDispatcher, + ): CoroutineScope = CoroutineScope(backgroundDispatcher) + + @Provides + @WMSingleton + @ShellBackgroundThread + fun provideBackgroundCoroutineContext( + @ShellBackgroundThread backgroundDispatcher: MainCoroutineDispatcher + ): CoroutineContext = backgroundDispatcher + SupervisorJob() +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index ce054a833107..02ecfd983d73 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -59,6 +59,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.dagger.back.ShellBackAnimationModule; import com.android.wm.shell.dagger.pip.PipModule; import com.android.wm.shell.desktopmode.DefaultDragToDesktopTransitionHandler; +import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler; import com.android.wm.shell.desktopmode.DesktopModeDragAndDropTransitionHandler; import com.android.wm.shell.desktopmode.DesktopModeEventLogger; import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver; @@ -72,6 +73,8 @@ import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler; import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator; import com.android.wm.shell.desktopmode.SpringDragToDesktopTransitionHandler; import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler; +import com.android.wm.shell.desktopmode.education.AppHandleEducationController; +import com.android.wm.shell.desktopmode.education.AppHandleEducationFilter; import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.draganddrop.GlobalDragListener; @@ -116,6 +119,8 @@ import dagger.Lazy; import dagger.Module; import dagger.Provides; +import kotlinx.coroutines.CoroutineScope; + import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -235,7 +240,9 @@ public abstract class WMShellModule { RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, InteractionJankMonitor interactionJankMonitor, AppToWebGenericLinksParser genericLinksParser, - MultiInstanceHelper multiInstanceHelper) { + MultiInstanceHelper multiInstanceHelper, + Optional<DesktopTasksLimiter> desktopTasksLimiter, + Optional<DesktopActivityOrientationChangeHandler> desktopActivityOrientationHandler) { if (DesktopModeStatus.canEnterDesktopMode(context)) { return new DesktopModeWindowDecorViewModel( context, @@ -256,7 +263,9 @@ public abstract class WMShellModule { rootTaskDisplayAreaOrganizer, interactionJankMonitor, genericLinksParser, - multiInstanceHelper); + multiInstanceHelper, + desktopTasksLimiter, + desktopActivityOrientationHandler); } return new CaptionWindowDecorViewModel( context, @@ -674,6 +683,24 @@ public abstract class WMShellModule { @WMSingleton @Provides + static Optional<DesktopActivityOrientationChangeHandler> provideActivityOrientationHandler( + Context context, + ShellInit shellInit, + ShellTaskOrganizer shellTaskOrganizer, + TaskStackListenerImpl taskStackListener, + ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler, + @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository + ) { + if (DesktopModeStatus.canEnterDesktopMode(context)) { + return Optional.of(new DesktopActivityOrientationChangeHandler( + context, shellInit, shellTaskOrganizer, taskStackListener, + toggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository)); + } + return Optional.empty(); + } + + @WMSingleton + @Provides static Optional<DesktopTasksTransitionObserver> provideDesktopTasksTransitionObserver( Context context, Optional<DesktopModeTaskRepository> desktopModeTaskRepository, @@ -711,6 +738,25 @@ public abstract class WMShellModule { return new AppHandleEducationDatastoreRepository(context); } + @WMSingleton + @Provides + static AppHandleEducationFilter provideAppHandleEducationFilter( + Context context, + AppHandleEducationDatastoreRepository appHandleEducationDatastoreRepository) { + return new AppHandleEducationFilter(context, appHandleEducationDatastoreRepository); + } + + @WMSingleton + @Provides + static AppHandleEducationController provideAppHandleEducationController( + AppHandleEducationFilter appHandleEducationFilter, + ShellTaskOrganizer shellTaskOrganizer, + AppHandleEducationDatastoreRepository appHandleEducationDatastoreRepository, + @ShellMainThread CoroutineScope applicationScope) { + return new AppHandleEducationController(appHandleEducationFilter, + shellTaskOrganizer, appHandleEducationDatastoreRepository, applicationScope); + } + // // Drag and drop // @@ -752,7 +798,8 @@ public abstract class WMShellModule { @Provides static Object provideIndependentShellComponentsToCreate( DragAndDropController dragAndDropController, - Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional + Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional, + AppHandleEducationController appHandleEducationController ) { return new Object(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java index 51ce2c6707ac..3464fef07f33 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java @@ -42,6 +42,7 @@ import com.android.wm.shell.pip2.phone.PhonePipMenuController; import com.android.wm.shell.pip2.phone.PipController; import com.android.wm.shell.pip2.phone.PipMotionHelper; import com.android.wm.shell.pip2.phone.PipScheduler; +import com.android.wm.shell.pip2.phone.PipTaskListener; import com.android.wm.shell.pip2.phone.PipTouchHandler; import com.android.wm.shell.pip2.phone.PipTransition; import com.android.wm.shell.pip2.phone.PipTransitionState; @@ -73,12 +74,13 @@ public abstract class Pip2Module { PipBoundsAlgorithm pipBoundsAlgorithm, Optional<PipController> pipController, PipTouchHandler pipTouchHandler, + PipTaskListener pipTaskListener, @NonNull PipScheduler pipScheduler, @NonNull PipTransitionState pipStackListenerController, @NonNull PipUiStateChangeController pipUiStateChangeController) { return new PipTransition(context, shellInit, shellTaskOrganizer, transitions, - pipBoundsState, null, pipBoundsAlgorithm, pipScheduler, - pipStackListenerController, pipUiStateChangeController); + pipBoundsState, null, pipBoundsAlgorithm, pipTaskListener, + pipScheduler, pipStackListenerController, pipUiStateChangeController); } @WMSingleton @@ -123,9 +125,11 @@ public abstract class Pip2Module { @Provides static PipScheduler providePipScheduler(Context context, PipBoundsState pipBoundsState, + PhonePipMenuController pipMenuController, @ShellMainThread ShellExecutor mainExecutor, PipTransitionState pipTransitionState) { - return new PipScheduler(context, pipBoundsState, mainExecutor, pipTransitionState); + return new PipScheduler(context, pipBoundsState, pipMenuController, + mainExecutor, pipTransitionState); } @WMSingleton @@ -190,4 +194,17 @@ public abstract class Pip2Module { PipTransitionState pipTransitionState) { return new PipUiStateChangeController(pipTransitionState); } + + @WMSingleton + @Provides + static PipTaskListener providePipTaskListener(Context context, + ShellTaskOrganizer shellTaskOrganizer, + PipTransitionState pipTransitionState, + PipScheduler pipScheduler, + PipBoundsState pipBoundsState, + PipBoundsAlgorithm pipBoundsAlgorithm, + @ShellMainThread ShellExecutor mainExecutor) { + return new PipTaskListener(context, shellTaskOrganizer, pipTransitionState, + pipScheduler, pipBoundsState, pipBoundsAlgorithm, mainExecutor); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt new file mode 100644 index 000000000000..59e006879da8 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2024 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.desktopmode + +import android.app.ActivityManager.RunningTaskInfo +import android.content.Context +import android.content.pm.ActivityInfo +import android.content.pm.ActivityInfo.ScreenOrientation +import android.content.res.Configuration.ORIENTATION_LANDSCAPE +import android.content.res.Configuration.ORIENTATION_PORTRAIT +import android.graphics.Rect +import android.util.Size +import android.window.WindowContainerTransaction +import com.android.window.flags.Flags +import com.android.wm.shell.ShellTaskOrganizer +import com.android.wm.shell.common.TaskStackListenerCallback +import com.android.wm.shell.common.TaskStackListenerImpl +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus +import com.android.wm.shell.sysui.ShellInit + +/** Handles task resizing to respect orientation change of non-resizeable activities in desktop. */ +class DesktopActivityOrientationChangeHandler( + context: Context, + shellInit: ShellInit, + private val shellTaskOrganizer: ShellTaskOrganizer, + private val taskStackListener: TaskStackListenerImpl, + private val resizeHandler: ToggleResizeDesktopTaskTransitionHandler, + private val taskRepository: DesktopModeTaskRepository, +) { + + init { + if (DesktopModeStatus.canEnterDesktopMode(context)) { + shellInit.addInitCallback({ onInit() }, this) + } + } + + private fun onInit() { + taskStackListener.addListener(object : TaskStackListenerCallback { + override fun onActivityRequestedOrientationChanged( + taskId: Int, + @ScreenOrientation requestedOrientation: Int + ) { + // Handle requested screen orientation changes at runtime. + handleActivityOrientationChange(taskId, requestedOrientation) + } + }) + } + + /** + * Triggered with onTaskInfoChanged to handle: + * * New activity launching from same task with different orientation + * * Top activity closing in same task with different orientation to previous activity + */ + fun handleActivityOrientationChange(oldTask: RunningTaskInfo, newTask: RunningTaskInfo) { + val newTopActivityInfo = newTask.topActivityInfo ?: return + val oldTopActivityInfo = oldTask.topActivityInfo ?: return + // Check if screen orientation is different from old task info so there is no duplicated + // calls to handle runtime requested orientation changes. + if (oldTopActivityInfo.screenOrientation != newTopActivityInfo.screenOrientation) { + handleActivityOrientationChange(newTask.taskId, newTopActivityInfo.screenOrientation) + } + } + + private fun handleActivityOrientationChange( + taskId: Int, + @ScreenOrientation requestedOrientation: Int + ) { + if (!Flags.respectOrientationChangeForUnresizeable()) return + val task = shellTaskOrganizer.getRunningTaskInfo(taskId) ?: return + if (!isDesktopModeShowing(task.displayId) || !task.isFreeform || task.isResizeable) return + + val taskBounds = task.configuration.windowConfiguration.bounds + val taskHeight = taskBounds.height() + val taskWidth = taskBounds.width() + if (taskWidth == taskHeight) return + val orientation = + if (taskWidth > taskHeight) ORIENTATION_LANDSCAPE else ORIENTATION_PORTRAIT + + // Non-resizeable activity requested opposite orientation. + if (orientation == ORIENTATION_PORTRAIT + && ActivityInfo.isFixedOrientationLandscape(requestedOrientation) + || orientation == ORIENTATION_LANDSCAPE + && ActivityInfo.isFixedOrientationPortrait(requestedOrientation)) { + + val finalSize = Size(taskHeight, taskWidth) + // Use the center x as the resizing anchor point. + val left = taskBounds.centerX() - finalSize.width / 2 + val right = left + finalSize.width + val finalBounds = Rect(left, taskBounds.top, right, taskBounds.top + finalSize.height) + + val wct = WindowContainerTransaction().setBounds(task.token, finalBounds) + resizeHandler.startTransition(wct) + } + } + + private fun isDesktopModeShowing(displayId: Int): Boolean = + taskRepository.getVisibleTaskCount(displayId) > 0 +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java index 31c8f1e45007..cca750014fc1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java @@ -18,8 +18,8 @@ package com.android.wm.shell.desktopmode; import android.graphics.Region; -import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.shared.annotations.ExternalThread; +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource; import java.util.concurrent.Executor; import java.util.function.Consumer; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt index b6f2a25ff1c0..02cbe01d0a03 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt @@ -132,7 +132,8 @@ class DesktopModeEventLogger { sessionId, taskUpdate.minimizeReason?.reason ?: UNSET_MINIMIZE_REASON, taskUpdate.unminimizeReason?.reason ?: UNSET_UNMINIMIZE_REASON, - + /* visible_task_count */ + taskUpdate.visibleTaskCount ) } @@ -159,6 +160,7 @@ class DesktopModeEventLogger { val taskY: Int, val minimizeReason: MinimizeReason? = null, val unminimizeReason: UnminimizeReason? = null, + val visibleTaskCount: Int, ) // Default value used when the task was not minimized. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt index a6ed3b8cb50c..063747494a82 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt @@ -22,6 +22,7 @@ import android.app.TaskInfo import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.content.Context import android.os.IBinder +import android.os.SystemProperties import android.os.Trace import android.util.SparseArray import android.view.SurfaceControl @@ -52,8 +53,6 @@ import com.android.wm.shell.shared.TransitionUtil import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions -const val VISIBLE_TASKS_COUNTER_NAME = "DESKTOP_MODE_VISIBLE_TASKS" - /** * A [Transitions.TransitionObserver] that observes transitions and the proposed changes to log * appropriate desktop mode session log events. This observes transitions related to desktop mode @@ -86,6 +85,10 @@ class DesktopModeLoggerTransitionObserver( // Caching whether the previous transition was exit to overview. private var wasPreviousTransitionExitToOverview: Boolean = false + // Caching whether the previous transition was exit due to screen off. This helps check if a + // following enter reason could be Screen On + private var wasPreviousTransitionExitByScreenOff: Boolean = false + // The instanceId for the current logging session private var loggerInstanceId: InstanceId? = null @@ -291,7 +294,8 @@ class DesktopModeLoggerTransitionObserver( postTransitionVisibleFreeformTasks: SparseArray<TaskInfo> ) { postTransitionVisibleFreeformTasks.forEach { taskId, taskInfo -> - val currentTaskUpdate = buildTaskUpdateForTask(taskInfo) + val currentTaskUpdate = buildTaskUpdateForTask(taskInfo, + postTransitionVisibleFreeformTasks.size()) val previousTaskInfo = preTransitionVisibleFreeformTasks[taskId] when { // new tasks added @@ -302,44 +306,54 @@ class DesktopModeLoggerTransitionObserver( VISIBLE_TASKS_COUNTER_NAME, postTransitionVisibleFreeformTasks.size().toLong() ) + SystemProperties.set(VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY, + postTransitionVisibleFreeformTasks.size().toString()) } // old tasks that were resized or repositioned // TODO(b/347935387): Log changes only once they are stable. - buildTaskUpdateForTask(previousTaskInfo) != currentTaskUpdate -> - desktopModeEventLogger.logTaskInfoChanged(sessionId, currentTaskUpdate) + buildTaskUpdateForTask(previousTaskInfo, postTransitionVisibleFreeformTasks.size()) + != currentTaskUpdate -> + desktopModeEventLogger.logTaskInfoChanged(sessionId, currentTaskUpdate) } } // find old tasks that were removed preTransitionVisibleFreeformTasks.forEach { taskId, taskInfo -> if (!postTransitionVisibleFreeformTasks.containsKey(taskId)) { - desktopModeEventLogger.logTaskRemoved(sessionId, buildTaskUpdateForTask(taskInfo)) + desktopModeEventLogger.logTaskRemoved(sessionId, + buildTaskUpdateForTask(taskInfo, postTransitionVisibleFreeformTasks.size())) Trace.setCounter( Trace.TRACE_TAG_WINDOW_MANAGER, VISIBLE_TASKS_COUNTER_NAME, postTransitionVisibleFreeformTasks.size().toLong() ) + SystemProperties.set(VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY, + postTransitionVisibleFreeformTasks.size().toString()) } } } - private fun buildTaskUpdateForTask(taskInfo: TaskInfo): TaskUpdate { + private fun buildTaskUpdateForTask(taskInfo: TaskInfo, visibleTasks: Int): TaskUpdate { val screenBounds = taskInfo.configuration.windowConfiguration.bounds val positionInParent = taskInfo.positionInParent return TaskUpdate( instanceId = taskInfo.taskId, - uid = taskInfo.userId, + uid = taskInfo.effectiveUid, taskHeight = screenBounds.height(), taskWidth = screenBounds.width(), taskX = positionInParent.x, taskY = positionInParent.y, + visibleTaskCount = visibleTasks, ) } /** Get [EnterReason] for this session enter */ - private fun getEnterReason(transitionInfo: TransitionInfo): EnterReason = - when { - transitionInfo.type == WindowManager.TRANSIT_WAKE -> EnterReason.SCREEN_ON + private fun getEnterReason(transitionInfo: TransitionInfo): EnterReason { + val enterReason = when { + transitionInfo.type == WindowManager.TRANSIT_WAKE + // If there is a screen lock, desktop window entry is after dismissing keyguard + || (transitionInfo.type == WindowManager.TRANSIT_TO_BACK + && wasPreviousTransitionExitByScreenOff) -> EnterReason.SCREEN_ON transitionInfo.type == Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP -> EnterReason.APP_HANDLE_DRAG transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON -> @@ -367,11 +381,17 @@ class DesktopModeLoggerTransitionObserver( EnterReason.UNKNOWN_ENTER } } + wasPreviousTransitionExitByScreenOff = false + return enterReason + } /** Get [ExitReason] for this session exit */ private fun getExitReason(transitionInfo: TransitionInfo): ExitReason = when { - transitionInfo.type == WindowManager.TRANSIT_SLEEP -> ExitReason.SCREEN_OFF + transitionInfo.type == WindowManager.TRANSIT_SLEEP -> { + wasPreviousTransitionExitByScreenOff = true + ExitReason.SCREEN_OFF + } transitionInfo.type == WindowManager.TRANSIT_CLOSE -> ExitReason.TASK_FINISHED transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG -> ExitReason.DRAG_TO_EXIT transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON -> @@ -414,4 +434,12 @@ class DesktopModeLoggerTransitionObserver( return this.type == WindowManager.TRANSIT_TO_FRONT && this.flags == WindowManager.TRANSIT_FLAG_IS_RECENTS } + + companion object { + @VisibleForTesting + const val VISIBLE_TASKS_COUNTER_NAME = "desktop_mode_visible_tasks" + @VisibleForTesting + const val VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY = + "debug.tracing." + VISIBLE_TASKS_COUNTER_NAME + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt index eca3c1fdc65a..dba8c9367654 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt @@ -16,7 +16,7 @@ package com.android.wm.shell.desktopmode -import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.UNKNOWN +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.UNKNOWN import com.android.wm.shell.sysui.ShellCommandHandler import java.io.PrintWriter diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt index b24bd10eaa0d..d6fccd116061 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt @@ -17,7 +17,7 @@ package com.android.wm.shell.desktopmode import android.view.WindowManager.TransitionType -import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_TYPES /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt index b68b436f2c1b..c8ffe28da79c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt @@ -171,6 +171,18 @@ fun calculateAspectRatio(taskInfo: RunningTaskInfo): Float { minOf(appBounds.height(), appBounds.width()).toFloat() } +/** Returns true if task's width or height is maximized else returns false. */ +fun isTaskWidthOrHeightEqual(taskBounds: Rect, stableBounds: Rect): Boolean { + return taskBounds.width() == stableBounds.width() || + taskBounds.height() == stableBounds.height() +} + +/** Returns true if task bound is equal to stable bounds else returns false. */ +fun isTaskBoundsEqual(taskBounds: Rect, stableBounds: Rect): Boolean { + return taskBounds.width() == stableBounds.width() && + taskBounds.height() == stableBounds.height() +} + /** * Calculates the desired initial bounds for applications in desktop windowing. This is done as a * scale of the screen bounds. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 33794d242c03..1d16980c617d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -53,6 +53,7 @@ import androidx.annotation.BinderThread import com.android.internal.annotations.VisibleForTesting import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE +import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE import com.android.internal.jank.InteractionJankMonitor import com.android.internal.policy.ScreenDecorationsUtils import com.android.internal.protolog.ProtoLog @@ -68,7 +69,6 @@ import com.android.wm.shell.common.RemoteCallable import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SingleInstanceRemoteListener import com.android.wm.shell.common.SyncTransactionQueue -import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.DragStartState @@ -88,6 +88,7 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.WALLPAPER_ACTIVI import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.DESKTOP_DENSITY_OVERRIDE import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.useDesktopOverrideDensity +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT import com.android.wm.shell.splitscreen.SplitScreenController @@ -168,6 +169,9 @@ class DesktopTasksController( } } + @VisibleForTesting + var taskbarDesktopTaskListener: TaskbarDesktopTaskListener? = null + /** Task id of the task currently being dragged from fullscreen/split. */ val draggingTaskId get() = dragToDesktopTransitionHandler.draggingTaskId @@ -322,7 +326,7 @@ class DesktopTasksController( logW("moveBackgroundTaskToDesktop taskId=%d not found", taskId) return false } - logV("moveBackgroundTaskToDesktop with taskId=%d, displayId=%d", taskId) + logV("moveBackgroundTaskToDesktop with taskId=%d", taskId) // TODO(342378842): Instead of using default display, support multiple displays val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask( DEFAULT_DISPLAY, wct, taskId) @@ -431,6 +435,20 @@ class DesktopTasksController( taskRepository.addClosingTask(displayId, taskId) } + /** + * Perform clean up of the desktop wallpaper activity if the minimized window task is the last + * active task. + * + * @param wct transaction to modify if the last active task is minimized + * @param taskId task id of the window that's being minimized + */ + fun onDesktopWindowMinimize(wct: WindowContainerTransaction, taskId: Int) { + if (taskRepository.isOnlyVisibleNonClosingTask(taskId)) { + removeWallpaperActivity(wct) + } + // Do not call taskRepository.minimizeTask because it will be called by DekstopTasksLimiter. + } + /** Move a task with given `taskId` to fullscreen */ fun moveToFullscreen(taskId: Int, transitionSource: DesktopModeTransitionSource) { shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> @@ -595,13 +613,10 @@ class DesktopTasksController( val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds val destinationBounds = Rect() - val isMaximized = if (taskInfo.isResizeable) { - currentTaskBounds == stableBounds - } else { - currentTaskBounds.width() == stableBounds.width() - || currentTaskBounds.height() == stableBounds.height() - } - + val isMaximized = isTaskMaximized(taskInfo, stableBounds) + // If the task is currently maximized, we will toggle it not to be and vice versa. This is + // helpful to eliminate the current task from logic to calculate taskbar corner rounding. + val willMaximize = !isMaximized if (isMaximized) { // The desktop task is at the maximized width and/or height of the stable bounds. // If the task's pre-maximize stable bounds were saved, toggle the task to those bounds. @@ -636,6 +651,18 @@ class DesktopTasksController( } } + + + val shouldRestoreToSnap = + isMaximized && isTaskSnappedToHalfScreen(taskInfo, destinationBounds) + + logD("willMaximize = %s", willMaximize) + logD("shouldRestoreToSnap = %s", shouldRestoreToSnap) + + val doesAnyTaskRequireTaskbarRounding = willMaximize || shouldRestoreToSnap || + doesAnyTaskRequireTaskbarRounding(taskInfo.displayId, taskInfo.taskId) + + taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding) val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds) if (Transitions.ENABLE_SHELL_TRANSITIONS) { toggleResizeDesktopTaskTransitionHandler.startTransition(wct) @@ -644,23 +671,98 @@ class DesktopTasksController( } } + private fun isTaskMaximized( + taskInfo: RunningTaskInfo, + stableBounds: Rect + ): Boolean { + val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds + + return if (taskInfo.isResizeable) { + isTaskBoundsEqual(currentTaskBounds, stableBounds) + } else { + isTaskWidthOrHeightEqual(currentTaskBounds, stableBounds) + } + } + + private fun isMaximizedToStableBoundsEdges( + taskInfo: RunningTaskInfo, + stableBounds: Rect + ): Boolean { + val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds + return isTaskBoundsEqual(currentTaskBounds, stableBounds) + } + + /** Returns if current task bound is snapped to half screen */ + private fun isTaskSnappedToHalfScreen( + taskInfo: RunningTaskInfo, + taskBounds: Rect = taskInfo.configuration.windowConfiguration.bounds + ): Boolean = + getSnapBounds(taskInfo, SnapPosition.LEFT) == taskBounds || + getSnapBounds(taskInfo, SnapPosition.RIGHT) == taskBounds + + @VisibleForTesting + fun doesAnyTaskRequireTaskbarRounding( + displayId: Int, + excludeTaskId: Int? = null, + ): Boolean { + val doesAnyTaskRequireTaskbarRounding = + taskRepository.getActiveNonMinimizedOrderedTasks(displayId) + // exclude current task since maximize/restore transition has not taken place yet. + .filterNot { taskId -> taskId == excludeTaskId } + .any { taskId -> + val taskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId)!! + val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) + val stableBounds = Rect().apply { displayLayout?.getStableBounds(this) } + logD("taskInfo = %s", taskInfo) + logD( + "isTaskSnappedToHalfScreen(taskInfo) = %s", + isTaskSnappedToHalfScreen(taskInfo) + ) + logD( + "isMaximizedToStableBoundsEdges(taskInfo, stableBounds) = %s", + isMaximizedToStableBoundsEdges(taskInfo, stableBounds) + ) + isTaskSnappedToHalfScreen(taskInfo) + || isMaximizedToStableBoundsEdges(taskInfo, stableBounds) + } + + logD("doesAnyTaskRequireTaskbarRounding = %s", doesAnyTaskRequireTaskbarRounding) + return doesAnyTaskRequireTaskbarRounding + } + /** * Quick-resize to the right or left half of the stable bounds. * * @param taskInfo current task that is being snap-resized via dragging or maximize menu button + * @param taskSurface the leash of the task being dragged * @param currentDragBounds current position of the task leash being dragged (or current task * bounds if being snapped resize via maximize menu button) * @param position the portion of the screen (RIGHT or LEFT) we want to snap the task to. */ fun snapToHalfScreen( taskInfo: RunningTaskInfo, + taskSurface: SurfaceControl, currentDragBounds: Rect, position: SnapPosition ) { val destinationBounds = getSnapBounds(taskInfo, position) + if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) { + // Handle the case where we attempt to snap resize when already snap resized: the task + // position won't need to change but we want to animate the surface going back to the + // snapped position from the "dragged-to-the-edge" position. + if (destinationBounds != currentDragBounds) { + returnToDragStartAnimator.start( + taskInfo.taskId, + taskSurface, + startBounds = currentDragBounds, + endBounds = destinationBounds, + isResizable = taskInfo.isResizeable + ) + } + return + } - if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return - + taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(true) val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds) if (Transitions.ENABLE_SHELL_TRANSITIONS) { toggleResizeDesktopTaskTransitionHandler.startTransition(wct, currentDragBounds) @@ -679,15 +781,23 @@ class DesktopTasksController( ) { releaseVisualIndicator() if (!taskInfo.isResizeable && DesktopModeFlags.DISABLE_SNAP_RESIZE.isEnabled(context)) { + interactionJankMonitor.begin( + taskSurface, context, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_non_resizable" + ) + // reposition non-resizable app back to its original position before being dragged returnToDragStartAnimator.start( taskInfo.taskId, taskSurface, startBounds = currentDragBounds, - endBounds = dragStartBounds + endBounds = dragStartBounds, + isResizable = taskInfo.isResizeable, ) } else { - snapToHalfScreen(taskInfo, currentDragBounds, position) + interactionJankMonitor.begin( + taskSurface, context, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_resizable" + ) + snapToHalfScreen(taskInfo, taskSurface, currentDragBounds, position) } } @@ -784,6 +894,10 @@ class DesktopTasksController( .mapNotNull { taskId -> shellTaskOrganizer.getRunningTaskInfo(taskId) } .reversed() // Start from the back so the front task is brought forward last .forEach { task -> wct.reorder(task.token, /* onTop= */ true) } + + taskbarDesktopTaskListener?. + onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(displayId)) + return taskToMinimize } @@ -799,6 +913,7 @@ class DesktopTasksController( val intent = Intent(context, DesktopWallpaperActivity::class.java) val options = ActivityOptions.makeBasic().apply { + launchWindowingMode = WINDOWING_MODE_FULLSCREEN pendingIntentBackgroundActivityStartMode = ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS } @@ -1090,12 +1205,11 @@ class DesktopTasksController( addMoveToDesktopChanges(wct, task) // In some launches home task is moved behind new task being launched. Make sure // that's not the case for launches in desktop. - moveHomeTask(wct, toTop = false) - // Move existing minimized tasks behind Home - taskRepository.getFreeformTasksInZOrder(task.displayId) - .filter { taskId -> taskRepository.isMinimizedTask(taskId) } - .mapNotNull { taskId -> shellTaskOrganizer.getRunningTaskInfo(taskId) } - .forEach { taskInfo -> wct.reorder(taskInfo.token, /* onTop= */ false) } + if (task.baseIntent.flags.and(Intent.FLAG_ACTIVITY_TASK_ON_HOME) != 0) { + bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId) + wct.reorder(task.token, true) + } + // Desktop Mode is already showing and we're launching a new Task - we might need to // minimize another Task. val taskToMinimize = addAndGetMinimizeChangesIfNeeded(task.displayId, wct, task) @@ -1135,6 +1249,12 @@ class DesktopTasksController( ) { wct.removeTask(task.token) } + taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate( + doesAnyTaskRequireTaskbarRounding( + task.displayId, + task.id + ) + ) return if (wct.isEmpty) null else wct } @@ -1429,6 +1549,8 @@ class DesktopTasksController( } // A freeform drag-move ended, remove the indicator immediately. releaseVisualIndicator() + taskbarDesktopTaskListener + ?.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(taskInfo.displayId)) } /** @@ -1665,17 +1787,39 @@ class DesktopTasksController( } } + private val mTaskbarDesktopTaskListener: TaskbarDesktopTaskListener = + object : TaskbarDesktopTaskListener { + override fun onTaskbarCornerRoundingUpdate( + hasTasksRequiringTaskbarRounding: Boolean) { + ProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "IDesktopModeImpl: onTaskbarCornerRoundingUpdate " + + "doesAnyTaskRequireTaskbarRounding=%s", + hasTasksRequiringTaskbarRounding + ) + + remoteListener.call { l -> + l.onTaskbarCornerRoundingUpdate(hasTasksRequiringTaskbarRounding) + } + } + } + init { remoteListener = SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener>( controller, { c -> - c.taskRepository.addVisibleTasksListener( - listener, - c.mainExecutor - ) + run { + c.taskRepository.addVisibleTasksListener(listener, c.mainExecutor) + c.taskbarDesktopTaskListener = mTaskbarDesktopTaskListener + } }, - { c -> c.taskRepository.removeVisibleTasksListener(listener) } + { c -> + run { + c.taskRepository.removeVisibleTasksListener(listener) + c.taskbarDesktopTaskListener = null + } + } ) } @@ -1758,6 +1902,16 @@ class DesktopTasksController( private const val TAG = "DesktopTasksController" } + /** Defines interface for classes that can listen to changes for task resize. */ + // TODO(b/343931111): Migrate to using TransitionObservers when ready + interface TaskbarDesktopTaskListener { + /** + * [hasTasksRequiringTaskbarRounding] is true when a task is either maximized or snapped + * left/right and rounded corners are enabled. + */ + fun onTaskbarCornerRoundingUpdate(hasTasksRequiringTaskbarRounding: Boolean) + } + /** The positions on a screen that a task can snap to. */ enum class SnapPosition { RIGHT, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt index 1a103d345ca7..d72ec90957fc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt @@ -22,13 +22,15 @@ import android.graphics.Rect import android.os.Bundle import android.os.IBinder import android.os.SystemClock +import android.os.SystemProperties import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CLOSE import android.window.TransitionInfo import android.window.TransitionInfo.Change import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction -import androidx.dynamicanimation.animation.SpringForce +import com.android.internal.annotations.VisibleForTesting +import com.android.internal.dynamicanimation.animation.SpringForce import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE import com.android.internal.jank.InteractionJankMonitor @@ -893,13 +895,10 @@ constructor( ) { private val positionSpringConfig = - PhysicsAnimator.SpringConfig( - SpringForce.STIFFNESS_LOW, - SpringForce.DAMPING_RATIO_LOW_BOUNCY - ) + PhysicsAnimator.SpringConfig(POSITION_SPRING_STIFFNESS, POSITION_SPRING_DAMPING_RATIO) private val sizeSpringConfig = - PhysicsAnimator.SpringConfig(SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_NO_BOUNCY) + PhysicsAnimator.SpringConfig(SIZE_SPRING_STIFFNESS, SIZE_SPRING_DAMPING_RATIO) /** * @return layers in order: @@ -929,7 +928,7 @@ constructor( finishTransaction.hide(homeLeash) // Setup freeform tasks before animation state.freeformTaskChanges.forEach { change -> - val startScale = DRAG_TO_DESKTOP_FREEFORM_TASK_INITIAL_SCALE + val startScale = FREEFORM_TASKS_INITIAL_SCALE val startX = change.endAbsBounds.left + change.endAbsBounds.width() * (1 - startScale) / 2 val startY = @@ -994,9 +993,22 @@ constructor( (animBounds.width() - startBounds.width()).toFloat() / (endBounds.width() - startBounds.width()) val animScale = startScale + animFraction * (1 - startScale) - // Freeform animation starts 50% in the animation - val freeformAnimFraction = max(animFraction - 0.5f, 0f) * 2f - val freeformStartScale = DRAG_TO_DESKTOP_FREEFORM_TASK_INITIAL_SCALE + // Freeform animation starts with freeform animation offset relative to the commit + // animation and plays until the commit animation ends. For instance: + // - if the freeform animation offset is `0.0` the freeform tasks animate alongside + // - if the freeform animation offset is `0.6` the freeform tasks will + // start animating at 60% fraction of the commit animation and will complete when + // the commit animation fraction is 100%. + // - if the freeform animation offset is `1.0` then freeform tasks will appear + // without animation after commit animation finishes. + val freeformAnimFraction = + if (FREEFORM_TASKS_ANIM_OFFSET != 1f) { + max(animFraction - FREEFORM_TASKS_ANIM_OFFSET, 0f) / + (1f - FREEFORM_TASKS_ANIM_OFFSET) + } else { + 0f + } + val freeformStartScale = FREEFORM_TASKS_INITIAL_SCALE val freeformAnimScale = freeformStartScale + freeformAnimFraction * (1 - freeformStartScale) tx.apply { @@ -1032,10 +1044,53 @@ constructor( } companion object { + /** The freeform tasks initial scale when committing the drag-to-desktop gesture. */ + private val FREEFORM_TASKS_INITIAL_SCALE = + propertyValue("freeform_tasks_initial_scale", scale = 100f, default = 0.9f) + + /** The freeform tasks animation offset relative to the whole animation duration. */ + private val FREEFORM_TASKS_ANIM_OFFSET = + propertyValue("freeform_tasks_anim_offset", scale = 100f, default = 0.5f) + + /** The spring force stiffness used to place the window into the final position. */ + private val POSITION_SPRING_STIFFNESS = + propertyValue("position_stiffness", default = SpringForce.STIFFNESS_LOW) + + /** The spring force damping ratio used to place the window into the final position. */ + private val POSITION_SPRING_DAMPING_RATIO = + propertyValue( + "position_damping_ratio", + scale = 100f, + default = SpringForce.DAMPING_RATIO_LOW_BOUNCY + ) + + /** The spring force stiffness used to resize the window into the final bounds. */ + private val SIZE_SPRING_STIFFNESS = + propertyValue("size_stiffness", default = SpringForce.STIFFNESS_LOW) + + /** The spring force damping ratio used to resize the window into the final bounds. */ + private val SIZE_SPRING_DAMPING_RATIO = + propertyValue( + "size_damping_ratio", + scale = 100f, + default = SpringForce.DAMPING_RATIO_NO_BOUNCY + ) + + /** Drag to desktop transition system properties group. */ + @VisibleForTesting + const val SYSTEM_PROPERTIES_GROUP = "persist.wm.debug.desktop_transitions.drag_to_desktop" + /** - * The initial scale of the freeform tasks in the animation to commit the drag-to-desktop - * gesture. + * Drag to desktop transition system property value with [name]. + * + * @param scale an optional scale to apply to the value read from the system property. + * @param default a default value to return if the system property isn't set. */ - private const val DRAG_TO_DESKTOP_FREEFORM_TASK_INITIAL_SCALE = 0.9f + @VisibleForTesting + fun propertyValue(name: String, scale: Float = 1f, default: Float = 0f): Float = + SystemProperties.getInt( + /* key= */ "$SYSTEM_PROPERTIES_GROUP.$name", + /* def= */ (default * scale).toInt() + ) / scale } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java index 04506c1e66f2..80e106f3990b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java @@ -41,7 +41,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.jank.InteractionJankMonitor; -import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource; +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java index 171378f9a164..e87be520cc91 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java @@ -44,7 +44,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.Cuj; import com.android.internal.jank.InteractionJankMonitor; -import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource; +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.transition.Transitions; import java.util.ArrayList; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl index a7ec2037706d..b036e40e6e16 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl @@ -18,8 +18,8 @@ package com.android.wm.shell.desktopmode; import android.app.ActivityManager.RunningTaskInfo; import android.window.RemoteTransition; -import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.desktopmode.IDesktopTaskListener; +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource; /** * Interface that is exposed to remote callers to manipulate desktop mode features. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl index 8ebdfdcf4731..c2acb87222d1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl @@ -27,4 +27,10 @@ interface IDesktopTaskListener { /** @deprecated this is no longer supported. */ oneway void onStashedChanged(int displayId, boolean stashed); + + /** + * Shows taskbar corner radius when running desktop tasks are updated if + * [hasTasksRequiringTaskbarRounding] is true. + */ + oneway void onTaskbarCornerRoundingUpdate(boolean hasTasksRequiringTaskbarRounding); }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt index 24a7d77c983f..f4df42cde10f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt @@ -24,6 +24,7 @@ import android.graphics.Rect import android.view.SurfaceControl import android.widget.Toast import androidx.core.animation.addListener +import com.android.internal.jank.Cuj import com.android.internal.jank.InteractionJankMonitor import com.android.wm.shell.R import com.android.wm.shell.windowdecor.OnTaskRepositionAnimationListener @@ -47,7 +48,13 @@ class ReturnToDragStartAnimator( } /** Builds new animator and starts animation of task leash reposition. */ - fun start(taskId: Int, taskSurface: SurfaceControl, startBounds: Rect, endBounds: Rect) { + fun start( + taskId: Int, + taskSurface: SurfaceControl, + startBounds: Rect, + endBounds: Rect, + isResizable: Boolean + ) { val tx = transactionSupplier.get() boundsAnimator?.cancel() @@ -80,12 +87,14 @@ class ReturnToDragStartAnimator( .apply() taskRepositionAnimationListener.onAnimationEnd(taskId) boundsAnimator = null - Toast.makeText( - context, - R.string.desktop_mode_non_resizable_snap_text, - Toast.LENGTH_SHORT - ).show() - // TODO(b/339582583) - add Jank CUJ using interactionJankMonitor + if (!isResizable) { + Toast.makeText( + context, + R.string.desktop_mode_non_resizable_snap_text, + Toast.LENGTH_SHORT + ).show() + } + interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE) } ) addUpdateListener { anim -> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt index bf185a463b1e..96719fa2301a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt @@ -117,8 +117,8 @@ class ToggleResizeDesktopTaskTransitionHandler( finishCallback.onTransitionFinished(null) initialBounds = null boundsAnimator = null - interactionJankMonitor.end( - Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW) + interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW) + interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE) } ) addUpdateListener { anim -> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt new file mode 100644 index 000000000000..6013e97977d8 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2024 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.desktopmode.education + +import android.app.ActivityManager.RunningTaskInfo +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.os.SystemProperties +import com.android.window.flags.Flags +import com.android.wm.shell.ShellTaskOrganizer +import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository +import com.android.wm.shell.shared.annotations.ShellMainThread +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch + +/** + * Controls app handle education end to end. + * + * Listen to the user trigger for app handle education, calls an api to check if the education + * should be shown and calls an api to show education. + */ +@OptIn(kotlinx.coroutines.FlowPreview::class) +@kotlinx.coroutines.ExperimentalCoroutinesApi +class AppHandleEducationController( + private val appHandleEducationFilter: AppHandleEducationFilter, + shellTaskOrganizer: ShellTaskOrganizer, + private val appHandleEducationDatastoreRepository: AppHandleEducationDatastoreRepository, + @ShellMainThread private val applicationCoroutineScope: CoroutineScope +) { + init { + runIfEducationFeatureEnabled { + // TODO: b/361038716 - Use app handle state flow instead of focus task change flow + val focusTaskChangeFlow = focusTaskChangeFlow(shellTaskOrganizer) + applicationCoroutineScope.launch { + // Central block handling the app's educational flow end-to-end. + // This flow listens to the changes to the result of + // [WindowingEducationProto#hasEducationViewedTimestampMillis()] in datastore proto object + isEducationViewedFlow() + .flatMapLatest { isEducationViewed -> + if (isEducationViewed) { + // If the education is viewed then return emptyFlow() that completes immediately. + // This will help us to not listen to focus task changes after the education has + // been viewed already. + emptyFlow() + } else { + // This flow listens for focus task changes, which trigger the app handle education. + focusTaskChangeFlow + .filter { runningTaskInfo -> + runningTaskInfo.topActivityInfo?.packageName?.let { + appHandleEducationFilter.shouldShowAppHandleEducation(it) + } ?: false && runningTaskInfo.windowingMode != WINDOWING_MODE_FREEFORM + } + .distinctUntilChanged() + } + } + .debounce( + APP_HANDLE_EDUCATION_DELAY) // Wait for few seconds, if the focus task changes. + // During the delay then current emission will be cancelled. + .flowOn(Dispatchers.IO) + .collectLatest { + // Fire and forget show education suspend function, manage entire lifecycle of + // tooltip in UI class. + } + } + } + } + + private inline fun runIfEducationFeatureEnabled(block: () -> Unit) { + if (Flags.enableDesktopWindowingAppHandleEducation()) block() + } + + private fun isEducationViewedFlow(): Flow<Boolean> = + appHandleEducationDatastoreRepository.dataStoreFlow + .map { preferences -> preferences.hasEducationViewedTimestampMillis() } + .distinctUntilChanged() + + private fun focusTaskChangeFlow(shellTaskOrganizer: ShellTaskOrganizer): Flow<RunningTaskInfo> = + callbackFlow { + val focusTaskChange = ShellTaskOrganizer.FocusListener { taskInfo -> trySend(taskInfo) } + shellTaskOrganizer.addFocusListener(focusTaskChange) + awaitClose { shellTaskOrganizer.removeFocusListener(focusTaskChange) } + } + + private companion object { + val APP_HANDLE_EDUCATION_DELAY: Long + get() = SystemProperties.getLong("persist.windowing_app_handle_education_delay", 3000L) + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt new file mode 100644 index 000000000000..51bdb40e12e6 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2024 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.desktopmode.education + +import android.annotation.IntegerRes +import android.app.usage.UsageStatsManager +import android.content.Context +import android.os.SystemClock +import android.provider.Settings.Secure +import com.android.wm.shell.R +import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository +import com.android.wm.shell.desktopmode.education.data.WindowingEducationProto +import java.time.Duration + +/** Filters incoming app handle education triggers based on set conditions. */ +class AppHandleEducationFilter( + private val context: Context, + private val appHandleEducationDatastoreRepository: AppHandleEducationDatastoreRepository +) { + private val usageStatsManager = + context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager + + /** Returns true if conditions to show app handle education are met, returns false otherwise. */ + suspend fun shouldShowAppHandleEducation(focusAppPackageName: String): Boolean { + val windowingEducationProto = appHandleEducationDatastoreRepository.windowingEducationProto() + return isFocusAppInAllowlist(focusAppPackageName) && + !isOtherEducationShowing() && + hasSufficientTimeSinceSetup() && + !isEducationViewedBefore(windowingEducationProto) && + !isFeatureUsedBefore(windowingEducationProto) && + hasMinAppUsage(windowingEducationProto, focusAppPackageName) + } + + private fun isFocusAppInAllowlist(focusAppPackageName: String): Boolean = + focusAppPackageName in + context.resources.getStringArray( + R.array.desktop_windowing_app_handle_education_allowlist_apps) + + // TODO: b/350953004 - Add checks based on App compat + // TODO: b/350951797 - Add checks based on PKT tips education + private fun isOtherEducationShowing(): Boolean = isTaskbarEducationShowing() + + private fun isTaskbarEducationShowing(): Boolean = + Secure.getInt(context.contentResolver, Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING, 0) == 1 + + private fun hasSufficientTimeSinceSetup(): Boolean = + Duration.ofMillis(SystemClock.elapsedRealtime()) > + convertIntegerResourceToDuration( + R.integer.desktop_windowing_education_required_time_since_setup_seconds) + + private fun isEducationViewedBefore(windowingEducationProto: WindowingEducationProto): Boolean = + windowingEducationProto.hasEducationViewedTimestampMillis() + + private fun isFeatureUsedBefore(windowingEducationProto: WindowingEducationProto): Boolean = + windowingEducationProto.hasFeatureUsedTimestampMillis() + + private suspend fun hasMinAppUsage( + windowingEducationProto: WindowingEducationProto, + focusAppPackageName: String + ): Boolean = + (launchCountByPackageName(windowingEducationProto)[focusAppPackageName] ?: 0) >= + context.resources.getInteger(R.integer.desktop_windowing_education_min_app_launch_count) + + private suspend fun launchCountByPackageName( + windowingEducationProto: WindowingEducationProto + ): Map<String, Int> = + if (isAppUsageCacheStale(windowingEducationProto)) { + // Query and return user stats, update cache in datastore + getAndCacheAppUsageStats() + } else { + // Return cached usage stats + windowingEducationProto.appHandleEducation.appUsageStatsMap + } + + private fun isAppUsageCacheStale(windowingEducationProto: WindowingEducationProto): Boolean { + val currentTime = currentTimeInDuration() + val lastUpdateTime = + Duration.ofMillis( + windowingEducationProto.appHandleEducation.appUsageStatsLastUpdateTimestampMillis) + val appUsageStatsCachingInterval = + convertIntegerResourceToDuration( + R.integer.desktop_windowing_education_app_usage_cache_interval_seconds) + return (currentTime - lastUpdateTime) > appUsageStatsCachingInterval + } + + private suspend fun getAndCacheAppUsageStats(): Map<String, Int> { + val currentTime = currentTimeInDuration() + val appUsageStats = queryAppUsageStats() + appHandleEducationDatastoreRepository.updateAppUsageStats(appUsageStats, currentTime) + return appUsageStats + } + + private fun queryAppUsageStats(): Map<String, Int> { + val endTime = currentTimeInDuration() + val appLaunchInterval = + convertIntegerResourceToDuration( + R.integer.desktop_windowing_education_app_launch_interval_seconds) + val startTime = endTime - appLaunchInterval + + return usageStatsManager + .queryAndAggregateUsageStats(startTime.toMillis(), endTime.toMillis()) + .mapValues { it.value.appLaunchCount } + } + + private fun convertIntegerResourceToDuration(@IntegerRes resourceId: Int): Duration = + Duration.ofSeconds(context.resources.getInteger(resourceId).toLong()) + + private fun currentTimeInDuration(): Duration = Duration.ofMillis(System.currentTimeMillis()) +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt index bf4a2abf9edc..f420c5be456f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt @@ -22,12 +22,15 @@ import androidx.datastore.core.CorruptionException import androidx.datastore.core.DataStore import androidx.datastore.core.DataStoreFactory import androidx.datastore.core.Serializer -import androidx.datastore.dataStore import androidx.datastore.dataStoreFile import com.android.framework.protobuf.InvalidProtocolBufferException import com.android.internal.annotations.VisibleForTesting +import java.io.IOException import java.io.InputStream import java.io.OutputStream +import java.time.Duration +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.first /** @@ -46,17 +49,44 @@ constructor(private val dataStore: DataStore<WindowingEducationProto>) { serializer = WindowingEducationProtoSerializer, produceFile = { context.dataStoreFile(APP_HANDLE_EDUCATION_DATASTORE_FILEPATH) })) + /** Provides dataStore.data flow and handles exceptions thrown during collection */ + val dataStoreFlow: Flow<WindowingEducationProto> = + dataStore.data.catch { exception -> + // dataStore.data throws an IOException when an error is encountered when reading data + if (exception is IOException) { + Log.e( + TAG, + "Error in reading app handle education related data from datastore, data is " + + "stored in a file named $APP_HANDLE_EDUCATION_DATASTORE_FILEPATH", + exception) + } else { + throw exception + } + } + /** * Reads and returns the [WindowingEducationProto] Proto object from the DataStore. If the * DataStore is empty or there's an error reading, it returns the default value of Proto. */ - suspend fun windowingEducationProto(): WindowingEducationProto = - try { - dataStore.data.first() - } catch (e: Exception) { - Log.e(TAG, "Unable to read from datastore") - WindowingEducationProto.getDefaultInstance() - } + suspend fun windowingEducationProto(): WindowingEducationProto = dataStoreFlow.first() + + /** + * Updates [AppHandleEducation.appUsageStats] and + * [AppHandleEducation.appUsageStatsLastUpdateTimestampMillis] fields in datastore with + * [appUsageStats] and [appUsageStatsLastUpdateTimestamp]. + */ + suspend fun updateAppUsageStats( + appUsageStats: Map<String, Int>, + appUsageStatsLastUpdateTimestamp: Duration + ) { + val currentAppHandleProto = windowingEducationProto().appHandleEducation.toBuilder() + currentAppHandleProto + .putAllAppUsageStats(appUsageStats) + .setAppUsageStatsLastUpdateTimestampMillis(appUsageStatsLastUpdateTimestamp.toMillis()) + dataStore.updateData { preferences: WindowingEducationProto -> + preferences.toBuilder().setAppHandleEducation(currentAppHandleProto).build() + } + } companion object { private const val TAG = "AppHandleEducationDatastoreRepository" diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md index 0acc7df98d1c..faa97ac4512f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md @@ -98,9 +98,8 @@ Don't: ### Exposing shared code for use in Launcher Launcher doesn't currently build against the Shell library, but needs to have access to some shared AIDL interfaces and constants. Currently, all AIDL files, and classes under the -`com.android.wm.shell.util` package are automatically built into the `SystemUISharedLib` that +`com.android.wm.shell.shared` package are automatically built into the `SystemUISharedLib` that Launcher uses. -If the new code doesn't fall into those categories, they can be added explicitly in the Shell's -[Android.bp](/libs/WindowManager/Shell/Android.bp) file under the -`wm_shell_util-sources` filegroup.
\ No newline at end of file +If the new code doesn't fall into those categories, they should be moved to the Shell shared +package (`com.android.wm.shell.shared`) under the `WindowManager-Shell-shared` library.
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md index 84f6af4125b8..72d1a76b17e3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md @@ -27,10 +27,13 @@ building to check the log state (is enabled) before printing the print format st traces in Winscope) ### Kotlin +Kotlin protologging is supported but not as optimized as in Java. -Protolog tool does not yet have support for Kotlin code (see [b/168581922](https://b.corp.google.com/issues/168581922)). -For logging in Kotlin, use the [KtProtoLog](/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt) -class which has a similar API to the Java ProtoLog class. +The Protolog tool does not yet have support for Kotlin code ([b/168581922](https://b.corp.google.com/issues/168581922)). + +What this implies is that ProtoLogs are not pre-processed to extract the static strings out when used in Kotlin. So, +there is no memory gain when using ProtoLogging in Kotlin. The logs will still be traced to Perfetto, but with a subtly +worse performance due to the additional string interning that needs to be done at run time instead of at build time. ### Enabling ProtoLog command line logging Run these commands to enable protologs (in logcat) for WM Core ([list of all core tags](/core/java/com/android/internal/protolog/ProtoLogGroup.java)): diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java index 4284d06a293f..1ffa54103d62 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java @@ -18,6 +18,7 @@ package com.android.wm.shell.freeform; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; + import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE; import android.animation.Animator; @@ -116,9 +117,11 @@ public class FreeformTaskTransitionHandler } @Override - public void startMinimizedModeTransition(WindowContainerTransaction wct) { + public IBinder startMinimizedModeTransition(WindowContainerTransaction wct) { final int type = WindowManager.TRANSIT_TO_BACK; - mPendingTransitionTokens.add(mTransitions.startTransition(type, wct, this)); + final IBinder token = mTransitions.startTransition(type, wct, this); + mPendingTransitionTokens.add(token); + return token; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java index 8da4c6ab4b36..ea68a694c3b9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java @@ -16,6 +16,7 @@ package com.android.wm.shell.freeform; +import android.os.IBinder; import android.window.WindowContainerTransaction; /** @@ -38,8 +39,9 @@ public interface FreeformTaskTransitionStarter { * * @param wct the {@link WindowContainerTransaction} that changes the windowing mode * + * @return the started transition */ - void startMinimizedModeTransition(WindowContainerTransaction wct); + IBinder startMinimizedModeTransition(WindowContainerTransaction wct); /** * Starts close window transition diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index b0c896fbe516..4df649ca8c93 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -43,6 +43,7 @@ import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.shared.animation.Interpolators; +import com.android.wm.shell.shared.pip.PipContentOverlay; import com.android.wm.shell.transition.Transitions; import java.lang.annotation.Retention; @@ -418,7 +419,7 @@ public class PipAnimationController { } SurfaceControl getContentOverlayLeash() { - return mContentOverlay == null ? null : mContentOverlay.mLeash; + return mContentOverlay == null ? null : mContentOverlay.getLeash(); } void setColorContentOverlay(Context context) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index b3beb4ab79c1..ab222c9cdbf6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -91,6 +91,7 @@ import com.android.wm.shell.pip.phone.PipMotionHelper; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.shared.animation.Interpolators; import com.android.wm.shell.shared.annotations.ShellMainThread; +import com.android.wm.shell.shared.pip.PipContentOverlay; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.transition.Transitions; @@ -98,6 +99,7 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.Objects; import java.util.Optional; +import java.util.StringJoiner; import java.util.function.Consumer; import java.util.function.IntConsumer; @@ -361,8 +363,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, SurfaceControl mPipOverlay; /** - * The app bounds used for the buffer size of the - * {@link com.android.wm.shell.pip.PipContentOverlay.PipAppIconOverlay}. + * The app bounds used for the buffer size of the {@link PipContentOverlay.PipAppIconOverlay}. * * Note that this is empty if the overlay is removed or if it's some other type of overlay * defined in {@link PipContentOverlay}. @@ -695,6 +696,16 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return; } + if (mSplitScreenOptional.isPresent()) { + // If pip activity will reparent to origin task case and if the origin task still + // under split root, apply exit split transaction to make it expand to fullscreen. + SplitScreenController split = mSplitScreenOptional.get(); + if (split.isTaskInSplitScreen(mTaskInfo.lastParentTaskIdBeforePip)) { + split.prepareExitSplitScreen(wct, split.getStageOfTask( + mTaskInfo.lastParentTaskIdBeforePip), + SplitScreenController.EXIT_REASON_APP_FINISHED); + } + } mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct, destinationBounds); return; } @@ -821,6 +832,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPictureInPictureParams.getTitle()); mPipParamsChangedForwarder.notifySubtitleChanged( mPictureInPictureParams.getSubtitle()); + logRemoteActions(mPictureInPictureParams); } mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo); @@ -1102,6 +1114,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } applyNewPictureInPictureParams(newParams); mPictureInPictureParams = newParams; + logRemoteActions(mPictureInPictureParams); } @Override @@ -1410,6 +1423,16 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } } + private void logRemoteActions(@NonNull PictureInPictureParams params) { + StringJoiner sj = new StringJoiner("|", "[", "]"); + if (params.hasSetActions()) { + params.getActions().forEach((action) -> sj.add(action.getTitle())); + } + + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: PIP remote actions=%s", TAG, sj.toString()); + } + /** * Animates resizing of the pinned stack given the duration. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 7ba6ec452b83..05d19846bfee 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -41,6 +41,7 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection; import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection; import static com.android.wm.shell.pip.PipTransitionState.ENTERED_PIP; +import static com.android.wm.shell.transition.Transitions.TRANSIT_CLEANUP_PIP_EXIT; import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP; import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT; import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP; @@ -74,6 +75,7 @@ import com.android.wm.shell.common.pip.PipMenuController; import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.shared.TransitionUtil; +import com.android.wm.shell.shared.pip.PipContentOverlay; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.CounterRotatorHelper; @@ -122,6 +124,8 @@ public class PipTransition extends PipTransitionController { @Nullable private IBinder mMoveToBackTransition; private IBinder mRequestedEnterTransition; + private IBinder mCleanupTransition; + private WindowContainerToken mRequestedEnterTask; /** The Task window that is currently in PIP windowing mode. */ @Nullable @@ -232,10 +236,12 @@ public class PipTransition extends PipTransitionController { // Exiting PIP. final int type = info.getType(); - if (transition.equals(mExitTransition) || transition.equals(mMoveToBackTransition)) { + if (transition.equals(mExitTransition) || transition.equals(mMoveToBackTransition) + || transition.equals(mCleanupTransition)) { mExitDestinationBounds.setEmpty(); mExitTransition = null; mMoveToBackTransition = null; + mCleanupTransition = null; mHasFadeOut = false; if (mFinishCallback != null) { callFinishCallback(null /* wct */); @@ -269,6 +275,9 @@ public class PipTransition extends PipTransitionController { removePipImmediately(info, startTransaction, finishTransaction, finishCallback, pipTaskInfo); break; + case TRANSIT_CLEANUP_PIP_EXIT: + cleanupPipExitTransition(startTransaction, finishCallback); + break; default: throw new IllegalStateException("mExitTransition with unexpected transit type=" + transitTypeToString(type)); @@ -768,7 +777,19 @@ public class PipTransition extends PipTransitionController { mPipAnimationController.resetAnimatorState(); finishTransaction.remove(pipLeash); } - finishCallback.onTransitionFinished(wct); + + if (mFixedRotationState == FIXED_ROTATION_TRANSITION) { + // TODO(b/358226697): start a new transition with the WCT instead of applying it in + // the {@link finishCallback}, to ensure shell creates a transition for it. + finishCallback.onTransitionFinished(wct); + } else { + // Apply wct in separate transition so that it can be correctly handled by the + // {@link FreeformTaskTransitionObserver} when desktop windowing (which does not + // utilize fixed rotation transitions for exiting pip) is enabled (See b/288910069). + mCleanupTransition = mTransitions.startTransition( + TRANSIT_CLEANUP_PIP_EXIT, wct, this); + finishCallback.onTransitionFinished(null); + } }; mFinishTransaction = finishTransaction; @@ -914,6 +935,16 @@ public class PipTransition extends PipTransitionController { finishCallback.onTransitionFinished(null); } + /** + * For {@link Transitions#TRANSIT_CLEANUP_PIP_EXIT} which applies final config changes needed + * after the exit from pip transition animation finishes. + */ + private void cleanupPipExitTransition(@NonNull SurfaceControl.Transaction startTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + startTransaction.apply(); + finishCallback.onTransitionFinished(null); + } + /** Whether we should handle the given {@link TransitionInfo} animation as entering PIP. */ private boolean isEnteringPip(@NonNull TransitionInfo info) { for (int i = info.getChanges().size() - 1; i >= 0; --i) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 5ec0c11109a0..755e9581326a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -240,6 +240,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb */ private final DisplayChangeController.OnDisplayChangingListener mRotationController = ( displayId, fromRotation, toRotation, newDisplayAreaInfo, t) -> { + if (fromRotation == toRotation) { + // OnDisplayChangingListener also gets triggered upon Display size changes; + // in PiP1, those are handled separately by OnDisplaysChangedListener callbacks. + return; + } + if (mPipTransitionController.handleRotateDisplay(fromRotation, toRotation, t)) { return; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java index 0d2b8e70422d..06d231144d81 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java @@ -35,9 +35,9 @@ import androidx.annotation.NonNull; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.DismissViewUtils; import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.bubbles.DismissCircleView; -import com.android.wm.shell.common.bubbles.DismissView; import com.android.wm.shell.common.pip.PipUiEventLogger; +import com.android.wm.shell.shared.bubbles.DismissCircleView; +import com.android.wm.shell.shared.bubbles.DismissView; import com.android.wm.shell.shared.magnetictarget.MagnetizedObject; import kotlin.Unit; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java index 8a9302bcfc98..8ebdc96c21a3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterExitAnimator.java @@ -22,6 +22,7 @@ import android.animation.ValueAnimator; import android.annotation.IntDef; import android.content.Context; import android.graphics.Rect; +import android.view.Surface; import android.view.SurfaceControl; import androidx.annotation.NonNull; @@ -51,8 +52,10 @@ public class PipEnterExitAnimator extends ValueAnimator @NonNull private final SurfaceControl mLeash; private final SurfaceControl.Transaction mStartTransaction; - private final int mEnterAnimationDuration; + private final SurfaceControl.Transaction mFinishTransaction; + private final int mEnterExitAnimationDuration; private final @BOUNDS int mDirection; + private final @Surface.Rotation int mRotation; // optional callbacks for tracking animation start and end @Nullable private Runnable mAnimationStartCallback; @@ -62,37 +65,59 @@ public class PipEnterExitAnimator extends ValueAnimator private final Rect mStartBounds = new Rect(); private final Rect mEndBounds = new Rect(); + @Nullable private final Rect mSourceRectHint; + private final Rect mSourceRectHintInsets = new Rect(); + private final Rect mZeroInsets = new Rect(0, 0, 0, 0); + // Bounds updated by the evaluator as animator is running. private final Rect mAnimatedRect = new Rect(); private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; private final RectEvaluator mRectEvaluator; + private final RectEvaluator mInsetEvaluator; private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper; public PipEnterExitAnimator(Context context, @NonNull SurfaceControl leash, SurfaceControl.Transaction startTransaction, + SurfaceControl.Transaction finishTransaction, @NonNull Rect baseBounds, @NonNull Rect startBounds, @NonNull Rect endBounds, - @BOUNDS int direction) { + @Nullable Rect sourceRectHint, + @BOUNDS int direction, + @Surface.Rotation int rotation) { mLeash = leash; mStartTransaction = startTransaction; + mFinishTransaction = finishTransaction; mBaseBounds.set(baseBounds); mStartBounds.set(startBounds); mAnimatedRect.set(startBounds); mEndBounds.set(endBounds); mRectEvaluator = new RectEvaluator(mAnimatedRect); + mInsetEvaluator = new RectEvaluator(new Rect()); mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context); mDirection = direction; + mRotation = rotation; + + mSourceRectHint = sourceRectHint != null ? new Rect(sourceRectHint) : null; + if (mSourceRectHint != null) { + mSourceRectHintInsets.set( + mSourceRectHint.left - mBaseBounds.left, + mSourceRectHint.top - mBaseBounds.top, + mBaseBounds.right - mSourceRectHint.right, + mBaseBounds.bottom - mSourceRectHint.bottom + ); + } mSurfaceControlTransactionFactory = new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory(); - mEnterAnimationDuration = context.getResources() + mEnterExitAnimationDuration = context.getResources() .getInteger(R.integer.config_pipEnterAnimationDuration); - setDuration(mEnterAnimationDuration); + setObjectValues(startBounds, endBounds); + setDuration(mEnterExitAnimationDuration); setEvaluator(mRectEvaluator); addListener(this); addUpdateListener(this); @@ -118,6 +143,14 @@ public class PipEnterExitAnimator extends ValueAnimator @Override public void onAnimationEnd(@NonNull Animator animation) { + if (mFinishTransaction != null) { + // finishTransaction might override some state (eg. corner radii) so we want to + // manually set the state to the end of the animation + mPipSurfaceTransactionHelper.scaleAndCrop(mFinishTransaction, mLeash, mSourceRectHint, + mBaseBounds, mAnimatedRect, getInsets(1f), isInPipDirection(), 1f) + .round(mFinishTransaction, mLeash, isInPipDirection()) + .shadow(mFinishTransaction, mLeash, isInPipDirection()); + } if (mAnimationEndCallback != null) { mAnimationEndCallback.run(); } @@ -127,19 +160,32 @@ public class PipEnterExitAnimator extends ValueAnimator public void onAnimationUpdate(@NonNull ValueAnimator animation) { final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); final float fraction = getAnimatedFraction(); + Rect insets = getInsets(fraction); + // TODO (b/350801661): implement fixed rotation - mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, null, - mBaseBounds, mAnimatedRect, null, isInPipDirection(), fraction) + mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint, + mBaseBounds, mAnimatedRect, insets, isInPipDirection(), fraction) .round(tx, mLeash, isInPipDirection()) .shadow(tx, mLeash, isInPipDirection()); tx.apply(); } + private Rect getInsets(float fraction) { + Rect startInsets = isInPipDirection() ? mZeroInsets : mSourceRectHintInsets; + Rect endInsets = isInPipDirection() ? mSourceRectHintInsets : mZeroInsets; + + return mInsetEvaluator.evaluate(fraction, startInsets, endInsets); + } + private boolean isInPipDirection() { return mDirection == BOUNDS_ENTER; } + private boolean isOutPipDirection() { + return mDirection == BOUNDS_EXIT; + } + // no-ops @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java index 88f9e4c740e3..d565776c9917 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java @@ -134,9 +134,10 @@ public class PipResizeAnimator extends ValueAnimator Rect baseBounds, Rect targetBounds, float degrees) { Matrix transformTensor = new Matrix(); final float[] mMatrixTmp = new float[9]; - final float scale = (float) targetBounds.width() / baseBounds.width(); + final float scaleX = (float) targetBounds.width() / baseBounds.width(); + final float scaleY = (float) targetBounds.height() / baseBounds.height(); - transformTensor.setScale(scale, scale); + transformTensor.setScale(scaleX, scaleY); transformTensor.postTranslate(targetBounds.left, targetBounds.top); transformTensor.postRotate(degrees, targetBounds.centerX(), targetBounds.centerY()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java index eb6caba0600c..e9c4c14234e6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java @@ -29,7 +29,6 @@ import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Bundle; -import android.view.InsetsState; import android.view.SurfaceControl; import android.window.DisplayAreaInfo; import android.window.WindowContainerTransaction; @@ -200,17 +199,8 @@ public class PipController implements ConfigurationChangeListener, DisplayLayout layout = new DisplayLayout(mContext, mContext.getDisplay()); mPipDisplayLayoutState.setDisplayLayout(layout); - mDisplayController.addDisplayWindowListener(this); mDisplayController.addDisplayChangingController(this); mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(), - new DisplayInsetsController.OnInsetsChangedListener() { - @Override - public void insetsChanged(InsetsState insetsState) { - setDisplayLayout(mDisplayController - .getDisplayLayout(mPipDisplayLayoutState.getDisplayId())); - } - }); - mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(), new ImeListener(mDisplayController, mPipDisplayLayoutState.getDisplayId()) { @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { @@ -285,34 +275,37 @@ public class PipController implements ConfigurationChangeListener, setDisplayLayout(mDisplayController.getDisplayLayout(displayId)); } - @Override - public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { - if (displayId != mPipDisplayLayoutState.getDisplayId()) { - return; - } - setDisplayLayout(mDisplayController.getDisplayLayout(displayId)); - } - /** * A callback for any observed transition that contains a display change in its - * {@link android.window.TransitionRequestInfo} with a non-zero rotation delta. + * {@link android.window.TransitionRequestInfo}, */ @Override public void onDisplayChange(int displayId, int fromRotation, int toRotation, @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction t) { + if (displayId != mPipDisplayLayoutState.getDisplayId()) { + return; + } + final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds()); + final float boundsScale = mPipBoundsState.getBoundsScale(); + + // Update the display layout caches even if we are not in PiP. + setDisplayLayout(mDisplayController.getDisplayLayout(displayId)); + if (!mPipTransitionState.isInPip()) { return; } - // Calculate the snap fraction pre-rotation. - float snapFraction = mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds()); + mPipTouchHandler.updateMinMaxSize(mPipBoundsState.getAspectRatio()); - // Update the caches to reflect the new display layout and movement bounds. - mPipDisplayLayoutState.rotateTo(toRotation); + // Update the caches to reflect the new display layout in the movement bounds; + // temporarily update bounds to be at the top left for the movement bounds calculation. + Rect toBounds = new Rect(0, 0, + (int) Math.ceil(mPipBoundsState.getMaxSize().x * boundsScale), + (int) Math.ceil(mPipBoundsState.getMaxSize().y * boundsScale)); + mPipBoundsState.setBounds(toBounds); mPipTouchHandler.updateMovementBounds(); - // The policy is to keep PiP width, height and snap fraction invariant. - Rect toBounds = mPipBoundsState.getBounds(); + // The policy is to keep PiP snap fraction invariant. mPipBoundsAlgorithm.applySnapFraction(toBounds, snapFraction); mPipBoundsState.setBounds(toBounds); t.setBounds(mPipTransitionState.mPipTaskToken, toBounds); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java index e04178e6d58c..b3070f29c6e2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java @@ -35,9 +35,9 @@ import androidx.annotation.NonNull; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.DismissViewUtils; import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.bubbles.DismissCircleView; -import com.android.wm.shell.common.bubbles.DismissView; import com.android.wm.shell.common.pip.PipUiEventLogger; +import com.android.wm.shell.shared.bubbles.DismissCircleView; +import com.android.wm.shell.shared.bubbles.DismissView; import com.android.wm.shell.shared.magnetictarget.MagnetizedObject; import kotlin.Unit; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java index 218d456e9596..0324fdba0fbf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java @@ -56,7 +56,6 @@ import kotlin.Unit; import kotlin.jvm.functions.Function0; import java.util.Optional; -import java.util.function.Consumer; /** * A helper to animate and manipulate the PiP. @@ -134,18 +133,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private final PhysicsAnimator.SpringConfig mConflictResolutionSpringConfig = new PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_NO_BOUNCY); - @Nullable private Runnable mUpdateMovementBoundsRunnable; - - private final Consumer<Rect> mUpdateBoundsCallback = (Rect newBounds) -> { - if (mPipBoundsState.getBounds().equals(newBounds)) { - return; - } - - mMenuController.updateMenuLayout(newBounds); - mPipBoundsState.setBounds(newBounds); - maybeUpdateMovementBounds(); - }; - /** * Whether we're springing to the touch event location (vs. moving it to that position * instantly). We spring-to-touch after PIP is dragged out of the magnetic target, since it was @@ -683,16 +670,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, cleanUpHighPerfSessionMaybe(); } - void setUpdateMovementBoundsRunnable(Runnable updateMovementBoundsRunnable) { - mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable; - } - - private void maybeUpdateMovementBounds() { - if (mUpdateMovementBoundsRunnable != null) { - mUpdateMovementBoundsRunnable.run(); - } - } - /** * Notifies the floating coordinator that we're moving, and sets the animating to bounds so * we return these bounds from @@ -720,7 +697,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, /** * Directly resizes the PiP to the given {@param bounds}. */ - private void resizeAndAnimatePipUnchecked(Rect toBounds, int duration) { + void resizeAndAnimatePipUnchecked(Rect toBounds, int duration) { if (mPipBoundsState.getMotionBoundsState().isInMotion()) { // Do not carry out any resizing if we are dragging or physics animator is running. return; @@ -813,7 +790,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, cleanUpHighPerfSessionMaybe(); // Signal that the transition is done - should update transition state by default. - mPipScheduler.scheduleFinishResizePip(false /* configAtEnd */); + mPipScheduler.scheduleFinishResizePip(destinationBounds, false /* configAtEnd */); } private void startResizeAnimation(SurfaceControl.Transaction startTx, @@ -829,8 +806,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, startTx, finishTx, mPipBoundsState.getBounds(), mPipBoundsState.getBounds(), destinationBounds, duration, 0f /* angle */); animator.setAnimationEndCallback(() -> { - mUpdateBoundsCallback.accept(destinationBounds); - // In case an ongoing drag/fling was present before a deterministic resize transition // kicked in, we need to update the update bounds properly before cleaning in-motion // state. @@ -839,7 +814,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, cleanUpHighPerfSessionMaybe(); // Signal that we are done with resize transition - mPipScheduler.scheduleFinishResizePip(true /* configAtEnd */); + mPipScheduler.scheduleFinishResizePip(destinationBounds, true /* configAtEnd */); }); animator.start(); } @@ -849,7 +824,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, // The physics animation ended, though we may not necessarily be done animating, such as // when we're still dragging after moving out of the magnetic target. Only set the final // bounds state and clear motion bounds completely if the whole animation is over. - mPipBoundsState.setBounds(mPipBoundsState.getMotionBoundsState().getBoundsInMotion()); mPipBoundsState.getMotionBoundsState().onAllAnimationsEnded(); } mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java index d28204add0ac..f5ef64dff94b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java @@ -50,7 +50,6 @@ import com.android.wm.shell.common.pip.PipUiEventLogger; import com.android.wm.shell.pip2.animation.PipResizeAnimator; import java.io.PrintWriter; -import java.util.function.Consumer; /** * Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to @@ -86,8 +85,6 @@ public class PipResizeGestureHandler implements private final Rect mUserResizeBounds = new Rect(); private final Rect mDownBounds = new Rect(); private final Rect mStartBoundsAfterRelease = new Rect(); - private final Runnable mUpdateMovementBoundsRunnable; - private final Consumer<Rect> mUpdateResizeBoundsCallback; private float mTouchSlop; @@ -121,7 +118,6 @@ public class PipResizeGestureHandler implements PipTouchState pipTouchState, PipScheduler pipScheduler, PipTransitionState pipTransitionState, - Runnable updateMovementBoundsRunnable, PipUiEventLogger pipUiEventLogger, PhonePipMenuController menuActivityController, ShellExecutor mainExecutor, @@ -138,18 +134,9 @@ public class PipResizeGestureHandler implements mPipTransitionState = pipTransitionState; mPipTransitionState.addPipTransitionStateChangedListener(this); - mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable; mPhonePipMenuController = menuActivityController; mPipUiEventLogger = pipUiEventLogger; mPinchResizingAlgorithm = new PipPinchResizingAlgorithm(); - - mUpdateResizeBoundsCallback = (rect) -> { - mUserResizeBounds.set(rect); - // mMotionHelper.synchronizePinnedStackBounds(); - mPipBoundsState.setBounds(rect); - mUpdateMovementBoundsRunnable.run(); - resetState(); - }; } void init() { @@ -563,11 +550,13 @@ public class PipResizeGestureHandler implements mLastResizeBounds, duration, mAngle); animator.setAnimationEndCallback(() -> { // All motion operations have actually finished, so make bounds cache updates. - mUpdateResizeBoundsCallback.accept(mLastResizeBounds); + mUserResizeBounds.set(mLastResizeBounds); + resetState(); cleanUpHighPerfSessionMaybe(); // Signal that we are done with resize transition - mPipScheduler.scheduleFinishResizePip(true /* configAtEnd */); + mPipScheduler.scheduleFinishResizePip( + mLastResizeBounds, true /* configAtEnd */); }); animator.start(); break; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java index ac670cf3f828..f4defdc7963c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java @@ -52,11 +52,14 @@ public class PipScheduler { private final Context mContext; private final PipBoundsState mPipBoundsState; + private final PhonePipMenuController mPipMenuController; private final ShellExecutor mMainExecutor; private final PipTransitionState mPipTransitionState; private PipSchedulerReceiver mSchedulerReceiver; private PipTransitionController mPipTransitionController; + @Nullable private Runnable mUpdateMovementBoundsRunnable; + /** * Temporary PiP CUJ codes to schedule PiP related transitions directly from Shell. * This is used for a broadcast receiver to resolve intents. This should be removed once @@ -94,10 +97,12 @@ public class PipScheduler { public PipScheduler(Context context, PipBoundsState pipBoundsState, + PhonePipMenuController pipMenuController, ShellExecutor mainExecutor, PipTransitionState pipTransitionState) { mContext = context; mPipBoundsState = pipBoundsState; + mPipMenuController = pipMenuController; mMainExecutor = mainExecutor; mPipTransitionState = pipTransitionState; @@ -189,9 +194,13 @@ public class PipScheduler { * Signals to Core to finish the PiP resize transition. * Note that we do not allow any actual WM Core changes at this point. * + * @param toBounds destination bounds used only for internal state updates - not sent to Core. * @param configAtEnd true if we are waiting for config updates at the end of the transition. */ - public void scheduleFinishResizePip(boolean configAtEnd) { + public void scheduleFinishResizePip(Rect toBounds, boolean configAtEnd) { + // Make updates to the internal state to reflect new bounds + onFinishingPipResize(toBounds); + SurfaceControl.Transaction tx = null; if (configAtEnd) { tx = new SurfaceControl.Transaction(); @@ -238,4 +247,23 @@ public class PipScheduler { tx.setMatrix(leash, transformTensor, mMatrixTmp); tx.apply(); } + + void setUpdateMovementBoundsRunnable(Runnable updateMovementBoundsRunnable) { + mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable; + } + + private void maybeUpdateMovementBounds() { + if (mUpdateMovementBoundsRunnable != null) { + mUpdateMovementBoundsRunnable.run(); + } + } + + private void onFinishingPipResize(Rect newBounds) { + if (mPipBoundsState.getBounds().equals(newBounds)) { + return; + } + mPipBoundsState.setBounds(newBounds); + mPipMenuController.updateMenuLayout(newBounds); + maybeUpdateMovementBounds(); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java new file mode 100644 index 000000000000..262c14d2bfe3 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2024 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.pip2.phone; + +import static com.android.wm.shell.pip2.phone.PipTransition.ANIMATING_BOUNDS_CHANGE_DURATION; + +import android.app.ActivityManager; +import android.app.PictureInPictureParams; +import android.content.Context; +import android.graphics.Rect; +import android.os.Bundle; +import android.view.SurfaceControl; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.util.Preconditions; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.pip.PipBoundsAlgorithm; +import com.android.wm.shell.common.pip.PipBoundsState; +import com.android.wm.shell.common.pip.PipUtils; +import com.android.wm.shell.pip2.animation.PipResizeAnimator; +import com.android.wm.shell.shared.annotations.ShellMainThread; + +/** + * A Task Listener implementation used only for CUJs and trigger paths that cannot be initiated via + * Transitions framework directly. + * Hence, it's the intention to keep the usage of this class for a very limited set of cases. + */ +public class PipTaskListener implements ShellTaskOrganizer.TaskListener, + PipTransitionState.PipTransitionStateChangedListener { + private static final int ASPECT_RATIO_CHANGE_DURATION = 250; + private static final String ANIMATING_ASPECT_RATIO_CHANGE = "animating_aspect_ratio_change"; + + private final Context mContext; + private final PipTransitionState mPipTransitionState; + private final PipScheduler mPipScheduler; + private final PipBoundsState mPipBoundsState; + private final PipBoundsAlgorithm mPipBoundsAlgorithm; + private final ShellExecutor mMainExecutor; + private final PictureInPictureParams mPictureInPictureParams = + new PictureInPictureParams.Builder().build(); + + private boolean mWaitingForAspectRatioChange = false; + + public PipTaskListener(Context context, + ShellTaskOrganizer shellTaskOrganizer, + PipTransitionState pipTransitionState, + PipScheduler pipScheduler, + PipBoundsState pipBoundsState, + PipBoundsAlgorithm pipBoundsAlgorithm, + @ShellMainThread ShellExecutor mainExecutor) { + mContext = context; + mPipTransitionState = pipTransitionState; + mPipScheduler = pipScheduler; + mPipBoundsState = pipBoundsState; + mPipBoundsAlgorithm = pipBoundsAlgorithm; + mMainExecutor = mainExecutor; + + mPipTransitionState.addPipTransitionStateChangedListener(this); + if (PipUtils.isPip2ExperimentEnabled()) { + mMainExecutor.execute(() -> { + shellTaskOrganizer.addListenerForType(this, + ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP); + }); + } + } + + void setPictureInPictureParams(@Nullable PictureInPictureParams params) { + if (mPictureInPictureParams.equals(params)) { + return; + } + mPictureInPictureParams.copyOnlySet(params != null ? params + : new PictureInPictureParams.Builder().build()); + } + + @NonNull + public PictureInPictureParams getPictureInPictureParams() { + return mPictureInPictureParams; + } + + @Override + public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { + PictureInPictureParams params = taskInfo.pictureInPictureParams; + if (mPictureInPictureParams.equals(params)) { + return; + } + setPictureInPictureParams(params); + float newAspectRatio = mPictureInPictureParams.getAspectRatioFloat(); + if (PipUtils.aspectRatioChanged(newAspectRatio, mPipBoundsState.getAspectRatio())) { + mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> { + onAspectRatioChanged(newAspectRatio); + }); + } + } + + private void onAspectRatioChanged(float ratio) { + mPipBoundsState.setAspectRatio(ratio); + + final Rect destinationBounds = mPipBoundsAlgorithm.getAdjustedDestinationBounds( + mPipBoundsState.getBounds(), mPipBoundsState.getAspectRatio()); + // Avoid scheduling a resize transition if destination bounds are unchanged, otherise + // we could end up with a no-op transition. + if (!destinationBounds.equals(mPipBoundsState.getBounds())) { + Bundle extra = new Bundle(); + extra.putBoolean(ANIMATING_ASPECT_RATIO_CHANGE, true); + mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra); + } + } + + @Override + public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState, + @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) { + switch (newState) { + case PipTransitionState.SCHEDULED_BOUNDS_CHANGE: + mWaitingForAspectRatioChange = extra.getBoolean(ANIMATING_ASPECT_RATIO_CHANGE); + if (!mWaitingForAspectRatioChange) break; + + mPipScheduler.scheduleAnimateResizePip( + mPipBoundsAlgorithm.getAdjustedDestinationBounds( + mPipBoundsState.getBounds(), mPipBoundsState.getAspectRatio()), + false /* configAtEnd */, ASPECT_RATIO_CHANGE_DURATION); + break; + case PipTransitionState.CHANGING_PIP_BOUNDS: + final SurfaceControl.Transaction startTx = extra.getParcelable( + PipTransition.PIP_START_TX, SurfaceControl.Transaction.class); + final SurfaceControl.Transaction finishTx = extra.getParcelable( + PipTransition.PIP_FINISH_TX, SurfaceControl.Transaction.class); + final Rect destinationBounds = extra.getParcelable( + PipTransition.PIP_DESTINATION_BOUNDS, Rect.class); + final int duration = extra.getInt(ANIMATING_BOUNDS_CHANGE_DURATION, + PipTransition.BOUNDS_CHANGE_JUMPCUT_DURATION); + + Preconditions.checkNotNull(mPipTransitionState.mPinnedTaskLeash, + "Leash is null for bounds transition."); + + if (mWaitingForAspectRatioChange) { + PipResizeAnimator animator = new PipResizeAnimator(mContext, + mPipTransitionState.mPinnedTaskLeash, startTx, finishTx, + destinationBounds, + mPipBoundsState.getBounds(), destinationBounds, duration, + 0f /* delta */); + animator.setAnimationEndCallback(() -> { + mPipScheduler.scheduleFinishResizePip( + destinationBounds, false /* configAtEnd */); + }); + animator.start(); + } + break; + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java index d75fa00b1fdd..029f001401c5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java @@ -206,7 +206,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha mMenuController.addListener(new PipMenuListener()); mGesture = new DefaultPipTouchGesture(); mMotionHelper = pipMotionHelper; - mMotionHelper.setUpdateMovementBoundsRunnable(this::updateMovementBounds); + mPipScheduler.setUpdateMovementBoundsRunnable(this::updateMovementBounds); mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger, mMotionHelper, mainExecutor); mTouchState = new PipTouchState(ViewConfiguration.get(context), @@ -219,8 +219,8 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha menuController::hideMenu, mainExecutor); mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsAlgorithm, - pipBoundsState, mTouchState, mPipScheduler, mPipTransitionState, - this::updateMovementBounds, pipUiEventLogger, menuController, mainExecutor, + pipBoundsState, mTouchState, mPipScheduler, mPipTransitionState, pipUiEventLogger, + menuController, mainExecutor, mPipPerfHintController); mPipBoundsState.addOnAspectRatioChangedCallback(this::updateMinMaxSize); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java index ed18712b283d..f93233ec7461 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java @@ -36,6 +36,7 @@ import android.content.Context; import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; +import android.view.Surface; import android.view.SurfaceControl; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; @@ -50,10 +51,10 @@ import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipMenuController; import com.android.wm.shell.common.pip.PipUtils; -import com.android.wm.shell.pip.PipContentOverlay; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip2.animation.PipAlphaAnimator; import com.android.wm.shell.pip2.animation.PipEnterExitAnimator; +import com.android.wm.shell.shared.pip.PipContentOverlay; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; @@ -87,6 +88,7 @@ public class PipTransition extends PipTransitionController implements // private final Context mContext; + private final PipTaskListener mPipTaskListener; private final PipScheduler mPipScheduler; private final PipTransitionState mPipTransitionState; @@ -118,6 +120,7 @@ public class PipTransition extends PipTransitionController implements PipBoundsState pipBoundsState, PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm, + PipTaskListener pipTaskListener, PipScheduler pipScheduler, PipTransitionState pipTransitionState, PipUiStateChangeController pipUiStateChangeController) { @@ -125,6 +128,7 @@ public class PipTransition extends PipTransitionController implements pipBoundsAlgorithm); mContext = context; + mPipTaskListener = pipTaskListener; mPipScheduler = pipScheduler; mPipScheduler.setPipTransitionController(this); mPipTransitionState = pipTransitionState; @@ -395,17 +399,22 @@ public class PipTransition extends PipTransitionController implements SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash; Preconditions.checkNotNull(pipLeash, "Leash is null for bounds transition."); + Rect sourceRectHint = null; + if (pipChange.getTaskInfo() != null + && pipChange.getTaskInfo().pictureInPictureParams != null) { + sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint(); + } + PipEnterExitAnimator animator = new PipEnterExitAnimator(mContext, pipLeash, - startTransaction, startBounds, startBounds, endBounds, - PipEnterExitAnimator.BOUNDS_ENTER); + startTransaction, finishTransaction, startBounds, startBounds, endBounds, + sourceRectHint, PipEnterExitAnimator.BOUNDS_ENTER, Surface.ROTATION_0); tx.addTransactionCommittedListener(mPipScheduler.getMainExecutor(), this::onClientDrawAtTransitionEnd); finishWct.setBoundsChangeTransaction(pipTaskToken, tx); - animator.setAnimationEndCallback(() -> { - finishCallback.onTransitionFinished(finishWct.isEmpty() ? null : finishWct); - }); + animator.setAnimationEndCallback(() -> + finishCallback.onTransitionFinished(finishWct)); animator.start(); return true; @@ -449,19 +458,53 @@ public class PipTransition extends PipTransitionController implements TransitionInfo.Change pipChange = getChangeByToken(info, pipToken); if (pipChange == null) { - return false; + // pipChange is null, check to see if we've reparented the PIP activity for + // the multi activity case. If so we should use the activity leash instead + for (TransitionInfo.Change change : info.getChanges()) { + if (change.getTaskInfo() == null + && change.getLastParent() != null + && change.getLastParent().equals(pipToken)) { + pipChange = change; + break; + } + } + + // failsafe + if (pipChange == null) { + return false; + } + } + + // for multi activity, we need to manually set the leash layer + if (pipChange.getTaskInfo() == null) { + TransitionInfo.Change parent = getChangeByToken(info, pipChange.getParent()); + if (parent != null) { + startTransaction.setLayer(parent.getLeash(), Integer.MAX_VALUE - 1); + } } Rect startBounds = pipChange.getStartAbsBounds(); Rect endBounds = pipChange.getEndAbsBounds(); SurfaceControl pipLeash = pipChange.getLeash(); + Preconditions.checkNotNull(pipLeash, "Leash is null for exit transition."); + + Rect sourceRectHint = null; + if (pipChange.getTaskInfo() != null + && pipChange.getTaskInfo().pictureInPictureParams != null) { + // single activity + sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint(); + } else if (mPipTaskListener.getPictureInPictureParams().hasSourceBoundsHint()) { + // multi activity + sourceRectHint = mPipTaskListener.getPictureInPictureParams().getSourceRectHint(); + } PipEnterExitAnimator animator = new PipEnterExitAnimator(mContext, pipLeash, - startTransaction, startBounds, startBounds, endBounds, - PipEnterExitAnimator.BOUNDS_EXIT); + startTransaction, finishTransaction, endBounds, startBounds, endBounds, + sourceRectHint, PipEnterExitAnimator.BOUNDS_EXIT, Surface.ROTATION_0); + animator.setAnimationEndCallback(() -> { - finishCallback.onTransitionFinished(null); mPipTransitionState.setState(PipTransitionState.EXITED_PIP); + finishCallback.onTransitionFinished(null); }); animator.start(); @@ -510,6 +553,7 @@ public class PipTransition extends PipTransitionController implements // cache the original task token to check for multi-activity case later final ActivityManager.RunningTaskInfo pipTask = request.getPipTask(); PictureInPictureParams pipParams = pipTask.pictureInPictureParams; + mPipTaskListener.setPictureInPictureParams(pipParams); mPipBoundsState.setBoundsStateForEntry(pipTask.topActivity, pipTask.topActivityInfo, pipParams, mPipBoundsAlgorithm); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl index 4048c5b8feab..ebfd3571ae6d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl @@ -24,7 +24,7 @@ import android.os.Bundle; import android.view.IRecentsAnimationRunner; import com.android.wm.shell.recents.IRecentTasksListener; -import com.android.wm.shell.util.GroupedRecentTaskInfo; +import com.android.wm.shell.shared.GroupedRecentTaskInfo; /** * Interface that is exposed to remote callers to fetch recent tasks. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java index 77b8663861ab..8c5d1e7e069d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java @@ -19,8 +19,8 @@ package com.android.wm.shell.recents; import android.annotation.Nullable; import android.graphics.Color; +import com.android.wm.shell.shared.GroupedRecentTaskInfo; import com.android.wm.shell.shared.annotations.ExternalThread; -import com.android.wm.shell.util.GroupedRecentTaskInfo; import java.util.List; import java.util.concurrent.Executor; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java index 2f0af8557538..39bea1bed447 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java @@ -51,16 +51,16 @@ import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import com.android.wm.shell.shared.GroupedRecentTaskInfo; import com.android.wm.shell.shared.annotations.ExternalThread; import com.android.wm.shell.shared.annotations.ShellMainThread; import com.android.wm.shell.shared.desktopmode.DesktopModeFlags; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; +import com.android.wm.shell.shared.split.SplitBounds; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; -import com.android.wm.shell.util.GroupedRecentTaskInfo; -import com.android.wm.shell.util.SplitBounds; import java.io.PrintWriter; import java.util.ArrayList; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index 7a9eb1c582b7..c90da052dd72 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -30,7 +30,7 @@ import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_CAN_HAND_OFF_ANIMATION; -import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS; +import static com.android.wm.shell.shared.split.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS; import android.annotation.Nullable; import android.annotation.SuppressLint; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java index 27ded57b38d9..cea995ded256 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java @@ -321,8 +321,10 @@ public class SplitscreenEventLogger { 0 /* enterReason */, 0 /* exitReason */, mLastSplitRatio, - 0 /* mainStagePosition */, 0 /* mainStageUid */, - 0 /* sideStagePosition */, 0 /* sideStageUid */, + mLastMainStagePosition, + mLastMainStageUid, + mLastSideStagePosition, + mLastSideStageUid, 0 /* dragInstanceId */, mLoggerSessionId.getId()); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 0b5c75104f65..8921ceb6175d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -21,6 +21,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT; import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; import static android.view.Display.DEFAULT_DISPLAY; @@ -133,13 +134,13 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.shared.TransactionPool; import com.android.wm.shell.shared.TransitionUtil; +import com.android.wm.shell.shared.split.SplitBounds; import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition; import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.splitscreen.SplitScreen.StageType; import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason; import com.android.wm.shell.transition.DefaultMixedHandler; import com.android.wm.shell.transition.Transitions; -import com.android.wm.shell.util.SplitBounds; import com.android.wm.shell.windowdecor.WindowDecorViewModel; import dalvik.annotation.optimization.NeverCompile; @@ -684,6 +685,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, setSideStagePosition(splitPosition, wct); options1 = options1 != null ? options1 : new Bundle(); addActivityOptions(options1, mSideStage); + prepareTasksForSplitScreen(new int[] {taskId1, taskId2}, wct); wct.startTask(taskId1, options1); startWithTask(wct, taskId2, options2, snapPosition, remoteTransition, instanceId); @@ -714,6 +716,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, options1 = options1 != null ? options1 : new Bundle(); addActivityOptions(options1, mSideStage); wct.sendPendingIntent(pendingIntent, fillInIntent, options1); + prepareTasksForSplitScreen(new int[] {taskId}, wct); startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId); } @@ -757,11 +760,30 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, options1 = options1 != null ? options1 : new Bundle(); addActivityOptions(options1, mSideStage); wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1); + prepareTasksForSplitScreen(new int[] {taskId}, wct); startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId); } /** + * Prepares the tasks whose IDs are provided in `taskIds` for split screen by clearing their + * bounds and windowing mode so that they can inherit the bounds and the windowing mode of + * their root stages. + * + * @param taskIds an array of task IDs whose bounds will be cleared. + * @param wct transaction to clear the bounds on the tasks. + */ + private void prepareTasksForSplitScreen(int[] taskIds, WindowContainerTransaction wct) { + for (int taskId : taskIds) { + ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId); + if (task != null) { + wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED) + .setBounds(task.token, null); + } + } + } + + /** * Starts with the second task to a split pair in one transition. * * @param wct transaction to start the first task diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java index b18feefe7eb3..81f444ba2af3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java @@ -352,12 +352,17 @@ public class SplashscreenContentDrawer { /** Extract the window background color from {@code attrs}. */ private static int peekWindowBGColor(Context context, SplashScreenWindowAttrs attrs) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "peekWindowBGColor"); - final Drawable themeBGDrawable; + Drawable themeBGDrawable = null; if (attrs.mWindowBgColor != 0) { themeBGDrawable = new ColorDrawable(attrs.mWindowBgColor); } else if (attrs.mWindowBgResId != 0) { - themeBGDrawable = context.getDrawable(attrs.mWindowBgResId); - } else { + try { + themeBGDrawable = context.getDrawable(attrs.mWindowBgResId); + } catch (Resources.NotFoundException e) { + Slog.w(TAG, "Unable get drawable from resource", e); + } + } + if (themeBGDrawable == null) { themeBGDrawable = createDefaultBackgroundDrawable(); Slog.w(TAG, "Window background does not exist, using " + themeBGDrawable); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java index 287e779d8e24..1140c82a7a08 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java @@ -48,7 +48,7 @@ public class ShellInit { public ShellInit(ShellExecutor mainExecutor) { mMainExecutor = mainExecutor; - ProtoLog.registerGroups(ShellProtoLogGroup.values()); + ProtoLog.init(ShellProtoLogGroup.values()); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java index a85188a9e04d..82c0aaf3bc8b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java @@ -118,6 +118,13 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, mTaskViewTaskController.startShortcutActivity(shortcut, options, launchBounds); } + /** + * Moves the current task in taskview out of the view and back to fullscreen. + */ + public void moveToFullscreen() { + mTaskViewTaskController.moveToFullscreen(); + } + @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { if (mTaskViewTaskController.isUsingShellTransitions()) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java index 9750d3ec99f5..e74342e1910c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java @@ -17,6 +17,7 @@ package com.android.wm.shell.taskview; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.WindowManager.TRANSIT_CHANGE; import android.annotation.NonNull; @@ -256,6 +257,24 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { mTaskViewTransitions.startInstantTransition(TRANSIT_CHANGE, wct); } + /** + * Moves the current task in TaskView out of the view and back to fullscreen. + */ + public void moveToFullscreen() { + if (mTaskToken == null) return; + mShellExecutor.execute(() -> { + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setWindowingMode(mTaskToken, WINDOWING_MODE_UNDEFINED); + wct.setAlwaysOnTop(mTaskToken, false); + mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false); + mTaskViewTransitions.moveTaskViewToFullscreen(wct, this); + if (mListener != null) { + // Task is being "removed" from the clients perspective + mListener.onTaskRemovalStarted(mTaskInfo.taskId); + } + }); + } + private void prepareActivityOptions(ActivityOptions options, Rect launchBounds) { final Binder launchCookie = new Binder(); mShellExecutor.execute(() -> { @@ -585,7 +604,6 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { }); } mTaskViewBase.onTaskVanished(taskInfo); - mTaskOrganizer.setInterceptBackPressedOnTaskRoot(taskInfo.token, false); } } @@ -699,6 +717,9 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { mTaskViewBase.setResizeBgColor(startTransaction, backgroundColor); } + // After the embedded task has appeared, set it to non-trimmable. This is important + // to prevent recents from trimming and removing the embedded task. + wct.setTaskTrimmableFromRecents(taskInfo.token, false /* isTrimmableFromRecents */); mTaskViewBase.onTaskAppeared(mTaskInfo, mTaskLeash); if (mListener != null) { final int taskId = mTaskInfo.taskId; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java index 15fe7abb96a5..39648f65b4f3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java @@ -236,6 +236,12 @@ public class TaskViewTransitions implements Transitions.TransitionHandler { startNextTransition(); } + void moveTaskViewToFullscreen(@NonNull WindowContainerTransaction wct, + @NonNull TaskViewTaskController taskView) { + mPending.add(new PendingTransition(TRANSIT_CHANGE, wct, taskView, null /* cookie */)); + startNextTransition(); + } + /** Starts a new transition to make the given {@code taskView} visible. */ public void setTaskViewVisible(TaskViewTaskController taskView, boolean visible) { setTaskViewVisible(taskView, visible, false /* reorder */); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index a9a4e1093392..9b0fb20f9777 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -473,7 +473,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { change.getLeash(), startTransaction); } else if (isOnlyTranslucent && TransitionUtil.isOpeningType(info.getType()) - && TransitionUtil.isClosingType(mode)) { + && TransitionUtil.isClosingType(mode)) { // If there is a closing translucent task in an OPENING transition, we will // actually select a CLOSING animation, so move the closing task into // the animating part of the z-order. @@ -767,12 +767,12 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { a = mTransitionAnimation.loadKeyguardExitAnimation(flags, (changeFlags & FLAG_SHOW_WALLPAPER) != 0); } else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) { - a = mTransitionAnimation.loadKeyguardUnoccludeAnimation(); + a = mTransitionAnimation.loadKeyguardUnoccludeAnimation(options.getUserId()); } else if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) { if (isOpeningType) { - a = mTransitionAnimation.loadVoiceActivityOpenAnimation(enter); + a = mTransitionAnimation.loadVoiceActivityOpenAnimation(enter, options.getUserId()); } else { - a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter); + a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter, options.getUserId()); } } else if (changeMode == TRANSIT_CHANGE) { // In the absence of a specific adapter, we just want to keep everything stationary. @@ -783,9 +783,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } else if (overrideType == ANIM_CUSTOM && (!isTask || options.getOverrideTaskTransition())) { a = mTransitionAnimation.loadAnimationRes(options.getPackageName(), enter - ? options.getEnterResId() : options.getExitResId()); + ? options.getEnterResId() : options.getExitResId(), options.getUserId()); } else if (overrideType == ANIM_OPEN_CROSS_PROFILE_APPS && enter) { - a = mTransitionAnimation.loadCrossProfileAppEnterAnimation(); + a = mTransitionAnimation.loadCrossProfileAppEnterAnimation(options.getUserId()); } else if (overrideType == ANIM_CLIP_REVEAL) { a = mTransitionAnimation.createClipRevealAnimationLocked(type, wallpaperTransit, enter, endBounds, endBounds, options.getTransitionBounds()); @@ -905,9 +905,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { final Rect bounds = change.getEndAbsBounds(); // Show the right drawable depending on the user we're transitioning to. final Drawable thumbnailDrawable = change.hasFlags(FLAG_CROSS_PROFILE_OWNER_THUMBNAIL) - ? mContext.getDrawable(R.drawable.ic_account_circle) - : change.hasFlags(FLAG_CROSS_PROFILE_WORK_THUMBNAIL) - ? mEnterpriseThumbnailDrawable : null; + ? mContext.getDrawable(R.drawable.ic_account_circle) + : change.hasFlags(FLAG_CROSS_PROFILE_WORK_THUMBNAIL) + ? mEnterpriseThumbnailDrawable : null; if (thumbnailDrawable == null) { return; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java index b7b42c723929..209fc39b096a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java @@ -114,7 +114,6 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler { t.clear(); mMainExecutor.execute(() -> { finishCallback.onTransitionFinished(wct); - mRemote = null; }); } }; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 68217c0cc98a..7dc336bdfb7d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -193,6 +193,9 @@ public class Transitions implements RemoteCallable<Transitions>, /** Remote Transition that split accepts but ultimately needs to be animated by the remote. */ public static final int TRANSIT_SPLIT_PASSTHROUGH = TRANSIT_FIRST_CUSTOM + 18; + /** Transition to set windowing mode after exit pip transition is finished animating. */ + public static final int TRANSIT_CLEANUP_PIP_EXIT = WindowManager.TRANSIT_FIRST_CUSTOM + 19; + /** Transition type for desktop mode transitions. */ public static final int TRANSIT_DESKTOP_MODE_TYPES = WindowManager.TRANSIT_FIRST_CUSTOM + 100; @@ -1187,12 +1190,15 @@ public class Transitions implements RemoteCallable<Transitions>, } if (request.getDisplayChange() != null) { TransitionRequestInfo.DisplayChange change = request.getDisplayChange(); - if (change.getEndRotation() != change.getStartRotation()) { - // Is a rotation, so dispatch to all displayChange listeners + if (change.getStartRotation() != change.getEndRotation() + || (change.getStartAbsBounds() != null + && !change.getStartAbsBounds().equals(change.getEndAbsBounds()))) { + // Is a display change, so dispatch to all displayChange listeners if (wct == null) { wct = new WindowContainerTransaction(); } - mDisplayController.onDisplayRotateRequested(wct, change.getDisplayId(), + mDisplayController.onDisplayChangeRequested(wct, change.getDisplayId(), + change.getStartAbsBounds(), change.getEndAbsBounds(), change.getStartRotation(), change.getEndRotation()); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS deleted file mode 100644 index 482aaab6bc74..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS +++ /dev/null @@ -1 +0,0 @@ -per-file KtProtolog.kt = file:platform/development:/tools/winscope/OWNERS diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java index d8dba71c0a4f..11976aed9315 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java @@ -19,6 +19,7 @@ package com.android.wm.shell.windowdecor; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.pm.PackageManager.FEATURE_PC; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; import static android.view.WindowManager.TRANSIT_CHANGE; @@ -26,7 +27,6 @@ import static android.view.WindowManager.TRANSIT_CHANGE; import android.app.ActivityManager.RunningTaskInfo; import android.content.ContentResolver; import android.content.Context; -import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; @@ -174,8 +174,12 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { if (decoration == null) return; + if (!shouldShowWindowDecor(taskInfo)) { + destroyWindowDecoration(taskInfo); + return; + } + decoration.relayout(taskInfo); - setupCaptionColor(taskInfo, decoration); } @Override @@ -237,19 +241,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { decoration.close(); } - private void setupCaptionColor(RunningTaskInfo taskInfo, CaptionWindowDecoration decoration) { - if (TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) { - decoration.setCaptionColor(Color.TRANSPARENT); - } else { - final int statusBarColor = taskInfo.taskDescription.getStatusBarColor(); - decoration.setCaptionColor(statusBarColor); - } - } - private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { return true; } + if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) { + return false; + } if (taskInfo.getActivityType() != ACTIVITY_TYPE_STANDARD) { return false; } @@ -311,7 +309,6 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { windowDecoration.setTaskDragResizer(taskPositioner); windowDecoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, false /* setTaskCropAndPosition */); - setupCaptionColor(taskInfo, windowDecoration); } private class CaptionTouchEventListener implements diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index 231570f5d90e..349ee0b1fcda 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -35,7 +35,6 @@ import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.GradientDrawable; -import android.graphics.drawable.VectorDrawable; import android.os.Handler; import android.util.Size; import android.view.Choreographer; @@ -310,6 +309,9 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL } private void bindData(View rootView, RunningTaskInfo taskInfo) { + // Set up the tint first so that the drawable can be stylized when loaded. + setupCaptionColor(taskInfo); + final boolean isFullscreen = taskInfo.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FULLSCREEN; rootView.findViewById(R.id.maximize_window) @@ -317,7 +319,16 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL : R.drawable.decor_maximize_button_dark); } - void setCaptionColor(int captionColor) { + private void setupCaptionColor(RunningTaskInfo taskInfo) { + if (TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) { + setCaptionColor(Color.TRANSPARENT); + } else { + final int statusBarColor = taskInfo.taskDescription.getStatusBarColor(); + setCaptionColor(statusBarColor); + } + } + + private void setCaptionColor(int captionColor) { if (mResult.mRootView == null) { return; } @@ -334,20 +345,16 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL caption.getResources().getColorStateList(buttonTintColorRes, null /* theme */); final View back = caption.findViewById(R.id.back_button); - final VectorDrawable backBackground = (VectorDrawable) back.getBackground(); - backBackground.setTintList(buttonTintColor); + back.setBackgroundTintList(buttonTintColor); final View minimize = caption.findViewById(R.id.minimize_window); - final VectorDrawable minimizeBackground = (VectorDrawable) minimize.getBackground(); - minimizeBackground.setTintList(buttonTintColor); + minimize.setBackgroundTintList(buttonTintColor); final View maximize = caption.findViewById(R.id.maximize_window); - final VectorDrawable maximizeBackground = (VectorDrawable) maximize.getBackground(); - maximizeBackground.setTintList(buttonTintColor); + maximize.setBackgroundTintList(buttonTintColor); final View close = caption.findViewById(R.id.close_window); - final VectorDrawable closeBackground = (VectorDrawable) close.getBackground(); - closeBackground.setTintList(buttonTintColor); + close.setBackgroundTintList(buttonTintColor); } boolean isHandlingDragResize() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 457b5112076e..c88c1e28b011 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -56,6 +56,7 @@ import android.graphics.Region; import android.hardware.input.InputManager; import android.net.Uri; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; @@ -96,15 +97,17 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.MultiInstanceHelper; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource; +import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler; import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition; +import com.android.wm.shell.desktopmode.DesktopTasksLimiter; import com.android.wm.shell.desktopmode.DesktopWallpaperActivity; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; import com.android.wm.shell.shared.annotations.ShellBackgroundThread; import com.android.wm.shell.shared.desktopmode.DesktopModeFlags; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreen.StageType; @@ -150,6 +153,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final InputManager mInputManager; private final InteractionJankMonitor mInteractionJankMonitor; private final MultiInstanceHelper mMultiInstanceHelper; + private final Optional<DesktopTasksLimiter> mDesktopTasksLimiter; private boolean mTransitionDragActive; private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>(); @@ -163,6 +167,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private TaskOperations mTaskOperations; private final Supplier<SurfaceControl.Transaction> mTransactionFactory; private final Transitions mTransitions; + private final Optional<DesktopActivityOrientationChangeHandler> + mActivityOrientationChangeHandler; private SplitScreenController mSplitScreenController; @@ -211,7 +217,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, InteractionJankMonitor interactionJankMonitor, AppToWebGenericLinksParser genericLinksParser, - MultiInstanceHelper multiInstanceHelper + MultiInstanceHelper multiInstanceHelper, + Optional<DesktopTasksLimiter> desktopTasksLimiter, + Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler ) { this( context, @@ -236,7 +244,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { SurfaceControl.Transaction::new, rootTaskDisplayAreaOrganizer, new SparseArray<>(), - interactionJankMonitor); + interactionJankMonitor, + desktopTasksLimiter, + activityOrientationChangeHandler); } @VisibleForTesting @@ -263,7 +273,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { Supplier<SurfaceControl.Transaction> transactionFactory, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, SparseArray<DesktopModeWindowDecoration> windowDecorByTaskId, - InteractionJankMonitor interactionJankMonitor) { + InteractionJankMonitor interactionJankMonitor, + Optional<DesktopTasksLimiter> desktopTasksLimiter, + Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler) { mContext = context; mMainExecutor = shellExecutor; mMainHandler = mainHandler; @@ -290,6 +302,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mSysUIPackageName = mContext.getResources().getString( com.android.internal.R.string.config_systemUi); mInteractionJankMonitor = interactionJankMonitor; + mDesktopTasksLimiter = desktopTasksLimiter; + mActivityOrientationChangeHandler = activityOrientationChangeHandler; mOnDisplayChangingListener = (displayId, fromRotation, toRotation, displayAreaInfo, t) -> { DesktopModeWindowDecoration decoration; RunningTaskInfo taskInfo; @@ -381,6 +395,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { incrementEventReceiverTasks(taskInfo.displayId); } decoration.relayout(taskInfo); + mActivityOrientationChangeHandler.ifPresent(handler -> + handler.handleActivityOrientationChange(oldTaskInfo, taskInfo)); } @Override @@ -473,7 +489,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { Toast.makeText(mContext, R.string.desktop_mode_non_resizable_snap_text, Toast.LENGTH_SHORT).show(); } else { - mDesktopTasksController.snapToHalfScreen(decoration.mTaskInfo, + mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext, + Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE, "maximize_menu_resizable"); + mDesktopTasksController.snapToHalfScreen( + decoration.mTaskInfo, + decoration.mTaskSurface, decoration.mTaskInfo.configuration.windowConfiguration.getBounds(), left ? SnapPosition.LEFT : SnapPosition.RIGHT); } @@ -487,16 +507,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { if (decoration == null) { return; } - openInBrowser(uri); + openInBrowser(uri, decoration.getUser()); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } - private void openInBrowser(Uri uri) { + private void openInBrowser(Uri uri, @NonNull UserHandle userHandle) { final Intent intent = Intent.makeMainSelectorActivity(ACTION_MAIN, CATEGORY_APP_BROWSER) .setData(uri) .addFlags(FLAG_ACTIVITY_NEW_TASK); - mContext.startActivity(intent); + mContext.startActivityAsUser(intent, userHandle); } private void onToDesktop(int taskId, DesktopModeTransitionSource source) { @@ -615,6 +635,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { // {@link DesktopModeWindowDecoration#setOnMaximizeOrRestoreClickListener}, which // should shared with the maximize menu's maximize/restore actions. onMaximizeOrRestore(decoration.mTaskInfo.taskId, "caption_bar_button"); + } else if (id == R.id.minimize_window) { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + mDesktopTasksController.onDesktopWindowMinimize(wct, mTaskId); + final IBinder transition = mTaskOperations.minimizeTask(mTaskToken, wct); + mDesktopTasksLimiter.ifPresent(limiter -> + limiter.addPendingMinimizeChange(transition, mDisplayId, mTaskId)); } } @@ -628,7 +654,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } if (id != R.id.caption_handle && id != R.id.desktop_mode_caption && id != R.id.open_menu_button && id != R.id.close_window - && id != R.id.maximize_window) { + && id != R.id.maximize_window && id != R.id.minimize_window) { return false; } @@ -768,7 +794,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return true; } final boolean touchingButton = (id == R.id.close_window || id == R.id.maximize_window - || id == R.id.open_menu_button); + || id == R.id.open_menu_button || id == R.id.minimize_window); switch (e.getActionMasked()) { case MotionEvent.ACTION_DOWN: { mDragPointerId = e.getPointerId(0); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index 75a6cd7b720e..81251b81e239 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -26,7 +26,7 @@ import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT; -import static com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON; +import static com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getFineResizeCornerSize; import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLargeResizeCornerSize; @@ -54,6 +54,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Handler; import android.os.Trace; +import android.os.UserHandle; import android.util.Size; import android.util.Slog; import android.view.Choreographer; @@ -79,10 +80,10 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.MultiInstanceHelper; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.shared.annotations.ShellBackgroundThread; import com.android.wm.shell.shared.desktopmode.DesktopModeFlags; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.windowdecor.extension.TaskInfoKt; import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder; @@ -480,6 +481,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin return mGenericLink; } + UserHandle getUser() { + return mUserContext.getUser(); + } + private void updateDragResizeListener(SurfaceControl oldDecorationSurface) { if (!isDragResizable(mTaskInfo)) { if (!mTaskInfo.positionInParent.equals(mPositionInParent)) { @@ -643,6 +648,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin final RelayoutParams.OccludingCaptionElement controlsElement = new RelayoutParams.OccludingCaptionElement(); controlsElement.mWidthResId = R.dimen.desktop_mode_customizable_caption_margin_end; + if (Flags.enableMinimizeButton()) { + controlsElement.mWidthResId = + R.dimen.desktop_mode_customizable_caption_with_minimize_button_margin_end; + } controlsElement.mAlignment = RelayoutParams.OccludingCaptionElement.Alignment.END; relayoutParams.mOccludingCaptionElements.add(controlsElement); } else if (isAppHandle && !Flags.enableAdditionalWindowsAboveStatusBar()) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt index 114c33114421..9c73e4a38aa9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt @@ -121,8 +121,14 @@ class MaximizeMenu( /** Closes the maximize window and releases its view. */ fun close() { - maximizeMenuView?.animateCloseMenu { - maximizeMenu?.releaseView() + val view = maximizeMenuView + val menu = maximizeMenu + if (view == null) { + menu?.releaseView() + } else { + view.animateCloseMenu { + menu?.releaseView() + } } maximizeMenu = null maximizeMenuView = null @@ -318,7 +324,7 @@ class MaximizeMenu( rootView.setOnTouchListener { _, event -> if (event.actionMasked == ACTION_OUTSIDE) { onOutsideTouchListener?.invoke() - false + return@setOnTouchListener false } true } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java index ad238c35dd83..61b93932013c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java @@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.hardware.input.InputManager; +import android.os.IBinder; import android.os.SystemClock; import android.util.Log; import android.view.InputDevice; @@ -84,13 +85,17 @@ class TaskOperations { } } - void minimizeTask(WindowContainerToken taskToken) { - WindowContainerTransaction wct = new WindowContainerTransaction(); + IBinder minimizeTask(WindowContainerToken taskToken) { + return minimizeTask(taskToken, new WindowContainerTransaction()); + } + + IBinder minimizeTask(WindowContainerToken taskToken, WindowContainerTransaction wct) { wct.reorder(taskToken, false); if (Transitions.ENABLE_SHELL_TRANSITIONS) { - mTransitionStarter.startMinimizedModeTransition(wct); + return mTransitionStarter.startMinimizedModeTransition(wct); } else { mSyncQueue.queue(wct); + return null; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java index 7f2c1a81d20c..4a884eb50595 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java @@ -104,9 +104,6 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback, wct.reorder(mDesktopWindowDecoration.mTaskInfo.token, true); mTaskOrganizer.applyTransaction(wct); } - } else { - mInteractionJankMonitor.begin(mDesktopWindowDecoration.mTaskSurface, - mDesktopWindowDecoration.mContext, CUJ_DESKTOP_MODE_DRAG_WINDOW); } mDragStartListener.onDragStart(mDesktopWindowDecoration.mTaskInfo.taskId); mRepositionTaskBounds.set(mTaskBoundsAtDragStart); @@ -133,6 +130,9 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback, mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds); } } else if (mCtrlType == CTRL_TYPE_UNDEFINED) { + // Begin window drag CUJ instrumentation only when drag position moves. + mInteractionJankMonitor.begin(mDesktopWindowDecoration.mTaskSurface, + mDesktopWindowDecoration.mContext, CUJ_DESKTOP_MODE_DRAG_WINDOW); final SurfaceControl.Transaction t = mTransactionSupplier.get(); DragPositioningCallbackUtility.setPositionOnDrag(mDesktopWindowDecoration, mRepositionTaskBounds, mTaskBoundsAtDragStart, mRepositionStartPoint, t, x, y); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt index d0eb6da36702..033d69583725 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt @@ -35,6 +35,7 @@ import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.ui.graphics.toArgb import androidx.core.content.withStyledAttributes +import androidx.core.view.isGone import androidx.core.view.isVisible import com.android.internal.R.attr.materialColorOnSecondaryContainer import com.android.internal.R.attr.materialColorOnSurface @@ -42,6 +43,7 @@ import com.android.internal.R.attr.materialColorSecondaryContainer import com.android.internal.R.attr.materialColorSurfaceContainerHigh import com.android.internal.R.attr.materialColorSurfaceContainerLow import com.android.internal.R.attr.materialColorSurfaceDim +import com.android.window.flags.Flags.enableMinimizeButton import com.android.wm.shell.R import com.android.wm.shell.shared.desktopmode.DesktopModeFlags import com.android.wm.shell.windowdecor.MaximizeButtonView @@ -82,9 +84,9 @@ internal class AppHeaderViewHolder( .getDimensionPixelSize(R.dimen.desktop_mode_header_buttons_ripple_radius) /** - * The app chip, maximize and close button's height extends to the top & bottom edges of the - * header, and their width may be larger than their height. This is by design to increase the - * clickable and hover-able bounds of the view as much as possible. However, to prevent the + * The app chip, minimize, maximize and close button's height extends to the top & bottom edges + * of the header, and their width may be larger than their height. This is by design to increase + * the clickable and hover-able bounds of the view as much as possible. However, to prevent the * ripple drawable from being as large as the views (and asymmetrical), insets are applied to * the background ripple drawable itself to give the appearance of a smaller button * (with padding between itself and the header edges / sibling buttons) but without affecting @@ -94,6 +96,12 @@ internal class AppHeaderViewHolder( vertical = context.resources .getDimensionPixelSize(R.dimen.desktop_mode_header_app_chip_ripple_inset_vertical) ) + private val minimizeDrawableInsets = DrawableInsets( + vertical = context.resources + .getDimensionPixelSize(R.dimen.desktop_mode_header_minimize_ripple_inset_vertical), + horizontal = context.resources + .getDimensionPixelSize(R.dimen.desktop_mode_header_minimize_ripple_inset_horizontal) + ) private val maximizeDrawableInsets = DrawableInsets( vertical = context.resources .getDimensionPixelSize(R.dimen.desktop_mode_header_maximize_ripple_inset_vertical), @@ -115,6 +123,7 @@ internal class AppHeaderViewHolder( private val maximizeButtonView: MaximizeButtonView = rootView.requireViewById(R.id.maximize_button_view) private val maximizeWindowButton: ImageButton = rootView.requireViewById(R.id.maximize_window) + private val minimizeWindowButton: ImageButton = rootView.requireViewById(R.id.minimize_window) private val appNameTextView: TextView = rootView.requireViewById(R.id.application_name) private val appIconImageView: ImageView = rootView.requireViewById(R.id.application_icon) val appNameTextWidth: Int @@ -131,6 +140,8 @@ internal class AppHeaderViewHolder( maximizeWindowButton.setOnGenericMotionListener(onCaptionGenericMotionListener) maximizeWindowButton.onLongClickListener = onLongClickListener closeWindowButton.setOnTouchListener(onCaptionTouchListener) + minimizeWindowButton.setOnClickListener(onCaptionButtonClickListener) + minimizeWindowButton.setOnTouchListener(onCaptionTouchListener) appNameTextView.text = appName appIconImageView.setImageBitmap(appIconBitmap) maximizeButtonView.onHoverAnimationFinishedListener = @@ -157,11 +168,13 @@ internal class AppHeaderViewHolder( val alpha = Color.alpha(color) closeWindowButton.imageTintList = ColorStateList.valueOf(color) maximizeWindowButton.imageTintList = ColorStateList.valueOf(color) + minimizeWindowButton.imageTintList = ColorStateList.valueOf(color) expandMenuButton.imageTintList = ColorStateList.valueOf(color) appNameTextView.isVisible = !taskInfo.isTransparentCaptionBarAppearance appNameTextView.setTextColor(color) appIconImageView.imageAlpha = alpha maximizeWindowButton.imageAlpha = alpha + minimizeWindowButton.imageAlpha = alpha closeWindowButton.imageAlpha = alpha expandMenuButton.imageAlpha = alpha context.withStyledAttributes( @@ -176,8 +189,10 @@ internal class AppHeaderViewHolder( openMenuButton.background = getDrawable(0) maximizeWindowButton.background = getDrawable(1) closeWindowButton.background = getDrawable(1) + minimizeWindowButton.background = getDrawable(1) } maximizeButtonView.setAnimationTints(isDarkMode()) + minimizeWindowButton.isGone = !enableMinimizeButton() } private fun bindDataWithThemedHeaders(taskInfo: RunningTaskInfo) { @@ -212,6 +227,16 @@ internal class AppHeaderViewHolder( } appIconImageView.imageAlpha = foregroundAlpha } + // Minimize button. + minimizeWindowButton.apply { + imageTintList = colorStateList + background = createRippleDrawable( + color = foregroundColor, + cornerRadius = headerButtonsRippleRadius, + drawableInsets = minimizeDrawableInsets + ) + } + minimizeWindowButton.isGone = !enableMinimizeButton() // Maximize button. maximizeButtonView.setAnimationTints( darkMode = header.appTheme == Theme.DARK, diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt index 8584b599a96c..880e02140db1 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt @@ -17,21 +17,32 @@ package com.android.wm.shell.flicker import android.tools.flicker.AssertionInvocationGroup +import android.tools.flicker.assertors.assertions.AppLayerIncreasesInSize import android.tools.flicker.assertors.assertions.AppLayerIsInvisibleAtEnd import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAlways import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAtStart import android.tools.flicker.assertors.assertions.AppWindowBecomesVisible +import android.tools.flicker.assertors.assertions.AppWindowCoversLeftHalfScreenAtEnd +import android.tools.flicker.assertors.assertions.AppWindowCoversRightHalfScreenAtEnd import android.tools.flicker.assertors.assertions.AppWindowHasDesktopModeInitialBoundsAtTheEnd +import android.tools.flicker.assertors.assertions.AppWindowHasMaxBoundsInOnlyOneDimension +import android.tools.flicker.assertors.assertions.AppWindowHasMaxDisplayHeight +import android.tools.flicker.assertors.assertions.AppWindowHasMaxDisplayWidth import android.tools.flicker.assertors.assertions.AppWindowHasSizeOfAtLeast import android.tools.flicker.assertors.assertions.AppWindowIsInvisibleAtEnd +import android.tools.flicker.assertors.assertions.AppWindowIsVisibleAlways +import android.tools.flicker.assertors.assertions.AppWindowMaintainsAspectRatioAlways import android.tools.flicker.assertors.assertions.AppWindowOnTopAtEnd import android.tools.flicker.assertors.assertions.AppWindowOnTopAtStart +import android.tools.flicker.assertors.assertions.AppWindowRemainInsideDisplayBounds +import android.tools.flicker.assertors.assertions.AppWindowReturnsToStartBoundsAndPosition import android.tools.flicker.assertors.assertions.LauncherWindowReplacesAppAsTopWindow import android.tools.flicker.config.AssertionTemplates import android.tools.flicker.config.FlickerConfigEntry import android.tools.flicker.config.ScenarioId -import android.tools.flicker.config.desktopmode.Components +import android.tools.flicker.config.desktopmode.Components.DESKTOP_MODE_APP import android.tools.flicker.config.desktopmode.Components.DESKTOP_WALLPAPER +import android.tools.flicker.config.desktopmode.Components.NON_RESIZABLE_APP import android.tools.flicker.extractors.ITransitionMatcher import android.tools.flicker.extractors.ShellTransitionScenarioExtractor import android.tools.flicker.extractors.TaggedCujTransitionMatcher @@ -46,29 +57,27 @@ class DesktopModeFlickerScenarios { FlickerConfigEntry( scenarioId = ScenarioId("END_DRAG_TO_DESKTOP"), extractor = - ShellTransitionScenarioExtractor( - transitionMatcher = - object : ITransitionMatcher { - override fun findAll( - transitions: Collection<Transition> - ): Collection<Transition> { - return transitions.filter { - // TODO(351168217) Use jank CUJ to extract a longer trace - it.type == TransitionType.DESKTOP_MODE_END_DRAG_TO_DESKTOP - } - } + ShellTransitionScenarioExtractor( + transitionMatcher = + object : ITransitionMatcher { + override fun findAll( + transitions: Collection<Transition> + ): Collection<Transition> { + return transitions.filter { + // TODO(351168217) Use jank CUJ to extract a longer trace + it.type == TransitionType.DESKTOP_MODE_END_DRAG_TO_DESKTOP } - ), + } + } + ), assertions = - AssertionTemplates.COMMON_ASSERTIONS + + AssertionTemplates.COMMON_ASSERTIONS + listOf( - AppLayerIsVisibleAlways(Components.DESKTOP_MODE_APP), - AppWindowOnTopAtEnd(Components.DESKTOP_MODE_APP), - AppWindowHasDesktopModeInitialBoundsAtTheEnd( - Components.DESKTOP_MODE_APP - ), - AppWindowBecomesVisible(DESKTOP_WALLPAPER) - ) + AppLayerIsVisibleAlways(DESKTOP_MODE_APP), + AppWindowOnTopAtEnd(DESKTOP_MODE_APP), + AppWindowHasDesktopModeInitialBoundsAtTheEnd(DESKTOP_MODE_APP), + AppWindowBecomesVisible(DESKTOP_WALLPAPER) + ) .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), ) @@ -78,57 +87,56 @@ class DesktopModeFlickerScenarios { FlickerConfigEntry( scenarioId = ScenarioId("CLOSE_APP"), extractor = - ShellTransitionScenarioExtractor( - transitionMatcher = - object : ITransitionMatcher { - override fun findAll( - transitions: Collection<Transition> - ): Collection<Transition> { - // In case there are multiple windows closing, filter out the - // last window closing. It should use the CLOSE_LAST_APP - // scenario below. - return transitions - .filter { it.type == TransitionType.CLOSE } - .sortedByDescending { it.id } - .drop(1) - } - } - ), + ShellTransitionScenarioExtractor( + transitionMatcher = + object : ITransitionMatcher { + override fun findAll( + transitions: Collection<Transition> + ): Collection<Transition> { + // In case there are multiple windows closing, filter out the + // last window closing. It should use the CLOSE_LAST_APP + // scenario below. + return transitions + .filter { it.type == TransitionType.CLOSE } + .sortedByDescending { it.id } + .drop(1) + } + } + ), assertions = - AssertionTemplates.COMMON_ASSERTIONS + + AssertionTemplates.COMMON_ASSERTIONS + listOf( - AppWindowOnTopAtStart(Components.DESKTOP_MODE_APP), - AppLayerIsVisibleAtStart(Components.DESKTOP_MODE_APP), - AppLayerIsInvisibleAtEnd(Components.DESKTOP_MODE_APP), - ) - .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + AppWindowOnTopAtStart(DESKTOP_MODE_APP), + AppLayerIsVisibleAtStart(DESKTOP_MODE_APP), + AppLayerIsInvisibleAtEnd(DESKTOP_MODE_APP) + ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), ) val CLOSE_LAST_APP = FlickerConfigEntry( scenarioId = ScenarioId("CLOSE_LAST_APP"), extractor = - ShellTransitionScenarioExtractor( - transitionMatcher = - object : ITransitionMatcher { - override fun findAll( - transitions: Collection<Transition> - ): Collection<Transition> { - val lastTransition = - transitions - .filter { it.type == TransitionType.CLOSE } - .maxByOrNull { it.id }!! - return listOf(lastTransition) - } - } - ), + ShellTransitionScenarioExtractor( + transitionMatcher = + object : ITransitionMatcher { + override fun findAll( + transitions: Collection<Transition> + ): Collection<Transition> { + val lastTransition = + transitions + .filter { it.type == TransitionType.CLOSE } + .maxByOrNull { it.id }!! + return listOf(lastTransition) + } + } + ), assertions = - AssertionTemplates.COMMON_ASSERTIONS + + AssertionTemplates.COMMON_ASSERTIONS + listOf( - AppWindowIsInvisibleAtEnd(Components.DESKTOP_MODE_APP), - LauncherWindowReplacesAppAsTopWindow(Components.DESKTOP_MODE_APP), - AppWindowIsInvisibleAtEnd(DESKTOP_WALLPAPER) - ) + AppWindowIsInvisibleAtEnd(DESKTOP_MODE_APP), + LauncherWindowReplacesAppAsTopWindow(DESKTOP_MODE_APP), + AppWindowIsInvisibleAtEnd(DESKTOP_WALLPAPER) + ) .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), ) @@ -136,29 +144,161 @@ class DesktopModeFlickerScenarios { FlickerConfigEntry( scenarioId = ScenarioId("CORNER_RESIZE"), extractor = - TaggedScenarioExtractorBuilder() - .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW) - .setTransitionMatcher( - TaggedCujTransitionMatcher(associatedTransitionRequired = false) - ) - .build(), + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS ) + val EDGE_RESIZE = + FlickerConfigEntry( + scenarioId = ScenarioId("EDGE_RESIZE"), + extractor = + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), + assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + + listOf( + AppLayerIncreasesInSize(DESKTOP_MODE_APP), + ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + ) + val CORNER_RESIZE_TO_MINIMUM_SIZE = FlickerConfigEntry( scenarioId = ScenarioId("CORNER_RESIZE_TO_MINIMUM_SIZE"), extractor = - TaggedScenarioExtractorBuilder() - .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW) - .setTransitionMatcher( - TaggedCujTransitionMatcher(associatedTransitionRequired = false) - ) - .build(), + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), assertions = - AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + - listOf(AppWindowHasSizeOfAtLeast(Components.DESKTOP_MODE_APP, 770, 700)) + AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + + listOf(AppWindowHasSizeOfAtLeast(DESKTOP_MODE_APP, 770, 700)) .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), ) + + val SNAP_RESIZE_LEFT_WITH_BUTTON = + FlickerConfigEntry( + scenarioId = ScenarioId("SNAP_RESIZE_LEFT_WITH_BUTTON"), + extractor = + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), + assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + + listOf(AppWindowCoversLeftHalfScreenAtEnd(DESKTOP_MODE_APP)) + .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + ) + + val SNAP_RESIZE_RIGHT_WITH_BUTTON = + FlickerConfigEntry( + scenarioId = ScenarioId("SNAP_RESIZE_RIGHT_WITH_BUTTON"), + extractor = + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), + assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + + listOf(AppWindowCoversRightHalfScreenAtEnd(DESKTOP_MODE_APP)) + .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + ) + + val SNAP_RESIZE_LEFT_WITH_DRAG = + FlickerConfigEntry( + scenarioId = ScenarioId("SNAP_RESIZE_LEFT_WITH_DRAG"), + extractor = + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), + assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + + listOf(AppWindowCoversLeftHalfScreenAtEnd(DESKTOP_MODE_APP)) + .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + ) + + val SNAP_RESIZE_RIGHT_WITH_DRAG = + FlickerConfigEntry( + scenarioId = ScenarioId("SNAP_RESIZE_RIGHT_WITH_DRAG"), + extractor = + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), + assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + + listOf(AppWindowCoversRightHalfScreenAtEnd(DESKTOP_MODE_APP)) + .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + ) + + val SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE = + FlickerConfigEntry( + scenarioId = ScenarioId("SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE"), + extractor = + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), + assertions = listOf( + AppWindowIsVisibleAlways(NON_RESIZABLE_APP), + AppWindowOnTopAtEnd(NON_RESIZABLE_APP), + AppWindowRemainInsideDisplayBounds(NON_RESIZABLE_APP), + AppWindowMaintainsAspectRatioAlways(NON_RESIZABLE_APP), + AppWindowReturnsToStartBoundsAndPosition(NON_RESIZABLE_APP) + ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + ) + + val MAXIMIZE_APP = + FlickerConfigEntry( + scenarioId = ScenarioId("MAXIMIZE_APP"), + extractor = + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), + assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + + listOf( + AppLayerIncreasesInSize(DESKTOP_MODE_APP), + AppWindowHasMaxDisplayHeight(DESKTOP_MODE_APP), + AppWindowHasMaxDisplayWidth(DESKTOP_MODE_APP) + ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + ) + + val MAXIMIZE_APP_NON_RESIZABLE = + FlickerConfigEntry( + scenarioId = ScenarioId("MAXIMIZE_APP_NON_RESIZABLE"), + extractor = + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), + assertions = + AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + + listOf( + AppLayerIncreasesInSize(DESKTOP_MODE_APP), + AppWindowMaintainsAspectRatioAlways(DESKTOP_MODE_APP), + AppWindowHasMaxBoundsInOnlyOneDimension(DESKTOP_MODE_APP) + ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + ) } } diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppLandscape.kt new file mode 100644 index 000000000000..217956671554 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppLandscape.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2024 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.flicker + +import android.tools.Rotation.ROTATION_90 +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP +import com.android.wm.shell.scenarios.MaximizeAppWindow +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Maximize app window by pressing the maximize button on the app header. + * + * Assert that the app window keeps the same increases in size, filling the vertical and horizontal + * stable display bounds. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class MaximizeAppLandscape : MaximizeAppWindow(rotation = ROTATION_90) { + @ExpectedScenarios(["MAXIMIZE_APP"]) + @Test + override fun maximizeAppWindow() = super.maximizeAppWindow() + + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizableLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizableLandscape.kt new file mode 100644 index 000000000000..b173a60132b2 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizableLandscape.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 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.flicker + +import android.tools.Rotation.ROTATION_90 +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP_NON_RESIZABLE +import com.android.wm.shell.scenarios.MaximizeAppWindow +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Maximize non-resizable app window by pressing the maximize button on the app header. + * + * Assert that the app window keeps the same increases in size, maintaining its aspect ratio, until + * filling the vertical or horizontal stable display bounds. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class MaximizeAppNonResizableLandscape : MaximizeAppWindow( + rotation = ROTATION_90, + isResizable = false +) { + @ExpectedScenarios(["MAXIMIZE_APP_NON_RESIZABLE"]) + @Test + override fun maximizeAppWindow() = super.maximizeAppWindow() + + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP_NON_RESIZABLE) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizablePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizablePortrait.kt new file mode 100644 index 000000000000..88888eee8378 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizablePortrait.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 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.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP_NON_RESIZABLE +import com.android.wm.shell.scenarios.MaximizeAppWindow +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Maximize non-resizable app window by pressing the maximize button on the app header. + * + * Assert that the app window keeps the same increases in size, maintaining its aspect ratio, until + * filling the vertical or horizontal stable display bounds. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class MaximizeAppNonResizablePortrait : MaximizeAppWindow(isResizable = false) { + @ExpectedScenarios(["MAXIMIZE_APP_NON_RESIZABLE"]) + @Test + override fun maximizeAppWindow() = super.maximizeAppWindow() + + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP_NON_RESIZABLE) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppPortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppPortrait.kt new file mode 100644 index 000000000000..b79fd203fe1e --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppPortrait.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 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.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP +import com.android.wm.shell.scenarios.MaximizeAppWindow +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Maximize app window by pressing the maximize button on the app header. + * + * Assert that the app window keeps the same increases in size, filling the vertical and horizontal + * stable display bounds. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class MaximizeAppPortrait : MaximizeAppWindow() { + @ExpectedScenarios(["MAXIMIZE_APP"]) + @Test + override fun maximizeAppWindow() = super.maximizeAppWindow() + + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt new file mode 100644 index 000000000000..c3abf238dc0d --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2024 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.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.server.wm.flicker.helpers.MotionEventHelper.InputMethod +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.EDGE_RESIZE +import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class ResizeAppWithEdgeResizeMouse : ResizeAppWithEdgeResize(InputMethod.MOUSE) { + @ExpectedScenarios(["EDGE_RESIZE"]) + @Test + override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight() + + @ExpectedScenarios(["EDGE_RESIZE"]) + @Test + override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft() + + @ExpectedScenarios(["EDGE_RESIZE"]) + @Test + override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop() + + @ExpectedScenarios(["EDGE_RESIZE"]) + @Test + override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(EDGE_RESIZE) + } +} diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt new file mode 100644 index 000000000000..86b0e6f17b24 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2024 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.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.server.wm.flicker.helpers.MotionEventHelper.InputMethod +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.EDGE_RESIZE +import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class ResizeAppWithEdgeResizeStylus : ResizeAppWithEdgeResize(InputMethod.STYLUS) { + @ExpectedScenarios(["EDGE_RESIZE"]) + @Test + override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight() + + @ExpectedScenarios(["EDGE_RESIZE"]) + @Test + override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft() + + @ExpectedScenarios(["EDGE_RESIZE"]) + @Test + override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop() + + @ExpectedScenarios(["EDGE_RESIZE"]) + @Test + override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(EDGE_RESIZE) + } +} diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt new file mode 100644 index 000000000000..e6bb9eff6715 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2024 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.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.server.wm.flicker.helpers.MotionEventHelper.InputMethod +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.EDGE_RESIZE +import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class ResizeAppWithEdgeResizeTouchpad : ResizeAppWithEdgeResize(InputMethod.TOUCHPAD) { + @ExpectedScenarios(["EDGE_RESIZE"]) + @Test + override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight() + + @ExpectedScenarios(["EDGE_RESIZE"]) + @Test + override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft() + + @ExpectedScenarios(["EDGE_RESIZE"]) + @Test + override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop() + + @ExpectedScenarios(["EDGE_RESIZE"]) + @Test + override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(EDGE_RESIZE) + } +} diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithButton.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithButton.kt new file mode 100644 index 000000000000..b5090086f129 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithButton.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 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.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_LEFT_WITH_BUTTON +import com.android.wm.shell.scenarios.SnapResizeAppWindowWithButton +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Snap resize app window using the Snap Left button from the maximize menu. + * + * Assert that the app window fills the left half the display after being snap resized. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class SnapResizeAppWindowLeftWithButton : SnapResizeAppWindowWithButton(toLeft = true) { + @ExpectedScenarios(["SNAP_RESIZE_LEFT_WITH_BUTTON"]) + @Test + override fun snapResizeAppWindowWithButton() = super.snapResizeAppWindowWithButton() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_LEFT_WITH_BUTTON) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithDrag.kt new file mode 100644 index 000000000000..a22e7603bf0f --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithDrag.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 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.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_LEFT_WITH_DRAG +import com.android.wm.shell.scenarios.SnapResizeAppWindowWithDrag +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Snap resize app window by dragging it to the left edge of the screen. + * + * Assert that the app window fills the left half the display after being snap resized. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class SnapResizeAppWindowLeftWithDrag : SnapResizeAppWindowWithDrag(toLeft = true) { + @ExpectedScenarios(["SNAP_RESIZE_LEFT_WITH_DRAG"]) + @Test + override fun snapResizeAppWindowWithDrag() = super.snapResizeAppWindowWithDrag() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_LEFT_WITH_DRAG) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithButton.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithButton.kt new file mode 100644 index 000000000000..375a2b8a61ac --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithButton.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 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.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_RIGHT_WITH_BUTTON +import com.android.wm.shell.scenarios.SnapResizeAppWindowWithButton +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Snap resize app window using the Snap Right button from the maximize menu. + * + * Assert that the app window fills the right half the display after being snap resized. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class SnapResizeAppWindowRightWithButton : SnapResizeAppWindowWithButton(toLeft = false) { + @ExpectedScenarios(["SNAP_RESIZE_RIGHT_WITH_BUTTON"]) + @Test + override fun snapResizeAppWindowWithButton() = super.snapResizeAppWindowWithButton() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_RIGHT_WITH_BUTTON) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithDrag.kt new file mode 100644 index 000000000000..4a9daf7e2ea1 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithDrag.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 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.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_RIGHT_WITH_DRAG +import com.android.wm.shell.scenarios.SnapResizeAppWindowWithDrag +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Snap resize app window by dragging it to the right edge of the screen. + * + * Assert that the app window fills the right half the display after being snap resized. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class SnapResizeAppWindowRightWithDrag : SnapResizeAppWindowWithDrag(toLeft = false) { + @ExpectedScenarios(["SNAP_RESIZE_RIGHT_WITH_DRAG"]) + @Test + override fun snapResizeAppWindowWithDrag() = super.snapResizeAppWindowWithDrag() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_RIGHT_WITH_DRAG) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeNonResizableAppWindowLeftWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeNonResizableAppWindowLeftWithDrag.kt new file mode 100644 index 000000000000..582658f8cb11 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeNonResizableAppWindowLeftWithDrag.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 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.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE +import com.android.wm.shell.scenarios.SnapResizeAppWindowWithDrag +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Snap resize non-resizable app window by dragging it to the left edge of the screen. + * + * Assert that the app window keeps the same size and returns to its original pre-drag position. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class SnapResizeNonResizableAppWindowLeftWithDrag : + SnapResizeAppWindowWithDrag(toLeft = true, isResizable = false) { + @ExpectedScenarios(["SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE"]) + @Test + override fun snapResizeAppWindowWithDrag() = super.snapResizeAppWindowWithDrag() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT) + .use(SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeNonResizableAppWindowRightWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeNonResizableAppWindowRightWithDrag.kt new file mode 100644 index 000000000000..7205ec412c99 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeNonResizableAppWindowRightWithDrag.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 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.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE +import com.android.wm.shell.scenarios.SnapResizeAppWindowWithDrag +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Snap resize non-resizable app window by dragging it to the right edge of the screen. + * + * Assert that the app window keeps the same size and returns to its original pre-drag position. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class SnapResizeNonResizableAppWindowRightWithDrag : + SnapResizeAppWindowWithDrag(toLeft = false, isResizable = false) { + @ExpectedScenarios(["SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE"]) + @Test + override fun snapResizeAppWindowWithDrag() = super.snapResizeAppWindowWithDrag() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT) + .use(SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DesktopScenarioCustomAppTestBase.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DesktopScenarioCustomAppTestBase.kt new file mode 100644 index 000000000000..5a69b27d9fbb --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DesktopScenarioCustomAppTestBase.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2024 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.scenarios + +import android.app.Instrumentation +import android.tools.traces.parsers.WindowManagerStateHelper +import android.tools.traces.parsers.toFlickerComponent +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import com.android.launcher3.tapl.LauncherInstrumentation +import com.android.server.wm.flicker.helpers.DesktopModeAppHelper +import com.android.server.wm.flicker.helpers.LetterboxAppHelper +import com.android.server.wm.flicker.helpers.NonResizeableAppHelper +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.server.wm.flicker.testapp.ActivityOptions +import org.junit.Ignore + +/** Base test class for desktop CUJ with customizable test app. */ +@Ignore("Base Test Class") +abstract class DesktopScenarioCustomAppTestBase( + isResizeable: Boolean = true, + isLandscapeApp: Boolean = true +) { + val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + val tapl = LauncherInstrumentation() + val wmHelper = WindowManagerStateHelper(instrumentation) + val device = UiDevice.getInstance(instrumentation) + // TODO(b/363181411): Consolidate in LetterboxAppHelper. + val testApp = when { + isResizeable && isLandscapeApp -> SimpleAppHelper(instrumentation) + isResizeable && !isLandscapeApp -> SimpleAppHelper( + instrumentation, + launcherName = ActivityOptions.PortraitOnlyActivity.LABEL, + component = ActivityOptions.PortraitOnlyActivity.COMPONENT.toFlickerComponent() + ) + // NonResizeablAppHelper has no fixed orientation. + !isResizeable && isLandscapeApp -> NonResizeableAppHelper(instrumentation) + // Opens NonResizeablePortraitActivity. + else -> LetterboxAppHelper(instrumentation) + }.let { DesktopModeAppHelper(it) } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindowAndPip.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindowAndPip.kt new file mode 100644 index 000000000000..6d52a11153d9 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindowAndPip.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 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.scenarios + +import android.platform.test.annotations.Postsubmit +import com.android.server.wm.flicker.helpers.DesktopModeAppHelper +import com.android.server.wm.flicker.helpers.ImeAppHelper +import com.android.server.wm.flicker.helpers.MailAppHelper +import com.android.server.wm.flicker.helpers.NewTasksAppHelper +import com.android.server.wm.flicker.helpers.PipAppHelper +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.window.flags.Flags +import org.junit.After +import org.junit.Assume +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +open class DragAppWindowMultiWindowAndPip : DragAppWindowScenarioTestBase() +{ + private val imeAppHelper = ImeAppHelper(instrumentation) + private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation)) + private val pipApp = PipAppHelper(instrumentation) + private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation)) + private val newTasksApp = DesktopModeAppHelper(NewTasksAppHelper(instrumentation)) + private val imeApp = DesktopModeAppHelper(ImeAppHelper(instrumentation)) + + @Before + fun setup() { + Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet) + // Set string extra to ensure the app is on PiP mode at launch + pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = mapOf("enter_pip" to "true")) + testApp.enterDesktopWithDrag(wmHelper, device) + mailApp.launchViaIntent(wmHelper) + newTasksApp.launchViaIntent(wmHelper) + imeApp.launchViaIntent(wmHelper) + } + + @Test + override fun dragAppWindow() { + val (startXIme, startYIme) = getWindowDragStartCoordinate(imeAppHelper) + + imeApp.dragWindow(startXIme, startYIme, + endX = startXIme + 150, endY = startYIme + 150, + wmHelper, device) + } + + @After + fun teardown() { + testApp.exit(wmHelper) + pipApp.exit(wmHelper) + mailApp.exit(wmHelper) + newTasksApp.exit(wmHelper) + imeApp.exit(wmHelper) + } +} diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt index 0f0d2df4337c..5f759e8ee682 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt @@ -17,15 +17,8 @@ package com.android.wm.shell.scenarios import android.platform.test.annotations.Postsubmit -import android.app.Instrumentation import android.tools.NavBar import android.tools.Rotation -import android.tools.traces.parsers.WindowManagerStateHelper -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.uiautomator.UiDevice -import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.server.wm.flicker.helpers.DesktopModeAppHelper -import com.android.server.wm.flicker.helpers.SimpleAppHelper import com.android.window.flags.Flags import com.android.wm.shell.Utils import org.junit.After @@ -40,13 +33,11 @@ import org.junit.runners.BlockJUnit4ClassRunner @Postsubmit open class EnterDesktopWithDrag @JvmOverloads -constructor(val rotation: Rotation = Rotation.ROTATION_0) { - - private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() - private val tapl = LauncherInstrumentation() - private val wmHelper = WindowManagerStateHelper(instrumentation) - private val device = UiDevice.getInstance(instrumentation) - private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation)) +constructor( + val rotation: Rotation = Rotation.ROTATION_0, + isResizeable: Boolean = true, + isLandscapeApp: Boolean = true +) : DesktopScenarioCustomAppTestBase(isResizeable, isLandscapeApp) { @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation) diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt index 533be8895fb9..b616e5347ac9 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt @@ -17,15 +17,8 @@ package com.android.wm.shell.scenarios import android.platform.test.annotations.Postsubmit -import android.app.Instrumentation import android.tools.NavBar import android.tools.Rotation -import android.tools.traces.parsers.WindowManagerStateHelper -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.uiautomator.UiDevice -import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.server.wm.flicker.helpers.DesktopModeAppHelper -import com.android.server.wm.flicker.helpers.SimpleAppHelper import com.android.window.flags.Flags import com.android.wm.shell.Utils import org.junit.After @@ -40,13 +33,11 @@ import org.junit.runners.BlockJUnit4ClassRunner @Postsubmit open class ExitDesktopWithDragToTopDragZone @JvmOverloads -constructor(val rotation: Rotation = Rotation.ROTATION_0) { - - private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() - private val tapl = LauncherInstrumentation() - private val wmHelper = WindowManagerStateHelper(instrumentation) - private val device = UiDevice.getInstance(instrumentation) - private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation)) +constructor( + val rotation: Rotation = Rotation.ROTATION_0, + isResizeable: Boolean = true, + isLandscapeApp: Boolean = true +) : DesktopScenarioCustomAppTestBase(isResizeable, isLandscapeApp) { @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation) diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt index e3660fe13bc2..426f40b5e81b 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt @@ -16,15 +16,17 @@ package com.android.wm.shell.scenarios -import android.platform.test.annotations.Postsubmit import android.app.Instrumentation +import android.platform.test.annotations.Postsubmit import android.tools.NavBar import android.tools.Rotation +import android.tools.flicker.rules.ChangeDisplayOrientationRule import android.tools.traces.parsers.WindowManagerStateHelper import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation import com.android.server.wm.flicker.helpers.DesktopModeAppHelper +import com.android.server.wm.flicker.helpers.NonResizeableAppHelper import com.android.server.wm.flicker.helpers.SimpleAppHelper import com.android.window.flags.Flags import com.android.wm.shell.Utils @@ -35,22 +37,31 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.BlockJUnit4ClassRunner + @RunWith(BlockJUnit4ClassRunner::class) @Postsubmit open class MaximizeAppWindow -{ +@JvmOverloads +constructor(private val rotation: Rotation = Rotation.ROTATION_0, isResizable: Boolean = true) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() private val tapl = LauncherInstrumentation() private val wmHelper = WindowManagerStateHelper(instrumentation) private val device = UiDevice.getInstance(instrumentation) - private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation)) + private val testApp = if (isResizable) { + DesktopModeAppHelper(SimpleAppHelper(instrumentation)) + } else { + DesktopModeAppHelper(NonResizeableAppHelper(instrumentation)) + } - @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, - Rotation.ROTATION_0) + @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation) @Before fun setup() { Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet) + tapl.setEnableRotation(true) + tapl.setExpectedRotation(rotation.value) + ChangeDisplayOrientationRule.setRotation(rotation) testApp.enterDesktopWithDrag(wmHelper, device) } diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt index 5bc975bbd565..b6bca7a94287 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt @@ -83,5 +83,8 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0, @After fun teardown() { testApp.exit(wmHelper) + mailApp.exit(wmHelper) + newTasksApp.exit(wmHelper) + imeApp.exit(wmHelper) } } diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt new file mode 100644 index 000000000000..285ea13bb9d4 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2024 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.scenarios + +import android.platform.test.annotations.Postsubmit +import android.app.Instrumentation +import android.tools.NavBar +import android.tools.Rotation +import android.tools.traces.parsers.WindowManagerStateHelper +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import com.android.launcher3.tapl.LauncherInstrumentation +import com.android.server.wm.flicker.helpers.DesktopModeAppHelper +import com.android.server.wm.flicker.helpers.ImeAppHelper +import com.android.server.wm.flicker.helpers.MailAppHelper +import com.android.server.wm.flicker.helpers.NewTasksAppHelper +import com.android.server.wm.flicker.helpers.PipAppHelper +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.window.flags.Flags +import com.android.wm.shell.Utils +import org.junit.After +import org.junit.Assume +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +open class ResizeAppCornerMultiWindowAndPip +@JvmOverloads +constructor(val rotation: Rotation = Rotation.ROTATION_0, + val horizontalChange: Int = 50, + val verticalChange: Int = -50) { + + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val tapl = LauncherInstrumentation() + private val wmHelper = WindowManagerStateHelper(instrumentation) + private val device = UiDevice.getInstance(instrumentation) + private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation)) + private val pipApp = PipAppHelper(instrumentation) + private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation)) + private val newTasksApp = DesktopModeAppHelper(NewTasksAppHelper(instrumentation)) + private val imeApp = DesktopModeAppHelper(ImeAppHelper(instrumentation)) + + @Rule + @JvmField + val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation) + + @Before + fun setup() { + Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet) + tapl.setEnableRotation(true) + tapl.setExpectedRotation(rotation.value) + // Set string extra to ensure the app is on PiP mode at launch + pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = mapOf("enter_pip" to "true")) + testApp.enterDesktopWithDrag(wmHelper, device) + mailApp.launchViaIntent(wmHelper) + newTasksApp.launchViaIntent(wmHelper) + imeApp.launchViaIntent(wmHelper) + } + + @Test + open fun resizeAppWithCornerResize() { + imeApp.cornerResize(wmHelper, + device, + DesktopModeAppHelper.Corners.RIGHT_TOP, + horizontalChange, + verticalChange) + } + + @After + fun teardown() { + testApp.exit(wmHelper) + pipApp.exit(wmHelper) + mailApp.exit(wmHelper) + newTasksApp.exit(wmHelper) + imeApp.exit(wmHelper) + } +} diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt index 63e7387f7a8a..42940a99b59f 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt @@ -25,6 +25,7 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation import com.android.server.wm.flicker.helpers.DesktopModeAppHelper +import com.android.server.wm.flicker.helpers.NonResizeableAppHelper import com.android.server.wm.flicker.helpers.SimpleAppHelper import com.android.window.flags.Flags import com.android.wm.shell.Utils @@ -40,15 +41,24 @@ import org.junit.runners.BlockJUnit4ClassRunner @Postsubmit open class ResizeAppWithCornerResize @JvmOverloads -constructor(val rotation: Rotation = Rotation.ROTATION_0, - val horizontalChange: Int = 50, - val verticalChange: Int = -50) { +constructor( + val rotation: Rotation = Rotation.ROTATION_0, + val horizontalChange: Int = 200, + val verticalChange: Int = -200, + val appProperty: AppProperty = AppProperty.STANDARD +) { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() private val tapl = LauncherInstrumentation() private val wmHelper = WindowManagerStateHelper(instrumentation) private val device = UiDevice.getInstance(instrumentation) - private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation)) + private val testApp = + DesktopModeAppHelper( + when (appProperty) { + AppProperty.STANDARD -> SimpleAppHelper(instrumentation) + AppProperty.NON_RESIZABLE -> NonResizeableAppHelper(instrumentation) + } + ) @Rule @JvmField @@ -64,15 +74,24 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0, @Test open fun resizeAppWithCornerResize() { - testApp.cornerResize(wmHelper, + testApp.cornerResize( + wmHelper, device, DesktopModeAppHelper.Corners.RIGHT_TOP, horizontalChange, - verticalChange) + verticalChange + ) } @After fun teardown() { testApp.exit(wmHelper) } + + companion object { + enum class AppProperty { + STANDARD, + NON_RESIZABLE + } + } } diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt new file mode 100644 index 000000000000..d094967e91e0 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2024 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.scenarios + +import android.platform.test.annotations.Postsubmit +import android.app.Instrumentation +import android.tools.NavBar +import android.tools.Rotation +import android.tools.traces.parsers.WindowManagerStateHelper +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import com.android.launcher3.tapl.LauncherInstrumentation +import com.android.server.wm.flicker.helpers.DesktopModeAppHelper +import com.android.server.wm.flicker.helpers.MotionEventHelper +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.window.flags.Flags +import com.android.wm.shell.Utils +import org.junit.After +import org.junit.Assume +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +open class ResizeAppWithEdgeResize +@JvmOverloads +constructor( + val inputMethod: MotionEventHelper.InputMethod, + val rotation: Rotation = Rotation.ROTATION_90 +) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val tapl = LauncherInstrumentation() + private val wmHelper = WindowManagerStateHelper(instrumentation) + private val device = UiDevice.getInstance(instrumentation) + private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation)) + private val motionEventHelper = MotionEventHelper(instrumentation, inputMethod) + + @Rule + @JvmField + val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation) + + @Before + fun setup() { + Assume.assumeTrue( + Flags.enableDesktopWindowingMode() + && Flags.enableWindowingEdgeDragResize() && tapl.isTablet + ) + tapl.setEnableRotation(true) + tapl.setExpectedRotation(rotation.value) + testApp.enterDesktopWithDrag(wmHelper, device) + } + + @Test + open fun resizeAppWithEdgeResizeRight() { + testApp.edgeResize( + wmHelper, + motionEventHelper, + DesktopModeAppHelper.Edges.RIGHT + ) + } + + @Test + open fun resizeAppWithEdgeResizeLeft() { + testApp.edgeResize( + wmHelper, + motionEventHelper, + DesktopModeAppHelper.Edges.LEFT + ) + } + + @Test + open fun resizeAppWithEdgeResizeTop() { + testApp.edgeResize( + wmHelper, + motionEventHelper, + DesktopModeAppHelper.Edges.TOP + ) + } + + @Test + open fun resizeAppWithEdgeResizeBottom() { + testApp.edgeResize( + wmHelper, + motionEventHelper, + DesktopModeAppHelper.Edges.BOTTOM + ) + } + + @After + fun teardown() { + testApp.exit(wmHelper) + } +} diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt new file mode 100644 index 000000000000..33242db66f9f --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2024 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.scenarios + +import android.app.Instrumentation +import android.platform.test.annotations.Postsubmit +import android.tools.NavBar +import android.tools.Rotation +import android.tools.traces.parsers.WindowManagerStateHelper +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import com.android.launcher3.tapl.LauncherInstrumentation +import com.android.server.wm.flicker.helpers.DesktopModeAppHelper +import com.android.server.wm.flicker.helpers.NonResizeableAppHelper +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.window.flags.Flags +import com.android.wm.shell.Utils +import org.junit.After +import org.junit.Assume +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +open class SnapResizeAppWindowWithButton +@JvmOverloads +constructor(private val toLeft: Boolean = true, isResizable: Boolean = true) { + + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val tapl = LauncherInstrumentation() + private val wmHelper = WindowManagerStateHelper(instrumentation) + private val device = UiDevice.getInstance(instrumentation) + private val testApp = if (isResizable) { + DesktopModeAppHelper(SimpleAppHelper(instrumentation)) + } else { + DesktopModeAppHelper(NonResizeableAppHelper(instrumentation)) + } + + @Rule + @JvmField + val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0) + + @Before + fun setup() { + Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet) + testApp.enterDesktopWithDrag(wmHelper, device) + } + + @Test + open fun snapResizeAppWindowWithButton() { + testApp.snapResizeDesktopApp(wmHelper, device, instrumentation.context, toLeft) + } + + @After + fun teardown() { + testApp.exit(wmHelper) + } +} diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt new file mode 100644 index 000000000000..14eb779165bb --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2024 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.scenarios + +import android.app.Instrumentation +import android.platform.test.annotations.Postsubmit +import android.tools.NavBar +import android.tools.Rotation +import android.tools.traces.parsers.WindowManagerStateHelper +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import com.android.launcher3.tapl.LauncherInstrumentation +import com.android.server.wm.flicker.helpers.DesktopModeAppHelper +import com.android.server.wm.flicker.helpers.NonResizeableAppHelper +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.window.flags.Flags +import com.android.wm.shell.Utils +import org.junit.After +import org.junit.Assume +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +open class SnapResizeAppWindowWithDrag +@JvmOverloads +constructor(private val toLeft: Boolean = true, isResizable: Boolean = true) { + + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val tapl = LauncherInstrumentation() + private val wmHelper = WindowManagerStateHelper(instrumentation) + private val device = UiDevice.getInstance(instrumentation) + private val testApp = if (isResizable) { + DesktopModeAppHelper(SimpleAppHelper(instrumentation)) + } else { + DesktopModeAppHelper(NonResizeableAppHelper(instrumentation)) + } + + @Rule + @JvmField + val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0) + + @Before + fun setup() { + Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet) + testApp.enterDesktopWithDrag(wmHelper, device) + } + + @Test + open fun snapResizeAppWindowWithDrag() { + testApp.dragToSnapResizeRegion(wmHelper, device, toLeft) + } + + @After + fun teardown() { + testApp.exit(wmHelper) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt index dee67f3f2e0e..c0fafef96775 100644 --- a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt +++ b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt @@ -17,6 +17,7 @@ package com.android.wm.shell import android.app.Instrumentation +import android.platform.test.rule.EnsureDeviceSettingsRule import android.platform.test.rule.NavigationModeRule import android.platform.test.rule.PressHomeRule import android.platform.test.rule.UnlockScreenRule @@ -49,5 +50,6 @@ object Utils { ) ) .around(PressHomeRule()) + .around(EnsureDeviceSettingsRule()) } } diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp index 4abaf5bd4a38..7305f4988aa9 100644 --- a/libs/WindowManager/Shell/tests/flicker/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/Android.bp @@ -68,6 +68,7 @@ java_defaults { "flickerlib-helpers", "flickerlib-trace_processor_shell", "platform-test-annotations", + "platform-test-rules", "wm-flicker-common-app-helpers", "wm-flicker-common-assertions", "launcher-helper-lib", diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/OWNERS b/libs/WindowManager/Shell/tests/flicker/appcompat/OWNERS new file mode 100644 index 000000000000..a36a4f85fa4e --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/OWNERS @@ -0,0 +1,2 @@ +# Window Manager > App Compat +# Bug component: 970984
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt index b85d7936efc2..a9ed13a099f3 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt @@ -16,10 +16,14 @@ package com.android.wm.shell.flicker.pip +import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.flicker.junit.FlickerParametersRunnerFactory import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest +import android.tools.flicker.subject.exceptions.ExceptionMessageBuilder +import android.tools.flicker.subject.exceptions.IncorrectRegionException +import android.tools.flicker.subject.layers.LayerSubject import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.wm.shell.flicker.pip.common.EnterPipTransition @@ -29,6 +33,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized +import kotlin.math.abs /** * Test entering pip from an app via auto-enter property when navigating to home. @@ -67,9 +72,24 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : EnterPipTran override val defaultTeardown: FlickerBuilder.() -> Unit = { teardown { pipApp.exit(wmHelper) } } - @FlakyTest(bugId = 293133362) + private val widthNotSmallerThan: LayerSubject.(LayerSubject) -> Unit = { + val width = visibleRegion.region.bounds.width() + val otherWidth = it.visibleRegion.region.bounds.width() + if (width < otherWidth && abs(width - otherWidth) > EPSILON) { + val errorMsgBuilder = + ExceptionMessageBuilder() + .forSubject(this) + .forIncorrectRegion("width. $width smaller than $otherWidth") + .setExpected(width) + .setActual(otherWidth) + throw IncorrectRegionException(errorMsgBuilder) + } + } + + @Postsubmit @Test override fun pipLayerReduces() { + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) flicker.assertLayers { val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible } pipLayerList.zipWithNext { previous, current -> @@ -78,6 +98,32 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : EnterPipTran } } + /** Checks that [pipApp] window's width is first decreasing then increasing. */ + @Postsubmit + @Test + fun pipLayerWidthDecreasesThenIncreases() { + Assume.assumeTrue(flicker.scenario.isGesturalNavigation) + flicker.assertLayers { + val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible } + var previousLayer = pipLayerList[0] + var currentLayer = previousLayer + var i = 0 + invoke("layer area is decreasing") { + if (i < pipLayerList.size - 1) { + previousLayer = currentLayer + currentLayer = pipLayerList[++i] + previousLayer.widthNotSmallerThan(currentLayer) + } + }.then().invoke("layer are is increasing", true /* isOptional */) { + if (i < pipLayerList.size - 1) { + previousLayer = currentLayer + currentLayer = pipLayerList[++i] + currentLayer.widthNotSmallerThan(previousLayer) + } + } + } + } + /** Checks that [pipApp] window is animated towards default position in right bottom corner */ @FlakyTest(bugId = 255578530) @Test @@ -108,4 +154,9 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : EnterPipTran override fun visibleLayersShownMoreThanOneConsecutiveEntry() { super.visibleLayersShownMoreThanOneConsecutiveEntry() } + + companion object { + // TODO(b/363080056): A margin of error allowed on certain layer size calculations. + const val EPSILON = 1 + } } diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt index 70be58f06548..429774f890a5 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt @@ -35,7 +35,7 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) class PipAspectRatioChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { - transitions { pipApp.changeAspectRatio() } + transitions { pipApp.changeAspectRatio(wmHelper) } } @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt index 90b9798c6329..cbd4a528474a 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt @@ -181,6 +181,13 @@ class PipDragThenSnapTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { super.taskBarWindowIsAlwaysVisible() } + // Overridden to remove @Postsubmit annotation + @Test + @FlakyTest(bugId = 294993100) + override fun pipLayerHasCorrectCornersAtEnd() { + // No rounded corners as we go back to fullscreen in new orientation. + } + companion object { /** * Creates the test configurations. diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt index ed2a0a718c6c..578a9b536289 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt @@ -147,6 +147,12 @@ open class SetRequestedOrientationWhilePinned(flicker: LegacyFlickerTest) : PipT @Test override fun entireScreenCovered() = super.entireScreenCovered() + @Postsubmit + @Test + override fun pipLayerHasCorrectCornersAtEnd() { + flicker.assertLayersEnd { hasNoRoundedCorners(pipApp) } + } + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt index 8cb81b46cf4d..f57335c2081b 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt @@ -70,6 +70,11 @@ abstract class ClosePipTransition(flicker: LegacyFlickerTest) : PipTransition(fl } } + @Test + override fun pipLayerHasCorrectCornersAtEnd() { + // PiP might have completely faded out by this point, so corner radii not applicable. + } + companion object { /** * Creates the test configurations. diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt index 0742cf9c5887..ce84eb644042 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.pip.common +import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.Rotation import android.tools.flicker.legacy.LegacyFlickerTest @@ -123,6 +124,12 @@ abstract class ExitPipToAppTransition(flicker: LegacyFlickerTest) : PipTransitio } } + @Postsubmit + @Test + override fun pipLayerHasCorrectCornersAtEnd() { + flicker.assertLayersEnd { hasNoRoundedCorners(pipApp) } + } + /** {@inheritDoc} */ @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt index 99c1ad2aaa4e..bc2bfdbe1df1 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.pip.common import android.app.Instrumentation import android.content.Intent +import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.tools.Rotation import android.tools.flicker.legacy.FlickerBuilder @@ -105,4 +106,10 @@ abstract class PipTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) { .doesNotContain(false) } } + + @Postsubmit + @Test + open fun pipLayerHasCorrectCornersAtEnd() { + flicker.assertLayersEnd { hasRoundedCorners(pipApp) } + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java index 499870220190..f31722d3c1a5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java @@ -19,6 +19,7 @@ package com.android.wm.shell; import com.android.wm.shell.common.ShellExecutor; import java.util.ArrayList; +import java.util.List; /** * Really basic test executor. It just gathers all events in a blob. The only option is to @@ -52,4 +53,9 @@ public class TestShellExecutor implements ShellExecutor { mRunnables.remove(0).run(); } } + + /** Returns the list of callbacks for this executor. */ + public List<Runnable> getCallbacks() { + return mRunnables; + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index 90e3f7fdb973..1e4b8b62a082 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -881,7 +881,7 @@ public class BackAnimationControllerTest extends ShellTestCase { RemoteAnimationTarget[] targets = new RemoteAnimationTarget[]{animationTarget}; if (mController.mBackAnimationAdapter != null) { mController.mBackAnimationAdapter.getRunner().onAnimationStart( - targets, null, null, mBackAnimationFinishedCallback); + targets, null /* prepareOpenTransition */, mBackAnimationFinishedCallback); mShellExecutor.flushAll(); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java index 859602ec709f..6fa37885b724 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java @@ -50,8 +50,8 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.bubbles.BubbleData.TimeSource; import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.bubbles.BubbleBarLocation; -import com.android.wm.shell.common.bubbles.BubbleBarUpdate; +import com.android.wm.shell.shared.bubbles.BubbleBarLocation; +import com.android.wm.shell.shared.bubbles.BubbleBarUpdate; import com.google.common.collect.ImmutableList; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java index 50c4a1828026..dca5fc4c2fe0 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java @@ -43,7 +43,7 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.R; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.bubbles.BubbleInfo; +import com.android.wm.shell.shared.bubbles.BubbleInfo; import org.junit.Before; import org.junit.Test; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt index 8035e917d5b4..4ac066e4ffb0 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt @@ -74,7 +74,6 @@ class BubbleViewInfoTest : ShellTestCase() { private lateinit var bubbleStackView: BubbleStackView private lateinit var bubbleBarLayerView: BubbleBarLayerView private lateinit var bubblePositioner: BubblePositioner - private lateinit var expandedViewManager: BubbleExpandedViewManager private val bubbleTaskViewFactory = BubbleTaskViewFactory { BubbleTaskView(mock<TaskView>(), mock<Executor>()) @@ -155,7 +154,6 @@ class BubbleViewInfoTest : ShellTestCase() { bubbleController, mainExecutor ) - expandedViewManager = BubbleExpandedViewManager.fromBubbleController(bubbleController) bubbleBarLayerView = BubbleBarLayerView(context, bubbleController, bubbleData) } @@ -165,7 +163,6 @@ class BubbleViewInfoTest : ShellTestCase() { val info = BubbleViewInfoTask.BubbleViewInfo.populate( context, - expandedViewManager, bubbleTaskViewFactory, bubblePositioner, bubbleStackView, @@ -193,9 +190,7 @@ class BubbleViewInfoTest : ShellTestCase() { val info = BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar( context, - expandedViewManager, bubbleTaskViewFactory, - bubblePositioner, bubbleBarLayerView, iconFactory, bubble, @@ -229,9 +224,7 @@ class BubbleViewInfoTest : ShellTestCase() { val info = BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar( context, - expandedViewManager, bubbleTaskViewFactory, - bubblePositioner, bubbleBarLayerView, iconFactory, bubble, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java index b39cf19a155a..d5287e742c2c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java @@ -35,9 +35,12 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.TaskInfo; import android.content.Context; import android.content.res.Configuration; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.platform.test.flag.junit.SetFlagsRule; import android.testing.AndroidTestingRunner; import android.view.InsetsSource; import android.view.InsetsState; @@ -90,6 +93,9 @@ public class CompatUIControllerTest extends ShellTestCase { public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private CompatUIController mController; private ShellInit mShellInit; @Mock @@ -122,7 +128,6 @@ public class CompatUIControllerTest extends ShellTestCase { private CompatUIConfiguration mCompatUIConfiguration; @Mock private CompatUIShellCommandHandler mCompatUIShellCommandHandler; - @Mock private AccessibilityManager mAccessibilityManager; @@ -132,6 +137,8 @@ public class CompatUIControllerTest extends ShellTestCase { @NonNull private CompatUIStatusManager mCompatUIStatusManager; + private boolean mInDesktopModePredicateResult; + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -157,7 +164,7 @@ public class CompatUIControllerTest extends ShellTestCase { mMockDisplayController, mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader, mCompatUIConfiguration, mCompatUIShellCommandHandler, mAccessibilityManager, - mCompatUIStatusManager) { + mCompatUIStatusManager, i -> mInDesktopModePredicateResult) { @Override CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) { @@ -685,6 +692,7 @@ public class CompatUIControllerTest extends ShellTestCase { } @Test + @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK) public void testLetterboxEduLayout_notCreatedWhenLetterboxEducationIsDisabled() { TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true); taskInfo.appCompatTaskInfo.setLetterboxEducationEnabled(false); @@ -695,6 +703,34 @@ public class CompatUIControllerTest extends ShellTestCase { eq(mMockTaskListener)); } + @Test + @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK) + @EnableFlags(Flags.FLAG_SKIP_COMPAT_UI_EDUCATION_IN_DESKTOP_MODE) + public void testUpdateActiveTaskInfo_removeAllComponentWhenInDesktopModeFlagEnabled() { + mInDesktopModePredicateResult = false; + TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true); + mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener)); + verify(mController, never()).removeLayouts(taskInfo.taskId); + + mInDesktopModePredicateResult = true; + mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener)); + verify(mController).removeLayouts(taskInfo.taskId); + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK) + @DisableFlags(Flags.FLAG_SKIP_COMPAT_UI_EDUCATION_IN_DESKTOP_MODE) + public void testUpdateActiveTaskInfo_removeAllComponentWhenInDesktopModeFlagDisabled() { + mInDesktopModePredicateResult = false; + TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true); + mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener)); + verify(mController, never()).removeLayouts(taskInfo.taskId); + + mInDesktopModePredicateResult = true; + mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener)); + verify(mController, never()).removeLayouts(taskInfo.taskId); + } + private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat) { return createTaskInfo(displayId, taskId, hasSizeCompat, /* isVisible */ false, /* isFocused */ false, /* isTopActivityTransparent */ false); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt new file mode 100644 index 000000000000..b14f1633e8fd --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2024 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.desktopmode + +import android.app.ActivityManager.RunningTaskInfo +import android.content.pm.ActivityInfo +import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE +import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT +import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT +import android.graphics.Rect +import android.os.Binder +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule +import android.testing.AndroidTestingRunner +import android.view.Display.DEFAULT_DISPLAY +import android.window.WindowContainerTransaction +import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn +import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession +import com.android.dx.mockito.inline.extended.ExtendedMockito.never +import com.android.dx.mockito.inline.extended.StaticMockitoSession +import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE +import com.android.window.flags.Flags.FLAG_RESPECT_ORIENTATION_CHANGE_FOR_UNRESIZEABLE +import com.android.wm.shell.ShellTaskOrganizer +import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.common.TaskStackListenerImpl +import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.transition.Transitions +import junit.framework.Assert.assertEquals +import junit.framework.Assert.assertTrue +import kotlin.test.assertNotNull +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.isNull +import org.mockito.Mock +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.atLeastOnce +import org.mockito.kotlin.capture +import org.mockito.kotlin.eq +import org.mockito.kotlin.whenever +import org.mockito.quality.Strictness + +/** + * Test class for {@link DesktopActivityOrientationChangeHandler} + * + * Usage: atest WMShellUnitTests:DesktopActivityOrientationChangeHandlerTest + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_RESPECT_ORIENTATION_CHANGE_FOR_UNRESIZEABLE) +class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() { + @JvmField @Rule val setFlagsRule = SetFlagsRule() + + @Mock lateinit var testExecutor: ShellExecutor + @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer + @Mock lateinit var transitions: Transitions + @Mock lateinit var resizeTransitionHandler: ToggleResizeDesktopTaskTransitionHandler + @Mock lateinit var taskStackListener: TaskStackListenerImpl + + private lateinit var mockitoSession: StaticMockitoSession + private lateinit var handler: DesktopActivityOrientationChangeHandler + private lateinit var shellInit: ShellInit + private lateinit var taskRepository: DesktopModeTaskRepository + // Mock running tasks are registered here so we can get the list from mock shell task organizer. + private val runningTasks = mutableListOf<RunningTaskInfo>() + + @Before + fun setUp() { + mockitoSession = + mockitoSession() + .strictness(Strictness.LENIENT) + .spyStatic(DesktopModeStatus::class.java) + .startMocking() + doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) } + + shellInit = spy(ShellInit(testExecutor)) + taskRepository = DesktopModeTaskRepository() + whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks } + whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() } + + handler = DesktopActivityOrientationChangeHandler(context, shellInit, shellTaskOrganizer, + taskStackListener, resizeTransitionHandler, taskRepository) + + shellInit.init() + } + + @After + fun tearDown() { + mockitoSession.finishMocking() + + runningTasks.clear() + } + + @Test + fun instantiate_addInitCallback() { + verify(shellInit).addInitCallback(any(), any<DesktopActivityOrientationChangeHandler>()) + } + + @Test + fun instantiate_cannotEnterDesktopMode_doNotAddInitCallback() { + whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false) + clearInvocations(shellInit) + + handler = DesktopActivityOrientationChangeHandler(context, shellInit, shellTaskOrganizer, + taskStackListener, resizeTransitionHandler, taskRepository) + + verify(shellInit, never()).addInitCallback(any(), + any<DesktopActivityOrientationChangeHandler>()) + } + + @Test + fun handleActivityOrientationChange_resizeable_doNothing() { + val task = setUpFreeformTask() + + taskStackListener.onActivityRequestedOrientationChanged(task.taskId, + SCREEN_ORIENTATION_LANDSCAPE) + + verify(resizeTransitionHandler, never()).startTransition(any(), any()) + } + + @Test + fun handleActivityOrientationChange_nonResizeableFullscreen_doNothing() { + val task = createFullscreenTask() + task.isResizeable = false + val activityInfo = ActivityInfo() + activityInfo.screenOrientation = SCREEN_ORIENTATION_PORTRAIT + task.topActivityInfo = activityInfo + whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + taskRepository.addActiveTask(DEFAULT_DISPLAY, task.taskId) + taskRepository.updateTaskVisibility(DEFAULT_DISPLAY, task.taskId, visible = true) + runningTasks.add(task) + + taskStackListener.onActivityRequestedOrientationChanged(task.taskId, + SCREEN_ORIENTATION_LANDSCAPE) + + verify(resizeTransitionHandler, never()).startTransition(any(), any()) + } + + @Test + fun handleActivityOrientationChange_nonResizeablePortrait_requestSameOrientation_doNothing() { + val task = setUpFreeformTask(isResizeable = false) + val newTask = setUpFreeformTask(isResizeable = false, + orientation = SCREEN_ORIENTATION_SENSOR_PORTRAIT) + + handler.handleActivityOrientationChange(task, newTask) + + verify(resizeTransitionHandler, never()).startTransition(any(), any()) + } + + @Test + fun handleActivityOrientationChange_notInDesktopMode_doNothing() { + val task = setUpFreeformTask(isResizeable = false) + taskRepository.updateTaskVisibility(task.displayId, task.taskId, visible = false) + + taskStackListener.onActivityRequestedOrientationChanged(task.taskId, + SCREEN_ORIENTATION_LANDSCAPE) + + verify(resizeTransitionHandler, never()).startTransition(any(), any()) + } + + @Test + fun handleActivityOrientationChange_nonResizeablePortrait_respectLandscapeRequest() { + val task = setUpFreeformTask(isResizeable = false) + val oldBounds = task.configuration.windowConfiguration.bounds + val newTask = setUpFreeformTask(isResizeable = false, + orientation = SCREEN_ORIENTATION_LANDSCAPE) + + handler.handleActivityOrientationChange(task, newTask) + + val wct = getLatestResizeDesktopTaskWct() + val finalBounds = findBoundsChange(wct, newTask) + assertNotNull(finalBounds) + val finalWidth = finalBounds.width() + val finalHeight = finalBounds.height() + // Bounds is landscape. + assertTrue(finalWidth > finalHeight) + // Aspect ratio remains the same. + assertEquals(oldBounds.height() / oldBounds.width(), finalWidth / finalHeight) + // Anchor point for resizing is at the center. + assertEquals(oldBounds.centerX(), finalBounds.centerX()) + } + + @Test + fun handleActivityOrientationChange_nonResizeableLandscape_respectPortraitRequest() { + val oldBounds = Rect(0, 0, 500, 200) + val task = setUpFreeformTask( + isResizeable = false, orientation = SCREEN_ORIENTATION_LANDSCAPE, bounds = oldBounds + ) + val newTask = setUpFreeformTask(isResizeable = false, bounds = oldBounds) + + handler.handleActivityOrientationChange(task, newTask) + + val wct = getLatestResizeDesktopTaskWct() + val finalBounds = findBoundsChange(wct, newTask) + assertNotNull(finalBounds) + val finalWidth = finalBounds.width() + val finalHeight = finalBounds.height() + // Bounds is portrait. + assertTrue(finalHeight > finalWidth) + // Aspect ratio remains the same. + assertEquals(oldBounds.width() / oldBounds.height(), finalHeight / finalWidth) + // Anchor point for resizing is at the center. + assertEquals(oldBounds.centerX(), finalBounds.centerX()) + } + + private fun setUpFreeformTask( + displayId: Int = DEFAULT_DISPLAY, + isResizeable: Boolean = true, + orientation: Int = SCREEN_ORIENTATION_PORTRAIT, + bounds: Rect? = Rect(0, 0, 200, 500) + ): RunningTaskInfo { + val task = createFreeformTask(displayId, bounds) + val activityInfo = ActivityInfo() + activityInfo.screenOrientation = orientation + task.topActivityInfo = activityInfo + task.isResizeable = isResizeable + whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) + taskRepository.addActiveTask(displayId, task.taskId) + taskRepository.updateTaskVisibility(displayId, task.taskId, visible = true) + taskRepository.addOrMoveFreeformTaskToTop(displayId, task.taskId) + runningTasks.add(task) + return task + } + + private fun getLatestResizeDesktopTaskWct( + currentBounds: Rect? = null + ): WindowContainerTransaction { + val arg: ArgumentCaptor<WindowContainerTransaction> = + ArgumentCaptor.forClass(WindowContainerTransaction::class.java) + verify(resizeTransitionHandler, atLeastOnce()) + .startTransition(capture(arg), eq(currentBounds)) + return arg.value + } + + private fun findBoundsChange(wct: WindowContainerTransaction, task: RunningTaskInfo): Rect? = + wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt index 70b366110692..ca972296e8d4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt @@ -104,7 +104,9 @@ class DesktopModeEventLoggerTest { /* session_id */ eq(SESSION_ID), eq(UNSET_MINIMIZE_REASON), - eq(UNSET_UNMINIMIZE_REASON)) + eq(UNSET_UNMINIMIZE_REASON), + /* visible_task_count */ + eq(TASK_COUNT)) } } @@ -131,7 +133,9 @@ class DesktopModeEventLoggerTest { /* session_id */ eq(SESSION_ID), eq(UNSET_MINIMIZE_REASON), - eq(UNSET_UNMINIMIZE_REASON)) + eq(UNSET_UNMINIMIZE_REASON), + /* visible_task_count */ + eq(TASK_COUNT)) } } @@ -159,7 +163,9 @@ class DesktopModeEventLoggerTest { /* session_id */ eq(SESSION_ID), eq(UNSET_MINIMIZE_REASON), - eq(UNSET_UNMINIMIZE_REASON)) + eq(UNSET_UNMINIMIZE_REASON), + /* visible_task_count */ + eq(TASK_COUNT)) } } @@ -190,7 +196,9 @@ class DesktopModeEventLoggerTest { /* minimize_reason */ eq(MinimizeReason.TASK_LIMIT.reason), /* unminimize_reason */ - eq(UNSET_UNMINIMIZE_REASON)) + eq(UNSET_UNMINIMIZE_REASON), + /* visible_task_count */ + eq(TASK_COUNT)) } } @@ -221,7 +229,9 @@ class DesktopModeEventLoggerTest { /* minimize_reason */ eq(UNSET_MINIMIZE_REASON), /* unminimize_reason */ - eq(UnminimizeReason.TASKBAR_TAP.reason)) + eq(UnminimizeReason.TASKBAR_TAP.reason), + /* visible_task_count */ + eq(TASK_COUNT)) } } @@ -233,15 +243,17 @@ class DesktopModeEventLoggerTest { private const val TASK_Y = 0 private const val TASK_HEIGHT = 100 private const val TASK_WIDTH = 100 + private const val TASK_COUNT = 1 private val TASK_UPDATE = TaskUpdate( - TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y + TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, + visibleTaskCount = TASK_COUNT, ) private fun createTaskUpdate( minimizeReason: MinimizeReason? = null, unminimizeReason: UnminimizeReason? = null, ) = TaskUpdate(TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, minimizeReason, - unminimizeReason) + unminimizeReason, TASK_COUNT) } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt index d4596396840e..d399b20abb2a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt @@ -22,6 +22,8 @@ import android.content.Context import android.graphics.Point import android.graphics.Rect import android.os.IBinder +import android.os.SystemProperties +import android.os.Trace import android.testing.AndroidTestingRunner import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CHANGE @@ -38,6 +40,7 @@ import android.window.TransitionInfo import android.window.TransitionInfo.Change import android.window.WindowContainerToken import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.modules.utils.testing.ExtendedMockitoRule import com.android.wm.shell.ShellTestCase import com.android.wm.shell.common.ShellExecutor @@ -86,7 +89,11 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { @JvmField @Rule val extendedMockitoRule = - ExtendedMockitoRule.Builder(this).mockStatic(DesktopModeStatus::class.java).build()!! + ExtendedMockitoRule.Builder(this) + .mockStatic(DesktopModeStatus::class.java) + .mockStatic(SystemProperties::class.java) + .mockStatic(Trace::class.java) + .build()!! private val testExecutor = mock<ShellExecutor>() private val mockShellInit = mock<ShellInit>() @@ -138,7 +145,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) - verifyTaskAddedAndEnterLogging(EnterReason.APP_FREEFORM_INTENT, DEFAULT_TASK_UPDATE) + verifyTaskAddedAndEnterLogging(EnterReason.APP_FREEFORM_INTENT, + DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1)) } @Test @@ -152,7 +160,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) - verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_DRAG, DEFAULT_TASK_UPDATE) + verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_DRAG, + DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1)) } @Test @@ -165,7 +174,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) - verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_MENU_BUTTON, DEFAULT_TASK_UPDATE) + verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_MENU_BUTTON, + DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1)) } @Test @@ -178,7 +188,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) - verifyTaskAddedAndEnterLogging(EnterReason.APP_FROM_OVERVIEW, DEFAULT_TASK_UPDATE) + verifyTaskAddedAndEnterLogging(EnterReason.APP_FROM_OVERVIEW, + DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1)) } @Test @@ -191,7 +202,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) - verifyTaskAddedAndEnterLogging(EnterReason.KEYBOARD_SHORTCUT_ENTER, DEFAULT_TASK_UPDATE) + verifyTaskAddedAndEnterLogging(EnterReason.KEYBOARD_SHORTCUT_ENTER, + DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1)) } @Test @@ -201,7 +213,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) - verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE) + verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, + DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1)) } @Test @@ -231,7 +244,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) - verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE) + verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, + DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1)) } @Test @@ -261,7 +275,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) - verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE) + verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, + DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1)) } @Test @@ -291,7 +306,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) - verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE) + verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, + DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1)) } @Test @@ -324,7 +340,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) - verifyTaskAddedAndEnterLogging(EnterReason.APP_FROM_OVERVIEW, DEFAULT_TASK_UPDATE) + verifyTaskAddedAndEnterLogging(EnterReason.APP_FROM_OVERVIEW, + DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1)) } @Test @@ -335,7 +352,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) - verifyTaskAddedAndEnterLogging(EnterReason.UNKNOWN_ENTER, DEFAULT_TASK_UPDATE) + verifyTaskAddedAndEnterLogging(EnterReason.UNKNOWN_ENTER, + DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1)) } @Test @@ -345,7 +363,51 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) - verifyTaskAddedAndEnterLogging(EnterReason.SCREEN_ON, DEFAULT_TASK_UPDATE) + verifyTaskAddedAndEnterLogging(EnterReason.SCREEN_ON, + DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1)) + } + + @Test + fun transitBack_previousExitReasonScreenOff_logTaskAddedAndEnterReasonScreenOn() { + val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM) + // Previous Exit reason recorded as Screen Off + val sessionId = 1 + transitionObserver.addTaskInfosToCachedMap(freeformTask) + transitionObserver.setLoggerSessionId(sessionId) + callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build()) + verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) + // Enter desktop through back transition, this happens when user enters after dismissing + // keyguard + val change = createChange(TRANSIT_TO_FRONT, freeformTask) + val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_BACK, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + + verifyTaskAddedAndEnterLogging(EnterReason.SCREEN_ON, + DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1)) + } + + @Test + fun transitEndDragToDesktop_previousExitReasonScreenOff_logTaskAddedAndEnterReasonAppDrag() { + val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM) + // Previous Exit reason recorded as Screen Off + val sessionId = 1 + transitionObserver.addTaskInfosToCachedMap(freeformTask) + transitionObserver.setLoggerSessionId(sessionId) + callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build()) + verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) + + // Enter desktop through app handle drag. This represents cases where instead of moving to + // desktop right after turning the screen on, we move to fullscreen then move another task + // to desktop + val transitionInfo = + TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0) + .addChange(createChange(TRANSIT_TO_FRONT, freeformTask)) + .build() + callOnTransitionReady(transitionInfo) + + verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_DRAG, + DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1)) } @Test @@ -481,7 +543,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build() callOnTransitionReady(transitionInfo2) - verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE) + verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, + DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1)) } @Test @@ -497,7 +560,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) verify(desktopModeEventLogger, times(1)) - .logTaskAdded(eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2))) + .logTaskAdded(eq(sessionId), + eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2))) verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) } @@ -519,7 +583,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { verify(desktopModeEventLogger, times(1)) .logTaskInfoChanged( - eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100))) + eq(sessionId), + eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1))) verifyZeroInteractions(desktopModeEventLogger) } @@ -548,7 +613,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { eq(sessionId), eq( DEFAULT_TASK_UPDATE.copy( - taskWidth = DEFAULT_TASK_WIDTH + 100, taskHeight = DEFAULT_TASK_HEIGHT - 100))) + taskWidth = DEFAULT_TASK_WIDTH + 100, taskHeight = DEFAULT_TASK_HEIGHT - 100, + visibleTaskCount = 1))) verifyZeroInteractions(desktopModeEventLogger) } @@ -572,7 +638,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { verify(desktopModeEventLogger, times(1)) .logTaskInfoChanged( - eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100))) + eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy( + taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2))) verifyZeroInteractions(desktopModeEventLogger) // task 2 resize @@ -596,7 +663,9 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { DEFAULT_TASK_UPDATE.copy( instanceId = 2, taskWidth = DEFAULT_TASK_WIDTH + 100, - taskHeight = DEFAULT_TASK_HEIGHT - 100))) + taskHeight = DEFAULT_TASK_HEIGHT - 100, + visibleTaskCount = 2)), + ) verifyZeroInteractions(desktopModeEventLogger) } @@ -614,7 +683,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) verify(desktopModeEventLogger, times(1)) - .logTaskRemoved(eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2))) + .logTaskRemoved(eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy( + instanceId = 2, visibleTaskCount = 1))) verify(desktopModeEventLogger, never()).logSessionExit(any(), any()) } @@ -632,6 +702,17 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { assertNotNull(sessionId) verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(sessionId!!), eq(enterReason)) verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), eq(taskUpdate)) + ExtendedMockito.verify { + Trace.setCounter( + eq(Trace.TRACE_TAG_WINDOW_MANAGER), + eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_NAME), + eq(taskUpdate.visibleTaskCount.toLong())) + } + ExtendedMockito.verify { + SystemProperties.set( + eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY), + eq(taskUpdate.visibleTaskCount.toString())) + } verifyZeroInteractions(desktopModeEventLogger) } @@ -653,6 +734,7 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { const val DEFAULT_TASK_WIDTH = 200 const val DEFAULT_TASK_X = 30 const val DEFAULT_TASK_Y = 70 + const val DEFAULT_VISIBLE_TASK_COUNT = 0 val DEFAULT_TASK_UPDATE = TaskUpdate( DEFAULT_TASK_ID, @@ -661,6 +743,7 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { DEFAULT_TASK_WIDTH, DEFAULT_TASK_X, DEFAULT_TASK_Y, + visibleTaskCount = DEFAULT_VISIBLE_TASK_COUNT, ) fun createTaskInfo( @@ -674,7 +757,7 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { ) = ActivityManager.RunningTaskInfo().apply { taskId = id - userId = uid + effectiveUid = uid configuration.windowConfiguration.apply { windowingMode = windowMode positionInParent = Point(taskX, taskY) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt index 518c00d377ad..db4e93de9541 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt @@ -18,11 +18,6 @@ package com.android.wm.shell.desktopmode import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest -import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON -import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.APP_FROM_OVERVIEW -import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.KEYBOARD_SHORTCUT -import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.TASK_DRAG -import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.UNKNOWN import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG @@ -33,6 +28,11 @@ import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.getEnterTransitionType import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.getExitTransitionType +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_FROM_OVERVIEW +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.KEYBOARD_SHORTCUT +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.TASK_DRAG +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.UNKNOWN import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUtilsTest.kt new file mode 100644 index 000000000000..8fab410adc30 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUtilsTest.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 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.desktopmode + +import android.graphics.Rect +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class DesktopModeUtilsTest { + @Test + fun isTaskBoundsEqual_stableBoundsAreEqual_returnTrue() { + assertThat(isTaskBoundsEqual(task2Bounds, stableBounds)).isTrue() + } + + @Test + fun isTaskBoundsEqual_stableBoundsAreNotEqual_returnFalse() { + assertThat(isTaskBoundsEqual(task4Bounds, stableBounds)).isFalse() + } + + @Test + fun isTaskWidthOrHeightEqual_stableBoundsAreEqual_returnTrue() { + assertThat(isTaskWidthOrHeightEqual(task2Bounds, stableBounds)).isTrue() + } + + @Test + fun isTaskWidthOrHeightEqual_stableBoundWidthIsEquals_returnTrue() { + assertThat(isTaskWidthOrHeightEqual(task3Bounds, stableBounds)).isTrue() + } + + @Test + fun isTaskWidthOrHeightEqual_stableBoundHeightIsEquals_returnTrue() { + assertThat(isTaskWidthOrHeightEqual(task3Bounds, stableBounds)).isTrue() + } + + @Test + fun isTaskWidthOrHeightEqual_stableBoundsWidthOrHeightAreNotEquals_returnFalse() { + assertThat(isTaskWidthOrHeightEqual(task1Bounds, stableBounds)).isTrue() + } + + private companion object { + val task1Bounds = Rect(0, 0, 0, 0) + val task2Bounds = Rect(1, 1, 1, 1) + val task3Bounds = Rect(0, 1, 0, 1) + val task4Bounds = Rect(1, 2, 2, 1) + val stableBounds = Rect(1, 1, 1, 1) + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index a841e168af18..10557dd9b439 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -83,8 +83,8 @@ import com.android.wm.shell.common.LaunchAdjacentController import com.android.wm.shell.common.MultiInstanceHelper import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SyncTransactionQueue -import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.UNKNOWN import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition +import com.android.wm.shell.desktopmode.DesktopTasksController.TaskbarDesktopTaskListener import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask @@ -94,6 +94,7 @@ import com.android.wm.shell.recents.RecentTasksController import com.android.wm.shell.recents.RecentsTransitionHandler import com.android.wm.shell.recents.RecentsTransitionStateListener import com.android.wm.shell.shared.desktopmode.DesktopModeStatus +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.UNKNOWN import com.android.wm.shell.shared.split.SplitScreenConstants import com.android.wm.shell.splitscreen.SplitScreenController import com.android.wm.shell.sysui.ShellCommandHandler @@ -122,12 +123,12 @@ import org.mockito.ArgumentMatchers.isA import org.mockito.ArgumentMatchers.isNull import org.mockito.Mock import org.mockito.Mockito -import org.mockito.Mockito.any import org.mockito.Mockito.anyInt import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.mock import org.mockito.Mockito.spy import org.mockito.Mockito.verify +import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.atLeastOnce import org.mockito.kotlin.eq @@ -175,6 +176,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor @Mock private lateinit var mockSurface: SurfaceControl + @Mock private lateinit var taskbarDesktopTaskListener: TaskbarDesktopTaskListener private lateinit var mockitoSession: StaticMockitoSession private lateinit var controller: DesktopTasksController @@ -237,6 +239,8 @@ class DesktopTasksControllerTest : ShellTestCase() { val captor = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java) verify(recentsTransitionHandler).addTransitionStateListener(captor.capture()) recentsTransitionStateListener = captor.value + + controller.taskbarDesktopTaskListener = taskbarDesktopTaskListener } private fun createController(): DesktopTasksController { @@ -282,6 +286,52 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun doesAnyTaskRequireTaskbarRounding_onlyFreeFormTaskIsRunning_returnFalse() { + setUpFreeformTask() + + assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isFalse() + } + + @Test + fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfFreeFormTask_returnTrue() { + val task1 = setUpFreeformTask() + + val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java) + controller.toggleDesktopTaskSize(task1) + verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture()) + + assertThat(argumentCaptor.value).isTrue() + } + + @Test + fun doesAnyTaskRequireTaskbarRounding_fullScreenTaskIsRunning_returnTrue() { + val stableBounds = Rect().apply { displayLayout.getStableBounds(this) } + setUpFreeformTask(bounds = stableBounds, active = true) + assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue() + } + + @Test + fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfFullScreenTask_returnFalse() { + val stableBounds = Rect().apply { displayLayout.getStableBounds(this) } + val task1 = setUpFreeformTask(bounds = stableBounds, active = true) + + val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java) + controller.toggleDesktopTaskSize(task1) + verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture()) + + assertThat(argumentCaptor.value).isFalse() + } + + @Test + fun doesAnyTaskRequireTaskbarRounding_splitScreenTaskIsRunning_returnTrue() { + val stableBounds = Rect().apply { displayLayout.getStableBounds(this) } + setUpFreeformTask(bounds = Rect(stableBounds.left, stableBounds.top, 500, stableBounds.bottom)) + + assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue() + } + + + @Test fun instantiate_cannotEnterDesktopMode_doNotAddInitCallback() { whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false) clearInvocations(shellInit) @@ -1429,6 +1479,78 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun onDesktopWindowMinimize_noActiveTask_doesntUpdateTransaction() { + val wct = WindowContainerTransaction() + controller.onDesktopWindowMinimize(wct, taskId = 1) + // Nothing happens. + assertThat(wct.hierarchyOps).isEmpty() + } + + @Test + fun onDesktopWindowMinimize_singleActiveTask_noWallpaperActivityToken_doesntUpdateTransaction() { + val task = setUpFreeformTask() + val wct = WindowContainerTransaction() + controller.onDesktopWindowMinimize(wct, taskId = task.taskId) + // Nothing happens. + assertThat(wct.hierarchyOps).isEmpty() + } + + @Test + fun onDesktopWindowMinimize_singleActiveTask_hasWallpaperActivityToken_removesWallpaper() { + val task = setUpFreeformTask() + val wallpaperToken = MockToken().token() + taskRepository.wallpaperActivityToken = wallpaperToken + + val wct = WindowContainerTransaction() + // The only active task is being minimized. + controller.onDesktopWindowMinimize(wct, taskId = task.taskId) + // Adds remove wallpaper operation + wct.assertRemoveAt(index = 0, wallpaperToken) + } + + @Test + fun onDesktopWindowMinimize_singleActiveTask_alreadyMinimized_doesntUpdateTransaction() { + val task = setUpFreeformTask() + val wallpaperToken = MockToken().token() + taskRepository.wallpaperActivityToken = wallpaperToken + taskRepository.minimizeTask(DEFAULT_DISPLAY, task.taskId) + + val wct = WindowContainerTransaction() + // The only active task is already minimized. + controller.onDesktopWindowMinimize(wct, taskId = task.taskId) + // Doesn't modify transaction + assertThat(wct.hierarchyOps).isEmpty() + } + + @Test + fun onDesktopWindowMinimize_multipleActiveTasks_doesntUpdateTransaction() { + val task1 = setUpFreeformTask() + setUpFreeformTask() + val wallpaperToken = MockToken().token() + taskRepository.wallpaperActivityToken = wallpaperToken + + val wct = WindowContainerTransaction() + controller.onDesktopWindowMinimize(wct, taskId = task1.taskId) + // Doesn't modify transaction + assertThat(wct.hierarchyOps).isEmpty() + } + + @Test + fun onDesktopWindowMinimize_multipleActiveTasks_minimizesTheOnlyVisibleTask_removesWallpaper() { + val task1 = setUpFreeformTask() + val task2 = setUpFreeformTask() + val wallpaperToken = MockToken().token() + taskRepository.wallpaperActivityToken = wallpaperToken + taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId) + + val wct = WindowContainerTransaction() + // task1 is the only visible task as task2 is minimized. + controller.onDesktopWindowMinimize(wct, taskId = task1.taskId) + // Adds remove wallpaper operation + wct.assertRemoveAt(index = 0, wallpaperToken) + } + + @Test fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() { assumeTrue(ENABLE_SHELL_TRANSITIONS) @@ -1443,8 +1565,34 @@ class DesktopTasksControllerTest : ShellTestCase() { assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_FREEFORM) - assertThat(wct.hierarchyOps).hasSize(2) - wct.assertReorderAt(1, homeTask, toTop = false) + assertThat(wct.hierarchyOps).hasSize(1) + } + + @Test + fun handleRequest_fullscreenTaskWithTaskOnHome_freeformVisible_returnSwitchToFreeformWCT() { + assumeTrue(ENABLE_SHELL_TRANSITIONS) + + val homeTask = setUpHomeTask() + val freeformTask = setUpFreeformTask() + markTaskVisible(freeformTask) + val fullscreenTask = createFullscreenTask() + fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME) + + val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask)) + + assertNotNull(wct, "should handle request") + assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode) + .isEqualTo(WINDOWING_MODE_FREEFORM) + + // There are 5 hops that are happening in this case: + // 1. Moving the fullscreen task to top as we add moveToDesktop() changes + // 2. Bringing home task to front + // 3. Pending intent for the wallpaper + // 4. Bringing the existing freeform task to top + // 5. Bringing the fullscreen task back at the top + assertThat(wct.hierarchyOps).hasSize(5) + wct.assertReorderAt(1, homeTask, toTop = true) + wct.assertReorderAt(4, fullscreenTask, toTop = true) } @Test @@ -1469,19 +1617,34 @@ class DesktopTasksControllerTest : ShellTestCase() { val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() } freeformTasks.forEach { markTaskVisible(it) } val fullscreenTask = createFullscreenTask() - val homeTask = setUpHomeTask(DEFAULT_DISPLAY) val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask)) // Make sure we reorder the new task to top, and the back task to the bottom - assertThat(wct!!.hierarchyOps.size).isEqualTo(3) + assertThat(wct!!.hierarchyOps.size).isEqualTo(2) + wct.assertReorderAt(0, fullscreenTask, toTop = true) + wct.assertReorderAt(1, freeformTasks[0], toTop = false) + } + + @Test + fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_otherTaskIsMinimized() { + assumeTrue(ENABLE_SHELL_TRANSITIONS) + + val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() } + freeformTasks.forEach { markTaskVisible(it) } + val fullscreenTask = createFullscreenTask() + fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME) + + val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask)) + + // Make sure we reorder the new task to top, and the back task to the bottom + assertThat(wct!!.hierarchyOps.size).isEqualTo(9) wct.assertReorderAt(0, fullscreenTask, toTop = true) - wct.assertReorderAt(1, homeTask, toTop = false) - wct.assertReorderAt(2, freeformTasks[0], toTop = false) + wct.assertReorderAt(8, freeformTasks[0], toTop = false) } @Test - fun handleRequest_fullscreenTaskToFreeform_alreadyBeyondLimit_existingAndNewTasksAreMinimized() { + fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_existingAndNewTasksAreMinimized() { assumeTrue(ENABLE_SHELL_TRANSITIONS) val minimizedTask = setUpFreeformTask() @@ -1490,15 +1653,16 @@ class DesktopTasksControllerTest : ShellTestCase() { freeformTasks.forEach { markTaskVisible(it) } val homeTask = setUpHomeTask() val fullscreenTask = createFullscreenTask() + fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME) val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask)) - assertThat(wct!!.hierarchyOps.size).isEqualTo(4) + assertThat(wct!!.hierarchyOps.size).isEqualTo(10) wct.assertReorderAt(0, fullscreenTask, toTop = true) - // Make sure we reorder the home task to the bottom, and minimized tasks below the home task. - wct.assertReorderAt(1, homeTask, toTop = false) - wct.assertReorderAt(2, minimizedTask, toTop = false) - wct.assertReorderAt(3, freeformTasks[0], toTop = false) + // Make sure we reorder the home task to the top, desktop tasks to top of them and minimized + // task is under the home task. + wct.assertReorderAt(1, homeTask, toTop = true) + wct.assertReorderAt(9, freeformTasks[0], toTop = false) } @Test @@ -2695,7 +2859,7 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun getSnapBounds_calculatesBoundsForResizable() { + fun snapToHalfScreen_getSnapBounds_calculatesBoundsForResizable() { val bounds = Rect(100, 100, 300, 300) val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply { topActivityInfo = ActivityInfo().apply { @@ -2710,13 +2874,45 @@ class DesktopTasksControllerTest : ShellTestCase() { STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom ) - controller.snapToHalfScreen(task, currentDragBounds, SnapPosition.LEFT) + controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT) // Assert bounds set to stable bounds val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds) assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds) } @Test + fun snapToHalfScreen_snapBoundsWhenAlreadySnapped_animatesSurfaceWithoutWCT() { + assumeTrue(ENABLE_SHELL_TRANSITIONS) + // Set up task to already be in snapped-left bounds + val bounds = Rect( + STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom + ) + val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply { + topActivityInfo = ActivityInfo().apply { + screenOrientation = SCREEN_ORIENTATION_LANDSCAPE + configuration.windowConfiguration.appBounds = bounds + } + isResizeable = true + } + + // Attempt to snap left again + val currentDragBounds = Rect(bounds).apply { offset(-100, 0) } + controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT) + + // Assert that task is NOT updated via WCT + verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any()) + + // Assert that task leash is updated via Surface Animations + verify(mReturnToDragStartAnimator).start( + eq(task.taskId), + eq(mockSurface), + eq(currentDragBounds), + eq(bounds), + eq(true) + ) + } + + @Test @DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING) fun handleSnapResizingTask_nonResizable_snapsToHalfScreen() { val task = setUpFreeformTask(DEFAULT_DISPLAY, Rect(0, 0, 200, 100)).apply { @@ -2747,7 +2943,8 @@ class DesktopTasksControllerTest : ShellTestCase() { eq(task.taskId), eq(mockSurface), eq(currentDragBounds), - eq(preDragBounds) + eq(preDragBounds), + eq(false) ) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt index 16a234b9e2f2..5b028371be2b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt @@ -8,6 +8,7 @@ import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW import android.app.WindowConfiguration.WindowingMode import android.graphics.PointF import android.os.IBinder +import android.os.SystemProperties import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.SurfaceControl @@ -16,6 +17,7 @@ import android.window.TransitionInfo import android.window.TransitionInfo.FLAG_IS_WALLPAPER import android.window.WindowContainerTransaction import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.internal.jank.InteractionJankMonitor import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTestCase @@ -29,19 +31,24 @@ import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_END_DRAG import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP import com.android.wm.shell.windowdecor.MoveToDesktopAnimator import java.util.function.Supplier +import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.eq import org.mockito.Mock +import org.mockito.MockitoSession import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.verifyZeroInteractions import org.mockito.kotlin.whenever +import org.mockito.quality.Strictness /** Tests of [DragToDesktopTransitionHandler]. */ @SmallTest @@ -61,10 +68,12 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { private lateinit var defaultHandler: DragToDesktopTransitionHandler private lateinit var springHandler: SpringDragToDesktopTransitionHandler + private lateinit var mockitoSession: MockitoSession @Before fun setUp() { - defaultHandler = DefaultDragToDesktopTransitionHandler( + defaultHandler = + DefaultDragToDesktopTransitionHandler( context, transitions, taskDisplayAreaOrganizer, @@ -72,7 +81,8 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { transactionSupplier, ) .apply { setSplitScreenController(splitScreenController) } - springHandler = SpringDragToDesktopTransitionHandler( + springHandler = + SpringDragToDesktopTransitionHandler( context, transitions, taskDisplayAreaOrganizer, @@ -80,6 +90,16 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { transactionSupplier, ) .apply { setSplitScreenController(splitScreenController) } + mockitoSession = + ExtendedMockito.mockitoSession() + .strictness(Strictness.LENIENT) + .mockStatic(SystemProperties::class.java) + .startMocking() + } + + @After + fun tearDown() { + mockitoSession.finishMocking() } @Test @@ -357,6 +377,77 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { verify(finishCallback).onTransitionFinished(null) } + @Test + fun propertyValue_returnsSystemPropertyValue() { + val name = "property_name" + val value = 10f + + whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), anyInt())) + .thenReturn(value.toInt()) + + assertEquals( + "Expects to return system properties stored value", + /* expected= */ value, + /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name) + ) + } + + @Test + fun propertyValue_withScale_returnsScaledSystemPropertyValue() { + val name = "property_name" + val value = 10f + val scale = 100f + + whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), anyInt())) + .thenReturn(value.toInt()) + + assertEquals( + "Expects to return scaled system properties stored value", + /* expected= */ value / scale, + /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name, scale = scale) + ) + } + + @Test + fun propertyValue_notSet_returnsDefaultValue() { + val name = "property_name" + val defaultValue = 50f + + whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), eq(defaultValue.toInt()))) + .thenReturn(defaultValue.toInt()) + + assertEquals( + "Expects to return the default value", + /* expected= */ defaultValue, + /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue( + name, + default = defaultValue + ) + ) + } + + @Test + fun propertyValue_withScaleNotSet_returnsDefaultValue() { + val name = "property_name" + val defaultValue = 0.5f + val scale = 100f + // Default value is multiplied when provided as a default value for [SystemProperties] + val scaledDefault = (defaultValue * scale).toInt() + + whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), eq(scaledDefault))) + .thenReturn(scaledDefault) + + assertEquals( + "Expects to return the default value", + /* expected= */ defaultValue, + /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue( + name, + default = defaultValue, + scale = scale + ) + ) + } + private fun startDrag( handler: DragToDesktopTransitionHandler, task: RunningTaskInfo = createTask(), @@ -462,4 +553,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { ) } } + + private fun systemPropertiesKey(name: String) = + "${SpringDragToDesktopTransitionHandler.SYSTEM_PROPERTIES_GROUP}.$name" } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java index e5157c974e2d..e0463b41ad20 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java @@ -48,7 +48,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.jank.InteractionJankMonitor; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource; +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.transition.Transitions; import org.junit.Before; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt index 4d407387d323..765021fbbd3d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt @@ -26,13 +26,16 @@ import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository import com.android.wm.shell.desktopmode.education.data.WindowingEducationProto +import com.android.wm.shell.util.createWindowingEducationProto import com.google.common.truth.Truth.assertThat import java.io.File +import java.time.Duration import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain @@ -88,35 +91,22 @@ class AppHandleEducationDatastoreRepositoryTest { assertThat(resultProto).isEqualTo(windowingEducationProto) } - private fun createWindowingEducationProto( - educationViewedTimestampMillis: Long? = null, - featureUsedTimestampMillis: Long? = null, - appUsageStats: Map<String, Int>? = null, - appUsageStatsLastUpdateTimestampMillis: Long? = null - ): WindowingEducationProto = - WindowingEducationProto.newBuilder() - .apply { - if (educationViewedTimestampMillis != null) - setEducationViewedTimestampMillis(educationViewedTimestampMillis) - if (featureUsedTimestampMillis != null) - setFeatureUsedTimestampMillis(featureUsedTimestampMillis) - setAppHandleEducation( - createAppHandleEducationProto( - appUsageStats, appUsageStatsLastUpdateTimestampMillis)) - } - .build() + @Test + fun updateAppUsageStats_updatesDatastoreProto() = + runTest(StandardTestDispatcher()) { + val appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 3) + val appUsageStatsLastUpdateTimestamp = Duration.ofMillis(123L) + val windowingEducationProto = + createWindowingEducationProto( + appUsageStats = appUsageStats, + appUsageStatsLastUpdateTimestampMillis = + appUsageStatsLastUpdateTimestamp.toMillis()) + + datastoreRepository.updateAppUsageStats(appUsageStats, appUsageStatsLastUpdateTimestamp) - private fun createAppHandleEducationProto( - appUsageStats: Map<String, Int>? = null, - appUsageStatsLastUpdateTimestampMillis: Long? = null - ): WindowingEducationProto.AppHandleEducation = - WindowingEducationProto.AppHandleEducation.newBuilder() - .apply { - if (appUsageStats != null) putAllAppUsageStats(appUsageStats) - if (appUsageStatsLastUpdateTimestampMillis != null) - setAppUsageStatsLastUpdateTimestampMillis(appUsageStatsLastUpdateTimestampMillis) - } - .build() + val result = testDatastore.data.first() + assertThat(result).isEqualTo(windowingEducationProto) + } companion object { private const val GMAIL_PACKAGE_NAME = "com.google.android.gm" diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt new file mode 100644 index 000000000000..c0d71c0bf5db --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2024 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.desktopmode.education + +import android.app.usage.UsageStats +import android.app.usage.UsageStatsManager +import android.content.Context +import android.testing.AndroidTestingRunner +import android.testing.TestableContext +import android.testing.TestableResources +import androidx.test.filters.SmallTest +import com.android.wm.shell.R +import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository +import com.android.wm.shell.util.createWindowingEducationProto +import com.google.common.truth.Truth.assertThat +import kotlin.Int.Companion.MAX_VALUE +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyLong +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class AppHandleEducationFilterTest : ShellTestCase() { + @Mock private lateinit var datastoreRepository: AppHandleEducationDatastoreRepository + @Mock private lateinit var mockUsageStatsManager: UsageStatsManager + private lateinit var educationFilter: AppHandleEducationFilter + private lateinit var testableResources: TestableResources + private lateinit var testableContext: TestableContext + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + testableContext = TestableContext(mContext) + testableResources = + testableContext.orCreateTestableResources.apply { + addOverride( + R.array.desktop_windowing_app_handle_education_allowlist_apps, + arrayOf(GMAIL_PACKAGE_NAME)) + addOverride(R.integer.desktop_windowing_education_required_time_since_setup_seconds, 0) + addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3) + addOverride( + R.integer.desktop_windowing_education_app_usage_cache_interval_seconds, MAX_VALUE) + addOverride(R.integer.desktop_windowing_education_app_launch_interval_seconds, 100) + } + testableContext.addMockSystemService(Context.USAGE_STATS_SERVICE, mockUsageStatsManager) + educationFilter = AppHandleEducationFilter(testableContext, datastoreRepository) + } + + @Test + fun shouldShowAppHandleEducation_isTriggerValid_returnsTrue() = runTest { + // setup() makes sure that all of the conditions satisfy and #shouldShowAppHandleEducation + // should return true + val windowingEducationProto = + createWindowingEducationProto( + appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4), + appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE) + `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto) + + val result = educationFilter.shouldShowAppHandleEducation(GMAIL_PACKAGE_NAME) + + assertThat(result).isTrue() + } + + @Test + fun shouldShowAppHandleEducation_focusAppNotInAllowlist_returnsFalse() = runTest { + // Pass Youtube as current focus app, it is not in allowlist hence #shouldShowAppHandleEducation + // should return false + testableResources.addOverride( + R.array.desktop_windowing_app_handle_education_allowlist_apps, arrayOf(GMAIL_PACKAGE_NAME)) + val windowingEducationProto = + createWindowingEducationProto( + appUsageStats = mapOf(YOUTUBE_PACKAGE_NAME to 4), + appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE) + `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto) + + val result = educationFilter.shouldShowAppHandleEducation(YOUTUBE_PACKAGE_NAME) + + assertThat(result).isFalse() + } + + @Test + fun shouldShowAppHandleEducation_timeSinceSetupIsNotSufficient_returnsFalse() = runTest { + // Time required to have passed setup is > 100 years, hence #shouldShowAppHandleEducation should + // return false + testableResources.addOverride( + R.integer.desktop_windowing_education_required_time_since_setup_seconds, MAX_VALUE) + val windowingEducationProto = + createWindowingEducationProto( + appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4), + appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE) + `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto) + + val result = educationFilter.shouldShowAppHandleEducation(GMAIL_PACKAGE_NAME) + + assertThat(result).isFalse() + } + + @Test + fun shouldShowAppHandleEducation_educationViewedBefore_returnsFalse() = runTest { + // Education has been viewed before, hence #shouldShowAppHandleEducation should return false + val windowingEducationProto = + createWindowingEducationProto( + appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4), + educationViewedTimestampMillis = 123L, + appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE) + `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto) + + val result = educationFilter.shouldShowAppHandleEducation(GMAIL_PACKAGE_NAME) + + assertThat(result).isFalse() + } + + @Test + fun shouldShowAppHandleEducation_featureUsedBefore_returnsFalse() = runTest { + // Feature has been used before, hence #shouldShowAppHandleEducation should return false + val windowingEducationProto = + createWindowingEducationProto( + appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4), + featureUsedTimestampMillis = 123L, + appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE) + `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto) + + val result = educationFilter.shouldShowAppHandleEducation(GMAIL_PACKAGE_NAME) + + assertThat(result).isFalse() + } + + @Test + fun shouldShowAppHandleEducation_doesNotHaveMinAppUsage_returnsFalse() = runTest { + // Simulate that gmail app has been launched twice before, minimum app launch count is 3, hence + // #shouldShowAppHandleEducation should return false + testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3) + val windowingEducationProto = + createWindowingEducationProto( + appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2), + appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE) + `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto) + + val result = educationFilter.shouldShowAppHandleEducation(GMAIL_PACKAGE_NAME) + + assertThat(result).isFalse() + } + + @Test + fun shouldShowAppHandleEducation_appUsageStatsStale_queryAppUsageStats() = runTest { + // UsageStats caching interval is set to 0ms, that means caching should happen very frequently + testableResources.addOverride( + R.integer.desktop_windowing_education_app_usage_cache_interval_seconds, 0) + // The DataStore currently holds a proto object where Gmail's app launch count is recorded as 4. + // This value exceeds the minimum required count of 3. + testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3) + val windowingEducationProto = + createWindowingEducationProto( + appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4), + appUsageStatsLastUpdateTimestampMillis = 0) + // The mocked UsageStatsManager is configured to return a launch count of 2 for Gmail. + // This value is below the minimum required count of 3. + `when`(mockUsageStatsManager.queryAndAggregateUsageStats(anyLong(), anyLong())) + .thenReturn(mapOf(GMAIL_PACKAGE_NAME to UsageStats().apply { mAppLaunchCount = 2 })) + `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto) + + val result = educationFilter.shouldShowAppHandleEducation(GMAIL_PACKAGE_NAME) + + // Result should be false as queried usage stats should be considered to determine the result + // instead of cached stats + assertThat(result).isFalse() + } + + companion object { + private const val GMAIL_PACKAGE_NAME = "com.google.android.gm" + private const val YOUTUBE_PACKAGE_NAME = "com.google.android.youtube" + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt index 6736593bba5b..0c3f98a324cd 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt @@ -24,13 +24,13 @@ import android.window.IWindowContainerToken import android.window.WindowContainerToken import androidx.test.filters.SmallTest import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.shared.GroupedRecentTaskInfo +import com.android.wm.shell.shared.GroupedRecentTaskInfo.CREATOR +import com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_FREEFORM +import com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_SINGLE +import com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_SPLIT +import com.android.wm.shell.shared.split.SplitBounds import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_50_50 -import com.android.wm.shell.util.GroupedRecentTaskInfo -import com.android.wm.shell.util.GroupedRecentTaskInfo.CREATOR -import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM -import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_SINGLE -import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_SPLIT -import com.android.wm.shell.util.SplitBounds import com.google.common.truth.Correspondence import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertThrows diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java index e1fe4e9054c0..a8d40db096dd 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java @@ -68,13 +68,13 @@ import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; +import com.android.wm.shell.shared.GroupedRecentTaskInfo; import com.android.wm.shell.shared.ShellSharedConstants; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; +import com.android.wm.shell.shared.split.SplitBounds; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; -import com.android.wm.shell.util.GroupedRecentTaskInfo; -import com.android.wm.shell.util.SplitBounds; import org.junit.After; import org.junit.Before; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java index bfb760b6fc8c..248393cef9ae 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java @@ -12,7 +12,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.util.SplitBounds; +import com.android.wm.shell.shared.split.SplitBounds; import org.junit.Before; import org.junit.Test; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleBarLocationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleBarLocationTest.kt index 27e0b196f0be..b9bf95b16e70 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleBarLocationTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleBarLocationTest.kt @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.wm.shell.common.bubbles +package com.android.wm.shell.shared.bubbles import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.wm.shell.ShellTestCase -import com.android.wm.shell.common.bubbles.BubbleBarLocation.DEFAULT -import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT -import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT +import com.android.wm.shell.shared.bubbles.BubbleBarLocation.DEFAULT +import com.android.wm.shell.shared.bubbles.BubbleBarLocation.LEFT +import com.android.wm.shell.shared.bubbles.BubbleBarLocation.RIGHT import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt index 5b22eddcb6ee..641063c27076 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.common.bubbles +package com.android.wm.shell.shared.bubbles import android.os.Parcel import android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE @@ -41,6 +41,7 @@ class BubbleInfoTest : ShellTestCase() { "com.some.package", "title", "Some app", + true, true ) val parcel = Parcel.obtain() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/handles/RegionSamplingHelperTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/handles/RegionSamplingHelperTest.kt new file mode 100644 index 000000000000..d3e291f7dd1f --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/handles/RegionSamplingHelperTest.kt @@ -0,0 +1,154 @@ +/* + * 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.shared.handles + + +import android.graphics.Rect +import android.testing.TestableLooper.RunWithLooper +import android.view.SurfaceControl +import android.view.View +import android.view.ViewRootImpl +import androidx.concurrent.futures.DirectExecutor +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.TestShellExecutor +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever +import org.mockito.junit.MockitoJUnit +import org.mockito.kotlin.argumentCaptor + +@RunWith(AndroidJUnit4::class) +@SmallTest +@RunWithLooper +class RegionSamplingHelperTest : ShellTestCase() { + + @Mock + lateinit var sampledView: View + @Mock + lateinit var samplingCallback: RegionSamplingHelper.SamplingCallback + @Mock + lateinit var compositionListener: RegionSamplingHelper.SysuiCompositionSamplingListener + @Mock + lateinit var viewRootImpl: ViewRootImpl + @Mock + lateinit var surfaceControl: SurfaceControl + @Mock + lateinit var wrappedSurfaceControl: SurfaceControl + @JvmField @Rule + var rule = MockitoJUnit.rule() + lateinit var regionSamplingHelper: RegionSamplingHelper + + @Before + fun setup() { + whenever(sampledView.isAttachedToWindow).thenReturn(true) + whenever(sampledView.viewRootImpl).thenReturn(viewRootImpl) + whenever(viewRootImpl.surfaceControl).thenReturn(surfaceControl) + whenever(surfaceControl.isValid).thenReturn(true) + whenever(wrappedSurfaceControl.isValid).thenReturn(true) + whenever(samplingCallback.isSamplingEnabled).thenReturn(true) + getInstrumentation().runOnMainSync(Runnable { + regionSamplingHelper = object : RegionSamplingHelper( + sampledView, samplingCallback, + DirectExecutor.INSTANCE, DirectExecutor.INSTANCE, compositionListener + ) { + override fun wrap(stopLayerControl: SurfaceControl?): SurfaceControl { + return wrappedSurfaceControl + } + } + }) + regionSamplingHelper.setWindowVisible(true) + } + + @Test + fun testStart_register() { + regionSamplingHelper.start(Rect(0, 0, 100, 100)) + verify(compositionListener).register(any(), anyInt(), eq(wrappedSurfaceControl), any()) + } + + @Test + fun testStart_unregister() { + regionSamplingHelper.start(Rect(0, 0, 100, 100)) + regionSamplingHelper.setWindowVisible(false) + verify(compositionListener).unregister(any()) + } + + @Test + fun testStart_hasBlur_neverRegisters() { + regionSamplingHelper.setWindowHasBlurs(true) + regionSamplingHelper.start(Rect(0, 0, 100, 100)) + verify(compositionListener, never()) + .register(any(), anyInt(), eq(wrappedSurfaceControl), any()) + } + + @Test + fun testStart_stopAndDestroy() { + regionSamplingHelper.start(Rect(0, 0, 100, 100)) + regionSamplingHelper.stopAndDestroy() + verify(compositionListener).unregister(any()) + } + + @Test + fun testCompositionSamplingListener_has_nonEmptyRect() { + // simulate race condition + val fakeExecutor = TestShellExecutor() // pass in as backgroundExecutor + val fakeSamplingCallback = mock(RegionSamplingHelper.SamplingCallback::class.java) + + whenever(fakeSamplingCallback.isSamplingEnabled).thenReturn(true) + whenever(wrappedSurfaceControl.isValid).thenReturn(true) + getInstrumentation().runOnMainSync(Runnable { + regionSamplingHelper = object : RegionSamplingHelper( + sampledView, fakeSamplingCallback, + DirectExecutor.INSTANCE, fakeExecutor, compositionListener + ) { + override fun wrap(stopLayerControl: SurfaceControl?): SurfaceControl { + return wrappedSurfaceControl + } + } + }) + regionSamplingHelper.setWindowVisible(true) + regionSamplingHelper.start(Rect(0, 0, 100, 100)) + + // make sure background task is enqueued + assertThat(fakeExecutor.getCallbacks().size).isEqualTo(1) + + // make sure regionSamplingHelper will have empty Rect + whenever(fakeSamplingCallback.getSampledRegion(any())).thenReturn(Rect(0, 0, 0, 0)) + regionSamplingHelper.onLayoutChange(sampledView, 0, 0, 0, 0, 0, 0, 0, 0) + + // resume running of background thread + fakeExecutor.flushAll() + + // grab Rect passed into compositionSamplingListener and make sure it's not empty + val argumentGrabber = argumentCaptor<Rect>() + verify(compositionListener).register(any(), anyInt(), eq(wrappedSurfaceControl), + argumentGrabber.capture()) + assertThat(argumentGrabber.firstValue.isEmpty).isFalse() + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java index 0434742c571b..198488582700 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java @@ -294,16 +294,6 @@ public class TaskViewTest extends ShellTestCase { } @Test - public void testUnsetOnBackPressedOnTaskRoot_legacyTransitions() { - assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS); - mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash); - verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true)); - - mTaskViewTaskController.onTaskVanished(mTaskInfo); - verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false)); - } - - @Test public void testOnNewTask_noSurface() { assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); WindowContainerTransaction wct = new WindowContainerTransaction(); @@ -443,19 +433,6 @@ public class TaskViewTest extends ShellTestCase { } @Test - public void testUnsetOnBackPressedOnTaskRoot() { - assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); - WindowContainerTransaction wct = new WindowContainerTransaction(); - mTaskViewTaskController.prepareOpenAnimation(true /* newTask */, - new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo, - mLeash, wct); - verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true)); - - mTaskViewTaskController.prepareCloseAnimation(); - verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false)); - } - - @Test public void testSetObscuredTouchRect() { mTaskView.setObscuredTouchRect( new Rect(/* left= */ 0, /* top= */ 10, /* right= */ 100, /* bottom= */ 120)); @@ -713,4 +690,26 @@ public class TaskViewTest extends ShellTestCase { verify(mViewHandler).post(any()); verify(mTaskView).setResizeBackgroundColor(eq(Color.BLUE)); } + + @Test + public void testOnAppeared_setsTrimmableTask() { + WindowContainerTransaction wct = new WindowContainerTransaction(); + mTaskViewTaskController.prepareOpenAnimation(true /* newTask */, + new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo, + mLeash, wct); + + assertThat(wct.getHierarchyOps().get(0).isTrimmableFromRecents()).isFalse(); + } + + @Test + public void testMoveToFullscreen_callsTaskRemovalStarted() { + WindowContainerTransaction wct = new WindowContainerTransaction(); + mTaskViewTaskController.prepareOpenAnimation(true /* newTask */, + new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo, + mLeash, wct); + mTaskView.surfaceCreated(mock(SurfaceHolder.class)); + mTaskViewTaskController.moveToFullscreen(); + + verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId)); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationProtoUtils.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationProtoUtils.kt new file mode 100644 index 000000000000..def4b916a5f7 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationProtoUtils.kt @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024 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.util + +import com.android.wm.shell.desktopmode.education.data.WindowingEducationProto + +/** + * Constructs a [WindowingEducationProto] object, populating its fields with the provided + * parameters. + * + * Any fields without corresponding parameters will retain their default values. + */ +fun createWindowingEducationProto( + educationViewedTimestampMillis: Long? = null, + featureUsedTimestampMillis: Long? = null, + appUsageStats: Map<String, Int>? = null, + appUsageStatsLastUpdateTimestampMillis: Long? = null +): WindowingEducationProto = + WindowingEducationProto.newBuilder() + .apply { + if (educationViewedTimestampMillis != null) { + setEducationViewedTimestampMillis(educationViewedTimestampMillis) + } + if (featureUsedTimestampMillis != null) { + setFeatureUsedTimestampMillis(featureUsedTimestampMillis) + } + setAppHandleEducation( + createAppHandleEducationProto(appUsageStats, appUsageStatsLastUpdateTimestampMillis)) + } + .build() + +/** + * Constructs a [WindowingEducationProto.AppHandleEducation] object, populating its fields with the + * provided parameters. + * + * Any fields without corresponding parameters will retain their default values. + */ +fun createAppHandleEducationProto( + appUsageStats: Map<String, Int>? = null, + appUsageStatsLastUpdateTimestampMillis: Long? = null +): WindowingEducationProto.AppHandleEducation = + WindowingEducationProto.AppHandleEducation.newBuilder() + .apply { + if (appUsageStats != null) putAllAppUsageStats(appUsageStats) + if (appUsageStatsLastUpdateTimestampMillis != null) { + setAppUsageStatsLastUpdateTimestampMillis(appUsageStatsLastUpdateTimestampMillis) + } + } + .build() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index 4d6b3b907a65..be0549b6655d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -34,6 +34,7 @@ import android.hardware.display.VirtualDisplay import android.hardware.input.InputManager import android.net.Uri import android.os.Handler +import android.os.UserHandle import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.CheckFlagsRule @@ -79,11 +80,13 @@ import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.common.MultiInstanceHelper import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SyncTransactionQueue -import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource +import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler import com.android.wm.shell.desktopmode.DesktopTasksController import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition +import com.android.wm.shell.desktopmode.DesktopTasksLimiter import com.android.wm.shell.freeform.FreeformTaskTransitionStarter import com.android.wm.shell.shared.desktopmode.DesktopModeStatus +import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource import com.android.wm.shell.splitscreen.SplitScreenController import com.android.wm.shell.sysui.ShellCommandHandler import com.android.wm.shell.sysui.ShellController @@ -110,8 +113,9 @@ import org.mockito.Mockito.anyInt import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.times -import org.mockito.Mockito.verify +import org.mockito.kotlin.verify import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.argThat import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doNothing @@ -160,9 +164,14 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Mock private lateinit var mockWindowManager: IWindowManager @Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor @Mock private lateinit var mockGenericLinksParser: AppToWebGenericLinksParser + @Mock private lateinit var mockUserHandle: UserHandle @Mock private lateinit var mockToast: Toast private val bgExecutor = TestShellExecutor() @Mock private lateinit var mockMultiInstanceHelper: MultiInstanceHelper + @Mock private lateinit var mockTasksLimiter: DesktopTasksLimiter + @Mock private lateinit var mockFreeformTaskTransitionStarter: FreeformTaskTransitionStarter + @Mock private lateinit var mockActivityOrientationChangeHandler: + DesktopActivityOrientationChangeHandler private lateinit var spyContext: TestableContext private val transactionFactory = Supplier<SurfaceControl.Transaction> { @@ -215,7 +224,9 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { transactionFactory, mockRootTaskDisplayAreaOrganizer, windowDecorByTaskIdSpy, - mockInteractionJankMonitor + mockInteractionJankMonitor, + Optional.of(mockTasksLimiter), + Optional.of(mockActivityOrientationChangeHandler) ) desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController) whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout) @@ -346,9 +357,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { val inputManager = mock(InputManager::class.java) spyContext.addMockSystemService(InputManager::class.java, inputManager) - val freeformTaskTransitionStarter = mock(FreeformTaskTransitionStarter::class.java) desktopModeWindowDecorViewModel - .setFreeformTaskTransitionStarter(freeformTaskTransitionStarter) + .setFreeformTaskTransitionStarter(mockFreeformTaskTransitionStarter) onClickListener.onClick(view) @@ -371,14 +381,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { val view = mock(View::class.java) whenever(view.id).thenReturn(R.id.close_window) - val freeformTaskTransitionStarter = mock(FreeformTaskTransitionStarter::class.java) desktopModeWindowDecorViewModel - .setFreeformTaskTransitionStarter(freeformTaskTransitionStarter) + .setFreeformTaskTransitionStarter(mockFreeformTaskTransitionStarter) onClickListenerCaptor.value.onClick(view) val transactionCaptor = argumentCaptor<WindowContainerTransaction>() - verify(freeformTaskTransitionStarter).startRemoveTransition(transactionCaptor.capture()) + verify(mockFreeformTaskTransitionStarter).startRemoveTransition(transactionCaptor.capture()) val wct = transactionCaptor.firstValue assertEquals(1, wct.getHierarchyOps().size) @@ -388,6 +397,38 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { } @Test + @EnableFlags(Flags.FLAG_ENABLE_MINIMIZE_BUTTON) + fun testMinimizeButtonInFreefrom_minimizeWindow() { + val onClickListenerCaptor = forClass(View.OnClickListener::class.java) + as ArgumentCaptor<View.OnClickListener> + val decor = createOpenTaskDecoration( + windowingMode = WINDOWING_MODE_FREEFORM, + onCaptionButtonClickListener = onClickListenerCaptor + ) + + val view = mock(View::class.java) + whenever(view.id).thenReturn(R.id.minimize_window) + + desktopModeWindowDecorViewModel + .setFreeformTaskTransitionStarter(mockFreeformTaskTransitionStarter) + + onClickListenerCaptor.value.onClick(view) + + val transactionCaptor = argumentCaptor<WindowContainerTransaction>() + verify(mockFreeformTaskTransitionStarter) + .startMinimizedModeTransition(transactionCaptor.capture()) + val wct = transactionCaptor.firstValue + + verify(mockTasksLimiter).addPendingMinimizeChange( + anyOrNull(), eq(DEFAULT_DISPLAY), eq(decor.mTaskInfo.taskId)) + + assertEquals(1, wct.getHierarchyOps().size) + assertEquals(HierarchyOp.HIERARCHY_OP_TYPE_REORDER, wct.getHierarchyOps().get(0).getType()) + assertFalse(wct.getHierarchyOps().get(0).getToTop()) + assertEquals(decor.mTaskInfo.token.asBinder(), wct.getHierarchyOps().get(0).getContainer()) + } + + @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY) fun testDecorationIsCreatedForTopTranslucentActivitiesWithStyleFloating() { val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true).apply { @@ -559,6 +600,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Test fun testOnDecorSnappedLeft_snapResizes() { + val taskSurfaceCaptor = argumentCaptor<SurfaceControl>() val onLeftSnapClickListenerCaptor = forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>> val decor = createOpenTaskDecoration( @@ -569,8 +611,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds onLeftSnapClickListenerCaptor.value.invoke() - verify(mockDesktopTasksController) - .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT) + verify(mockDesktopTasksController).snapToHalfScreen( + eq(decor.mTaskInfo), + taskSurfaceCaptor.capture(), + eq(currentBounds), + eq(SnapPosition.LEFT) + ) + assertEquals(taskSurfaceCaptor.firstValue, decor.mTaskSurface) } @Test @@ -591,6 +638,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Test @DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING) fun testOnSnapResizeLeft_nonResizable_decorSnappedLeft() { + val taskSurfaceCaptor = argumentCaptor<SurfaceControl>() val onLeftSnapClickListenerCaptor = forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>> val decor = createOpenTaskDecoration( @@ -601,8 +649,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds onLeftSnapClickListenerCaptor.value.invoke() - verify(mockDesktopTasksController) - .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT) + verify(mockDesktopTasksController).snapToHalfScreen( + eq(decor.mTaskInfo), + taskSurfaceCaptor.capture(), + eq(currentBounds), + eq(SnapPosition.LEFT) + ) + assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue) } @Test @@ -619,12 +672,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { onLeftSnapClickListenerCaptor.value.invoke() verify(mockDesktopTasksController, never()) - .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT) + .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.LEFT)) verify(mockToast).show() } @Test fun testOnDecorSnappedRight_snapResizes() { + val taskSurfaceCaptor = argumentCaptor<SurfaceControl>() val onRightSnapClickListenerCaptor = forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>> val decor = createOpenTaskDecoration( @@ -635,8 +689,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds onRightSnapClickListenerCaptor.value.invoke() - verify(mockDesktopTasksController) - .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT) + verify(mockDesktopTasksController).snapToHalfScreen( + eq(decor.mTaskInfo), + taskSurfaceCaptor.capture(), + eq(currentBounds), + eq(SnapPosition.RIGHT) + ) + assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue) } @Test @@ -657,6 +716,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Test @DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING) fun testOnSnapResizeRight_nonResizable_decorSnappedRight() { + val taskSurfaceCaptor = argumentCaptor<SurfaceControl>() val onRightSnapClickListenerCaptor = forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>> val decor = createOpenTaskDecoration( @@ -667,8 +727,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds onRightSnapClickListenerCaptor.value.invoke() - verify(mockDesktopTasksController) - .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT) + verify(mockDesktopTasksController).snapToHalfScreen( + eq(decor.mTaskInfo), + taskSurfaceCaptor.capture(), + eq(currentBounds), + eq(SnapPosition.RIGHT) + ) + assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue) } @Test @@ -685,7 +750,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { onRightSnapClickListenerCaptor.value.invoke() verify(mockDesktopTasksController, never()) - .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT) + .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.RIGHT)) verify(mockToast).show() } @@ -853,12 +918,12 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { openInBrowserListenerCaptor.value.accept(uri) - verify(spyContext).startActivity(argThat { intent -> + verify(spyContext).startActivityAsUser(argThat { intent -> intent.data == uri && ((intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK) != 0) && intent.categories.contains(Intent.CATEGORY_LAUNCHER) && intent.action == Intent.ACTION_MAIN - }) + }, eq(mockUserHandle)) } @Test @@ -992,6 +1057,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { private fun createOpenTaskDecoration( @WindowingMode windowingMode: Int, + taskSurface: SurfaceControl = SurfaceControl(), onMaxOrRestoreListenerCaptor: ArgumentCaptor<Function0<Unit>> = forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>, onLeftSnapClickListenerCaptor: ArgumentCaptor<Function0<Unit>> = @@ -1010,7 +1076,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { forClass(View.OnClickListener::class.java) as ArgumentCaptor<View.OnClickListener> ): DesktopModeWindowDecoration { val decor = setUpMockDecorationForTask(createTask(windowingMode = windowingMode)) - onTaskOpening(decor.mTaskInfo) + onTaskOpening(decor.mTaskInfo, taskSurface) verify(decor).setOnMaximizeOrRestoreClickListener(onMaxOrRestoreListenerCaptor.capture()) verify(decor).setOnLeftSnapClickListener(onLeftSnapClickListenerCaptor.capture()) verify(decor).setOnRightSnapClickListener(onRightSnapClickListenerCaptor.capture()) @@ -1069,6 +1135,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { ).thenReturn(decoration) decoration.mTaskInfo = task whenever(decoration.isFocused).thenReturn(task.isFocused) + whenever(decoration.user).thenReturn(mockUserHandle) if (task.windowingMode == WINDOWING_MODE_MULTI_WINDOW) { whenever(mockSplitScreenController.isTaskInSplitScreen(task.taskId)) .thenReturn(true) diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp index 43a70c176a83..5a4cff0c319e 100644 --- a/libs/androidfw/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -309,9 +309,8 @@ Asset::Asset(void) return NULL; } - // We succeeded, so relinquish control of dataMap pAsset->mAccessMode = mode; - return std::move(pAsset); + return pAsset; } /* @@ -328,9 +327,8 @@ Asset::Asset(void) return NULL; } - // We succeeded, so relinquish control of dataMap pAsset->mAccessMode = mode; - return std::move(pAsset); + return pAsset; } /* diff --git a/libs/hwui/ColorFilter.h b/libs/hwui/ColorFilter.h index 31c9db7ca4fb..3a3bfb47dfdd 100644 --- a/libs/hwui/ColorFilter.h +++ b/libs/hwui/ColorFilter.h @@ -106,7 +106,7 @@ public: private: sk_sp<SkColorFilter> createInstance() override { - return SkColorFilters::Matrix(mMatrix.data()); + return SkColorFilters::Matrix(mMatrix.data(), SkColorFilters::Clamp::kNo); } private: diff --git a/libs/hwui/FeatureFlags.h b/libs/hwui/FeatureFlags.h index ac75c077b58f..c1c30f5379ab 100644 --- a/libs/hwui/FeatureFlags.h +++ b/libs/hwui/FeatureFlags.h @@ -49,6 +49,15 @@ inline bool letter_spacing_justification() { #endif // __ANDROID__ } +inline bool typeface_redesign() { +#ifdef __ANDROID__ + static bool flag = com_android_text_flags_typeface_redesign(); + return flag; +#else + return true; +#endif // __ANDROID__ +} + } // namespace text_feature } // namespace android diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h index f8574ee50525..1510ce1378d8 100644 --- a/libs/hwui/hwui/MinikinUtils.h +++ b/libs/hwui/hwui/MinikinUtils.h @@ -27,6 +27,8 @@ #include <cutils/compiler.h> #include <log/log.h> #include <minikin/Layout.h> + +#include "FeatureFlags.h" #include "MinikinSkia.h" #include "Paint.h" #include "Typeface.h" @@ -71,27 +73,42 @@ public: static void forFontRun(const minikin::Layout& layout, Paint* paint, F& f) { float saveSkewX = paint->getSkFont().getSkewX(); bool savefakeBold = paint->getSkFont().isEmbolden(); - const minikin::MinikinFont* curFont = nullptr; - size_t start = 0; - size_t nGlyphs = layout.nGlyphs(); - for (size_t i = 0; i < nGlyphs; i++) { - const minikin::MinikinFont* nextFont = layout.typeface(i).get(); - if (i > 0 && nextFont != curFont) { + if (text_feature::typeface_redesign()) { + for (uint32_t runIdx = 0; runIdx < layout.getFontRunCount(); ++runIdx) { + uint32_t start = layout.getFontRunStart(runIdx); + uint32_t end = layout.getFontRunEnd(runIdx); + const minikin::FakedFont& fakedFont = layout.getFontRunFont(runIdx); + + std::shared_ptr<minikin::MinikinFont> font = fakedFont.typeface(); + SkFont* skfont = &paint->getSkFont(); + MinikinFontSkia::populateSkFont(skfont, font.get(), fakedFont.fakery); + f(start, end); + skfont->setSkewX(saveSkewX); + skfont->setEmbolden(savefakeBold); + } + } else { + const minikin::MinikinFont* curFont = nullptr; + size_t start = 0; + size_t nGlyphs = layout.nGlyphs(); + for (size_t i = 0; i < nGlyphs; i++) { + const minikin::MinikinFont* nextFont = layout.typeface(i).get(); + if (i > 0 && nextFont != curFont) { + SkFont* skfont = &paint->getSkFont(); + MinikinFontSkia::populateSkFont(skfont, curFont, layout.getFakery(start)); + f(start, i); + skfont->setSkewX(saveSkewX); + skfont->setEmbolden(savefakeBold); + start = i; + } + curFont = nextFont; + } + if (nGlyphs > start) { SkFont* skfont = &paint->getSkFont(); MinikinFontSkia::populateSkFont(skfont, curFont, layout.getFakery(start)); - f(start, i); + f(start, nGlyphs); skfont->setSkewX(saveSkewX); skfont->setEmbolden(savefakeBold); - start = i; } - curFont = nextFont; - } - if (nGlyphs > start) { - SkFont* skfont = &paint->getSkFont(); - MinikinFontSkia::populateSkFont(skfont, curFont, layout.getFakery(start)); - f(start, nGlyphs); - skfont->setSkewX(saveSkewX); - skfont->setEmbolden(savefakeBold); } } }; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 8bb11badb607..dfda25d013ed 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -761,8 +761,8 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) { if (mExpectSurfaceStats) { reportMetricsWithPresentTime(); { // acquire lock - std::lock_guard lock(mLast4FrameMetricsInfosMutex); - FrameMetricsInfo& next = mLast4FrameMetricsInfos.next(); + std::lock_guard lock(mLastFrameMetricsInfosMutex); + FrameMetricsInfo& next = mLastFrameMetricsInfos.next(); next.frameInfo = mCurrentFrameInfo; next.frameNumber = frameCompleteNr; next.surfaceId = mSurfaceControlGenerationId; @@ -816,12 +816,12 @@ void CanvasContext::reportMetricsWithPresentTime() { int32_t surfaceControlId; { // acquire lock - std::scoped_lock lock(mLast4FrameMetricsInfosMutex); - if (mLast4FrameMetricsInfos.size() != mLast4FrameMetricsInfos.capacity()) { + std::scoped_lock lock(mLastFrameMetricsInfosMutex); + if (mLastFrameMetricsInfos.size() != mLastFrameMetricsInfos.capacity()) { // Not enough frames yet return; } - auto frameMetricsInfo = mLast4FrameMetricsInfos.front(); + auto frameMetricsInfo = mLastFrameMetricsInfos.front(); forthBehind = frameMetricsInfo.frameInfo; frameNumber = frameMetricsInfo.frameNumber; surfaceControlId = frameMetricsInfo.surfaceId; @@ -869,12 +869,12 @@ void CanvasContext::removeFrameMetricsObserver(FrameMetricsObserver* observer) { } } -FrameInfo* CanvasContext::getFrameInfoFromLast4(uint64_t frameNumber, uint32_t surfaceControlId) { - std::scoped_lock lock(mLast4FrameMetricsInfosMutex); - for (size_t i = 0; i < mLast4FrameMetricsInfos.size(); i++) { - if (mLast4FrameMetricsInfos[i].frameNumber == frameNumber && - mLast4FrameMetricsInfos[i].surfaceId == surfaceControlId) { - return mLast4FrameMetricsInfos[i].frameInfo; +FrameInfo* CanvasContext::getFrameInfoFromLastFew(uint64_t frameNumber, uint32_t surfaceControlId) { + std::scoped_lock lock(mLastFrameMetricsInfosMutex); + for (size_t i = 0; i < mLastFrameMetricsInfos.size(); i++) { + if (mLastFrameMetricsInfos[i].frameNumber == frameNumber && + mLastFrameMetricsInfos[i].surfaceId == surfaceControlId) { + return mLastFrameMetricsInfos[i].frameInfo; } } @@ -894,7 +894,7 @@ void CanvasContext::onSurfaceStatsAvailable(void* context, int32_t surfaceContro } uint64_t frameNumber = functions.getFrameNumberFunc(stats); - FrameInfo* frameInfo = instance->getFrameInfoFromLast4(frameNumber, surfaceControlId); + FrameInfo* frameInfo = instance->getFrameInfoFromLastFew(frameNumber, surfaceControlId); if (frameInfo != nullptr) { std::scoped_lock lock(instance->mFrameInfoMutex); diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index e2e3fa35b9b0..cb3753822035 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -260,7 +260,7 @@ private: void finishFrame(FrameInfo* frameInfo); /** - * Invoke 'reportFrameMetrics' on the last frame stored in 'mLast4FrameInfos'. + * Invoke 'reportFrameMetrics' on the last frame stored in 'mLastFrameInfos'. * Populate the 'presentTime' field before calling. */ void reportMetricsWithPresentTime(); @@ -271,7 +271,7 @@ private: int32_t surfaceId; }; - FrameInfo* getFrameInfoFromLast4(uint64_t frameNumber, uint32_t surfaceControlId); + FrameInfo* getFrameInfoFromLastFew(uint64_t frameNumber, uint32_t surfaceControlId); Frame getFrame(); @@ -336,9 +336,9 @@ private: // List of data of frames that are awaiting GPU completion reporting. Used to compute frame // metrics and determine whether or not to report the metrics. - RingBuffer<FrameMetricsInfo, 4> mLast4FrameMetricsInfos - GUARDED_BY(mLast4FrameMetricsInfosMutex); - std::mutex mLast4FrameMetricsInfosMutex; + RingBuffer<FrameMetricsInfo, 6> mLastFrameMetricsInfos + GUARDED_BY(mLastFrameMetricsInfosMutex); + std::mutex mLastFrameMetricsInfosMutex; std::string mName; JankTracker mJankTracker; diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp index fd596d998dfd..e427c97e41fa 100644 --- a/libs/hwui/tests/common/TestContext.cpp +++ b/libs/hwui/tests/common/TestContext.cpp @@ -16,6 +16,7 @@ #include "tests/common/TestContext.h" +#include <com_android_graphics_libgui_flags.h> #include <cutils/trace.h> namespace android { @@ -101,6 +102,14 @@ void TestContext::createWindowSurface() { } void TestContext::createOffscreenSurface() { +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) + mConsumer = new BufferItemConsumer(GRALLOC_USAGE_HW_COMPOSER, 4); + const ui::Size& resolution = getActiveDisplayResolution(); + mConsumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight()); + mSurface = mConsumer->getSurface(); + mSurface->setMaxDequeuedBufferCount(3); + mSurface->setAsyncMode(true); +#else sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); @@ -110,6 +119,7 @@ void TestContext::createOffscreenSurface() { const ui::Size& resolution = getActiveDisplayResolution(); mConsumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight()); mSurface = new Surface(producer); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) } void TestContext::waitForVsync() { @@ -144,4 +154,4 @@ void TestContext::waitForVsync() { } // namespace test } // namespace uirenderer -} // namespace android +} // namespace android
\ No newline at end of file diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp index eecc741a3bbb..1afef75bc741 100644 --- a/libs/input/MouseCursorController.cpp +++ b/libs/input/MouseCursorController.cpp @@ -25,6 +25,9 @@ #include <input/Input.h> #include <log/log.h> +#define INDENT " " +#define INDENT2 " " + namespace { // Time to spend fading out the pointer completely. const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms @@ -449,6 +452,24 @@ bool MouseCursorController::resourcesLoaded() { return mLocked.resourcesLoaded; } +std::string MouseCursorController::dump() const { + std::string dump = INDENT "MouseCursorController:\n"; + std::scoped_lock lock(mLock); + dump += StringPrintf(INDENT2 "viewport: %s\n", mLocked.viewport.toString().c_str()); + dump += StringPrintf(INDENT2 "stylusHoverMode: %s\n", + mLocked.stylusHoverMode ? "true" : "false"); + dump += StringPrintf(INDENT2 "pointerFadeDirection: %d\n", mLocked.pointerFadeDirection); + dump += StringPrintf(INDENT2 "updatePointerIcon: %s\n", + mLocked.updatePointerIcon ? "true" : "false"); + dump += StringPrintf(INDENT2 "resourcesLoaded: %s\n", + mLocked.resourcesLoaded ? "true" : "false"); + dump += StringPrintf(INDENT2 "requestedPointerType: %d\n", mLocked.requestedPointerType); + dump += StringPrintf(INDENT2 "resolvedPointerType: %d\n", mLocked.resolvedPointerType); + dump += StringPrintf(INDENT2 "skipScreenshot: %s\n", mLocked.skipScreenshot ? "true" : "false"); + dump += StringPrintf(INDENT2 "animating: %s\n", mLocked.animating ? "true" : "false"); + return dump; +} + bool MouseCursorController::doAnimations(nsecs_t timestamp) { std::scoped_lock lock(mLock); bool keepFading = doFadingAnimationLocked(timestamp); diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h index 78f6413ff111..860034141a0b 100644 --- a/libs/input/MouseCursorController.h +++ b/libs/input/MouseCursorController.h @@ -67,6 +67,8 @@ public: bool resourcesLoaded(); + std::string dump() const; + private: mutable std::mutex mLock; diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 11b27a214984..5ae967bc369a 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -25,6 +25,7 @@ #include <android-base/stringprintf.h> #include <android-base/thread_annotations.h> #include <ftl/enum.h> +#include <input/PrintTools.h> #include <mutex> @@ -353,6 +354,8 @@ std::string PointerController::dump() { for (const auto& [_, spotController] : mLocked.spotControllers) { spotController.dump(dump, INDENT3); } + dump += INDENT2 "Cursor Controller:\n"; + dump += addLinePrefix(mCursorController.dump(), INDENT3); return dump; } |