Merge "Apply the whole auto pip transaction in recents animation" into sc-dev am: 660008c5c2
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13922011 Change-Id: Ia5d47d94f0259f26f6c225e261a52d57a19f5556
This commit is contained in:
@@ -20,6 +20,7 @@ import android.app.ActivityManager;
|
||||
import android.view.IRemoteAnimationFinishedCallback;
|
||||
import android.graphics.GraphicBuffer;
|
||||
import android.graphics.Rect;
|
||||
import android.window.PictureInPictureSurfaceTransaction;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
/**
|
||||
@@ -43,11 +44,10 @@ interface IRecentsAnimationController {
|
||||
* accordingly. This should be called before `finish`
|
||||
* @param taskId for which the leash should be updated
|
||||
* @param destinationBounds bounds of the final PiP window
|
||||
* @param windowCrop bounds to crop as part of final transform.
|
||||
* @param float9 An array of 9 floats to be used as matrix transform.
|
||||
* @param finishTransaction leash operations for the final transform.
|
||||
*/
|
||||
void setFinishTaskBounds(int taskId, in Rect destinationBounds, in Rect windowCrop,
|
||||
in float[] float9);
|
||||
void setFinishTaskBounds(int taskId, in Rect destinationBounds,
|
||||
in PictureInPictureSurfaceTransaction finishTransaction);
|
||||
|
||||
/**
|
||||
* Notifies to the system that the animation into Recents should end, and all leashes associated
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 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 android.window;
|
||||
|
||||
parcelable PictureInPictureSurfaceTransaction;
|
||||
140
core/java/android/window/PictureInPictureSurfaceTransaction.java
Normal file
140
core/java/android/window/PictureInPictureSurfaceTransaction.java
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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 android.window;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a set of {@link android.view.SurfaceControl.Transaction} operations used to
|
||||
* operate on the {@link android.view.SurfaceControl} for picture-in-picture.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class PictureInPictureSurfaceTransaction implements Parcelable {
|
||||
|
||||
public final float mPositionX;
|
||||
public final float mPositionY;
|
||||
|
||||
public final float mScaleX;
|
||||
public final float mScaleY;
|
||||
|
||||
public final float mRotation;
|
||||
public final float mCornerRadius;
|
||||
|
||||
private final Rect mWindowCrop = new Rect();
|
||||
|
||||
public PictureInPictureSurfaceTransaction(Parcel in) {
|
||||
mPositionX = in.readFloat();
|
||||
mPositionY = in.readFloat();
|
||||
mScaleX = in.readFloat();
|
||||
mScaleY = in.readFloat();
|
||||
mRotation = in.readFloat();
|
||||
mCornerRadius = in.readFloat();
|
||||
mWindowCrop.set(Objects.requireNonNull(in.readTypedObject(Rect.CREATOR)));
|
||||
}
|
||||
|
||||
public PictureInPictureSurfaceTransaction(float positionX, float positionY,
|
||||
float scaleX, float scaleY,
|
||||
float rotation, float cornerRadius,
|
||||
@Nullable Rect windowCrop) {
|
||||
mPositionX = positionX;
|
||||
mPositionY = positionY;
|
||||
mScaleX = scaleX;
|
||||
mScaleY = scaleY;
|
||||
mRotation = rotation;
|
||||
mCornerRadius = cornerRadius;
|
||||
if (windowCrop != null) {
|
||||
mWindowCrop.set(windowCrop);
|
||||
}
|
||||
}
|
||||
|
||||
public PictureInPictureSurfaceTransaction(PictureInPictureSurfaceTransaction other) {
|
||||
this(other.mPositionX, other.mPositionY,
|
||||
other.mScaleX, other.mScaleY,
|
||||
other.mRotation, other.mCornerRadius,
|
||||
other.mWindowCrop);
|
||||
}
|
||||
|
||||
public Rect getWindowCrop() {
|
||||
return new Rect(mWindowCrop);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof PictureInPictureSurfaceTransaction)) return false;
|
||||
PictureInPictureSurfaceTransaction that = (PictureInPictureSurfaceTransaction) o;
|
||||
return Objects.equals(mPositionX, that.mPositionX)
|
||||
&& Objects.equals(mPositionY, that.mPositionY)
|
||||
&& Objects.equals(mScaleX, that.mScaleX)
|
||||
&& Objects.equals(mScaleY, that.mScaleY)
|
||||
&& Objects.equals(mRotation, that.mRotation)
|
||||
&& Objects.equals(mCornerRadius, that.mCornerRadius)
|
||||
&& Objects.equals(mWindowCrop, that.mWindowCrop);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mPositionX, mPositionY, mScaleX, mScaleY,
|
||||
mRotation, mCornerRadius, mWindowCrop);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeFloat(mPositionX);
|
||||
out.writeFloat(mPositionY);
|
||||
out.writeFloat(mScaleX);
|
||||
out.writeFloat(mScaleY);
|
||||
out.writeFloat(mRotation);
|
||||
out.writeFloat(mCornerRadius);
|
||||
out.writeTypedObject(mWindowCrop, 0 /* flags */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PictureInPictureSurfaceTransaction("
|
||||
+ " posX=" + mPositionX
|
||||
+ " posY=" + mPositionY
|
||||
+ " scaleX=" + mScaleX
|
||||
+ " scaleY=" + mScaleY
|
||||
+ " rotation=" + mRotation
|
||||
+ " cornerRadius=" + mCornerRadius
|
||||
+ " crop=" + mWindowCrop
|
||||
+ ")";
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Creator<PictureInPictureSurfaceTransaction>
|
||||
CREATOR =
|
||||
new Creator<PictureInPictureSurfaceTransaction>() {
|
||||
public PictureInPictureSurfaceTransaction createFromParcel(Parcel in) {
|
||||
return new PictureInPictureSurfaceTransaction(in);
|
||||
}
|
||||
public PictureInPictureSurfaceTransaction[] newArray(int size) {
|
||||
return new PictureInPictureSurfaceTransaction[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -16,11 +16,15 @@
|
||||
|
||||
package com.android.systemui.shared.pip;
|
||||
|
||||
import static android.graphics.Matrix.MSCALE_X;
|
||||
import static android.graphics.Matrix.MSCALE_Y;
|
||||
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.view.Choreographer;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.PictureInPictureSurfaceTransaction;
|
||||
|
||||
/**
|
||||
* TODO(b/171721389): unify this class with
|
||||
@@ -28,22 +32,32 @@ import android.view.SurfaceControl;
|
||||
* source of truth on enabling/disabling and the actual value of corner radius.
|
||||
*/
|
||||
public class PipSurfaceTransactionHelper {
|
||||
/** corner radius is currently disabled. */
|
||||
private final float mCornerRadius = 0f;
|
||||
|
||||
private final Matrix mTmpTransform = new Matrix();
|
||||
private final float[] mTmpFloat9 = new float[9];
|
||||
private final RectF mTmpSourceRectF = new RectF();
|
||||
private final RectF mTmpDestinationRectF = new RectF();
|
||||
private final Rect mTmpDestinationRect = new Rect();
|
||||
|
||||
public void scale(SurfaceControl.Transaction tx, SurfaceControl leash,
|
||||
public PictureInPictureSurfaceTransaction scale(
|
||||
SurfaceControl.Transaction tx, SurfaceControl leash,
|
||||
Rect sourceBounds, Rect destinationBounds) {
|
||||
mTmpSourceRectF.set(sourceBounds);
|
||||
mTmpDestinationRectF.set(destinationBounds);
|
||||
mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
|
||||
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
|
||||
.setPosition(leash, mTmpDestinationRectF.left, mTmpDestinationRectF.top);
|
||||
.setPosition(leash, mTmpDestinationRectF.left, mTmpDestinationRectF.top)
|
||||
.setCornerRadius(leash, mCornerRadius);
|
||||
return new PictureInPictureSurfaceTransaction(
|
||||
mTmpDestinationRectF.left, mTmpDestinationRectF.top,
|
||||
mTmpFloat9[MSCALE_X], mTmpFloat9[MSCALE_Y],
|
||||
0 /* rotation*/, mCornerRadius, sourceBounds);
|
||||
}
|
||||
|
||||
public void scale(SurfaceControl.Transaction tx, SurfaceControl leash,
|
||||
public PictureInPictureSurfaceTransaction scale(
|
||||
SurfaceControl.Transaction tx, SurfaceControl leash,
|
||||
Rect sourceBounds, Rect destinationBounds,
|
||||
float degree, float positionX, float positionY) {
|
||||
mTmpSourceRectF.set(sourceBounds);
|
||||
@@ -51,10 +65,16 @@ public class PipSurfaceTransactionHelper {
|
||||
mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
|
||||
mTmpTransform.postRotate(degree, 0, 0);
|
||||
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
|
||||
.setPosition(leash, positionX, positionY);
|
||||
.setPosition(leash, positionX, positionY)
|
||||
.setCornerRadius(leash, mCornerRadius);
|
||||
return new PictureInPictureSurfaceTransaction(
|
||||
positionX, positionY,
|
||||
mTmpFloat9[MSCALE_X], mTmpFloat9[MSCALE_Y],
|
||||
degree, mCornerRadius, sourceBounds);
|
||||
}
|
||||
|
||||
public void scaleAndCrop(SurfaceControl.Transaction tx, SurfaceControl leash,
|
||||
public PictureInPictureSurfaceTransaction scaleAndCrop(
|
||||
SurfaceControl.Transaction tx, SurfaceControl leash,
|
||||
Rect sourceBounds, Rect destinationBounds, Rect insets) {
|
||||
mTmpSourceRectF.set(sourceBounds);
|
||||
mTmpDestinationRect.set(sourceBounds);
|
||||
@@ -69,10 +89,14 @@ public class PipSurfaceTransactionHelper {
|
||||
mTmpTransform.setScale(scale, scale);
|
||||
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
|
||||
.setWindowCrop(leash, mTmpDestinationRect)
|
||||
.setPosition(leash, left, top);
|
||||
.setPosition(leash, left, top)
|
||||
.setCornerRadius(leash, mCornerRadius);
|
||||
return new PictureInPictureSurfaceTransaction(
|
||||
left, top, scale, scale, 0 /* rotation */, mCornerRadius, mTmpDestinationRect);
|
||||
}
|
||||
|
||||
public void scaleAndRotate(SurfaceControl.Transaction tx, SurfaceControl leash,
|
||||
public PictureInPictureSurfaceTransaction scaleAndRotate(
|
||||
SurfaceControl.Transaction tx, SurfaceControl leash,
|
||||
Rect sourceBounds, Rect destinationBounds, Rect insets,
|
||||
float degree, float positionX, float positionY) {
|
||||
mTmpSourceRectF.set(sourceBounds);
|
||||
@@ -87,17 +111,10 @@ public class PipSurfaceTransactionHelper {
|
||||
mTmpTransform.postScale(scale, scale);
|
||||
tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
|
||||
.setWindowCrop(leash, mTmpDestinationRect)
|
||||
.setPosition(leash, positionX, positionY);
|
||||
}
|
||||
|
||||
public void resetCornerRadius(SurfaceControl.Transaction tx, SurfaceControl leash) {
|
||||
tx.setCornerRadius(leash, 0);
|
||||
}
|
||||
|
||||
public void crop(SurfaceControl.Transaction tx, SurfaceControl leash,
|
||||
Rect destinationBounds) {
|
||||
tx.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
|
||||
.setPosition(leash, destinationBounds.left, destinationBounds.top);
|
||||
.setPosition(leash, positionX, positionY)
|
||||
.setCornerRadius(leash, mCornerRadius);
|
||||
return new PictureInPictureSurfaceTransaction(
|
||||
positionX, positionY, scale, scale, degree, mCornerRadius, mTmpDestinationRect);
|
||||
}
|
||||
|
||||
/** @return {@link SurfaceControl.Transaction} instance with vsync-id */
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.graphics.Rect;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.IRecentsAnimationController;
|
||||
import android.window.PictureInPictureSurfaceTransaction;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
@@ -76,14 +77,12 @@ public class RecentsAnimationControllerCompat {
|
||||
* accordingly. This should be called before `finish`
|
||||
* @param taskId Task id of the Activity in PiP mode.
|
||||
* @param destinationBounds Bounds of the PiP window on home.
|
||||
* @param windowCrop bounds to crop as part of final transform.
|
||||
* @param float9 An array of 9 floats to be used as matrix transform.
|
||||
* @param finishTransaction leash operations for the final transform.
|
||||
*/
|
||||
public void setFinishTaskBounds(int taskId, Rect destinationBounds, Rect windowCrop,
|
||||
float[] float9) {
|
||||
public void setFinishTaskBounds(int taskId, Rect destinationBounds,
|
||||
PictureInPictureSurfaceTransaction finishTransaction) {
|
||||
try {
|
||||
mAnimationController.setFinishTaskBounds(taskId, destinationBounds, windowCrop,
|
||||
float9);
|
||||
mAnimationController.setFinishTaskBounds(taskId, destinationBounds, finishTransaction);
|
||||
} catch (RemoteException e) {
|
||||
Log.d(TAG, "Failed to set finish task bounds", e);
|
||||
}
|
||||
@@ -152,4 +151,4 @@ public class RecentsAnimationControllerCompat {
|
||||
Log.e(TAG, "Failed to detach the navigation bar from app", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.view.IRecentsAnimationController;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.IRemoteTransition;
|
||||
import android.window.IRemoteTransitionFinishedCallback;
|
||||
import android.window.PictureInPictureSurfaceTransaction;
|
||||
import android.window.TransitionFilter;
|
||||
import android.window.TransitionInfo;
|
||||
import android.window.WindowContainerToken;
|
||||
@@ -138,9 +139,9 @@ public class RemoteTransitionCompat implements Parcelable {
|
||||
}
|
||||
|
||||
@Override public void setFinishTaskBounds(int taskId, Rect destinationBounds,
|
||||
Rect windowCrop, float[] float9) {
|
||||
PictureInPictureSurfaceTransaction finishTransaction) {
|
||||
if (mWrapped != null) {
|
||||
mWrapped.setFinishTaskBounds(taskId, destinationBounds, windowCrop, float9);
|
||||
mWrapped.setFinishTaskBounds(taskId, destinationBounds, finishTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,10 +20,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
|
||||
import static android.graphics.Matrix.MSCALE_X;
|
||||
import static android.graphics.Matrix.MSCALE_Y;
|
||||
import static android.graphics.Matrix.MSKEW_X;
|
||||
import static android.graphics.Matrix.MSKEW_Y;
|
||||
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
|
||||
import static android.view.RemoteAnimationTarget.MODE_OPENING;
|
||||
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
|
||||
@@ -40,6 +36,7 @@ import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Binder;
|
||||
@@ -59,6 +56,7 @@ import android.view.RemoteAnimationTarget;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
import android.view.WindowInsets.Type;
|
||||
import android.window.PictureInPictureSurfaceTransaction;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -233,8 +231,8 @@ public class RecentsAnimationController implements DeathRecipient {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFinishTaskBounds(int taskId, Rect destinationBounds, Rect windowCrop,
|
||||
float[] float9) {
|
||||
public void setFinishTaskBounds(int taskId, Rect destinationBounds,
|
||||
PictureInPictureSurfaceTransaction finishTransaction) {
|
||||
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
|
||||
"setFinishTaskBounds(%d): bounds=%s", taskId, destinationBounds);
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
@@ -244,8 +242,7 @@ public class RecentsAnimationController implements DeathRecipient {
|
||||
final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
|
||||
if (taskAdapter.mTask.mTaskId == taskId) {
|
||||
taskAdapter.mFinishBounds.set(destinationBounds);
|
||||
taskAdapter.mFinishWindowCrop.set(windowCrop);
|
||||
taskAdapter.mFinishTransform = float9;
|
||||
taskAdapter.mFinishTransaction = finishTransaction;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1098,8 +1095,7 @@ public class RecentsAnimationController implements DeathRecipient {
|
||||
// The bounds of the target when animation is finished
|
||||
private final Rect mFinishBounds = new Rect();
|
||||
// Bounds and transform for the final transaction.
|
||||
private final Rect mFinishWindowCrop = new Rect();
|
||||
private float[] mFinishTransform;
|
||||
private PictureInPictureSurfaceTransaction mFinishTransaction;
|
||||
|
||||
TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
|
||||
mTask = task;
|
||||
@@ -1138,29 +1134,25 @@ public class RecentsAnimationController implements DeathRecipient {
|
||||
if (!mFinishBounds.isEmpty()) {
|
||||
final SurfaceControl taskSurface = mTask.mSurfaceControl;
|
||||
final Transaction pendingTransaction = mTask.getPendingTransaction();
|
||||
if (mFinishTransform != null) {
|
||||
pendingTransaction
|
||||
.setMatrix(taskSurface,
|
||||
mFinishTransform[MSCALE_X], mFinishTransform[MSKEW_Y],
|
||||
mFinishTransform[MSKEW_X], mFinishTransform[MSCALE_Y]);
|
||||
}
|
||||
float left = mFinishBounds.left;
|
||||
float top = mFinishBounds.top;
|
||||
if (!mFinishWindowCrop.isEmpty()) {
|
||||
pendingTransaction.setWindowCrop(taskSurface, mFinishWindowCrop);
|
||||
if (mFinishTransform != null) {
|
||||
// adjust the position for insets.
|
||||
left -= mFinishWindowCrop.left * mFinishTransform[MSCALE_X];
|
||||
top -= mFinishWindowCrop.top * mFinishTransform[MSCALE_Y];
|
||||
if (mFinishTransaction != null) {
|
||||
final Matrix matrix = new Matrix();
|
||||
matrix.setScale(mFinishTransaction.mScaleX, mFinishTransaction.mScaleY);
|
||||
if (mFinishTransaction.mRotation != 0) {
|
||||
matrix.postRotate(mFinishTransaction.mRotation);
|
||||
}
|
||||
pendingTransaction.setMatrix(taskSurface, matrix, new float[9])
|
||||
.setPosition(taskSurface,
|
||||
mFinishTransaction.mPositionX, mFinishTransaction.mPositionY)
|
||||
.setWindowCrop(taskSurface, mFinishTransaction.getWindowCrop())
|
||||
.setCornerRadius(taskSurface, mFinishTransaction.mCornerRadius);
|
||||
mTask.mLastRecentsAnimationBounds.set(mFinishBounds);
|
||||
mFinishTransaction = null;
|
||||
} else {
|
||||
pendingTransaction
|
||||
.setPosition(taskSurface, mFinishBounds.left, mFinishBounds.top)
|
||||
.setWindowCrop(taskSurface, mFinishBounds);
|
||||
}
|
||||
pendingTransaction
|
||||
.setPosition(taskSurface, left, top)
|
||||
.apply();
|
||||
mTask.mLastRecentsAnimationBounds.set(mFinishBounds);
|
||||
// reset the variables
|
||||
mFinishTransform = null;
|
||||
mFinishWindowCrop.setEmpty();
|
||||
pendingTransaction.apply();
|
||||
mFinishBounds.setEmpty();
|
||||
} else if (!mTask.isAttached()) {
|
||||
// Apply the task's pending transaction in case it is detached and its transaction
|
||||
|
||||
Reference in New Issue
Block a user