Merge "Fixes the flicker when transfer splash screen view to client"
This commit is contained in:
committed by
Android (Google) Code Review
commit
c16a7b7129
@@ -138,7 +138,6 @@ import android.widget.AdapterView;
|
||||
import android.widget.Toast;
|
||||
import android.widget.Toolbar;
|
||||
import android.window.SplashScreen;
|
||||
import android.window.SplashScreenView;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
@@ -955,7 +954,6 @@ public class Activity extends ContextThemeWrapper
|
||||
private UiTranslationController mUiTranslationController;
|
||||
|
||||
private SplashScreen mSplashScreen;
|
||||
private SplashScreenView mSplashScreenView;
|
||||
|
||||
private final WindowControllerCallback mWindowControllerCallback =
|
||||
new WindowControllerCallback() {
|
||||
@@ -1615,16 +1613,6 @@ public class Activity extends ContextThemeWrapper
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void setSplashScreenView(SplashScreenView v) {
|
||||
mSplashScreenView = v;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
SplashScreenView getSplashScreenView() {
|
||||
return mSplashScreenView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #onCreate(android.os.Bundle)} but called for those activities created with
|
||||
* the attribute {@link android.R.attr#persistableMode} set to
|
||||
|
||||
@@ -166,6 +166,7 @@ import android.view.Choreographer;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayAdjustments;
|
||||
import android.view.DisplayAdjustments.FixedRotationAdjustments;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.ThreadedRenderer;
|
||||
import android.view.View;
|
||||
import android.view.ViewDebug;
|
||||
@@ -235,7 +236,6 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
@@ -4074,10 +4074,11 @@ public final class ActivityThread extends ClientTransactionHandler
|
||||
|
||||
@Override
|
||||
public void handleAttachSplashScreenView(@NonNull ActivityClientRecord r,
|
||||
@Nullable SplashScreenView.SplashScreenViewParcelable parcelable) {
|
||||
@Nullable SplashScreenView.SplashScreenViewParcelable parcelable,
|
||||
@NonNull SurfaceControl startingWindowLeash) {
|
||||
final DecorView decorView = (DecorView) r.window.peekDecorView();
|
||||
if (parcelable != null && decorView != null) {
|
||||
createSplashScreen(r, decorView, parcelable);
|
||||
createSplashScreen(r, decorView, parcelable, startingWindowLeash);
|
||||
} else {
|
||||
// shouldn't happen!
|
||||
Slog.e(TAG, "handleAttachSplashScreenView failed, unable to attach");
|
||||
@@ -4085,61 +4086,50 @@ public final class ActivityThread extends ClientTransactionHandler
|
||||
}
|
||||
|
||||
private void createSplashScreen(ActivityClientRecord r, DecorView decorView,
|
||||
SplashScreenView.SplashScreenViewParcelable parcelable) {
|
||||
SplashScreenView.SplashScreenViewParcelable parcelable,
|
||||
@NonNull SurfaceControl startingWindowLeash) {
|
||||
final SplashScreenView.Builder builder = new SplashScreenView.Builder(r.activity);
|
||||
final SplashScreenView view = builder.createFromParcel(parcelable).build();
|
||||
decorView.addView(view);
|
||||
view.attachHostActivityAndSetSystemUIColors(r.activity, r.window);
|
||||
view.requestLayout();
|
||||
// Ensure splash screen view is shown before remove the splash screen window.
|
||||
final ViewRootImpl impl = decorView.getViewRootImpl();
|
||||
final boolean hardwareEnabled = impl != null && impl.isHardwareEnabled();
|
||||
final AtomicBoolean notified = new AtomicBoolean();
|
||||
if (hardwareEnabled) {
|
||||
final Runnable frameCommit = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
view.post(() -> {
|
||||
if (!notified.get()) {
|
||||
view.getViewTreeObserver().unregisterFrameCommitCallback(this);
|
||||
ActivityClient.getInstance().reportSplashScreenAttached(
|
||||
r.token);
|
||||
notified.set(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
view.getViewTreeObserver().registerFrameCommitCallback(frameCommit);
|
||||
} else {
|
||||
final ViewTreeObserver.OnDrawListener onDrawListener =
|
||||
new ViewTreeObserver.OnDrawListener() {
|
||||
@Override
|
||||
public void onDraw() {
|
||||
view.post(() -> {
|
||||
if (!notified.get()) {
|
||||
view.getViewTreeObserver().removeOnDrawListener(this);
|
||||
ActivityClient.getInstance().reportSplashScreenAttached(
|
||||
r.token);
|
||||
notified.set(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
view.getViewTreeObserver().addOnDrawListener(onDrawListener);
|
||||
|
||||
view.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
|
||||
@Override
|
||||
public void onDraw() {
|
||||
// Transfer the splash screen view from shell to client.
|
||||
// Call syncTransferSplashscreenViewTransaction at the first onDraw so we can ensure
|
||||
// the client view is ready to show and we can use applyTransactionOnDraw to make
|
||||
// all transitions happen at the same frame.
|
||||
syncTransferSplashscreenViewTransaction(
|
||||
view, r.token, decorView, startingWindowLeash);
|
||||
view.postOnAnimation(() -> view.getViewTreeObserver().removeOnDrawListener(this));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void reportSplashscreenViewShown(IBinder token, SplashScreenView view) {
|
||||
ActivityClient.getInstance().reportSplashScreenAttached(token);
|
||||
synchronized (this) {
|
||||
if (mSplashScreenGlobal != null) {
|
||||
mSplashScreenGlobal.handOverSplashScreenView(token, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handOverSplashScreenView(@NonNull ActivityClientRecord r) {
|
||||
final SplashScreenView v = r.activity.getSplashScreenView();
|
||||
if (v == null) {
|
||||
return;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (mSplashScreenGlobal != null) {
|
||||
mSplashScreenGlobal.handOverSplashScreenView(r.token, v);
|
||||
}
|
||||
}
|
||||
private void syncTransferSplashscreenViewTransaction(SplashScreenView view, IBinder token,
|
||||
View decorView, @NonNull SurfaceControl startingWindowLeash) {
|
||||
// Ensure splash screen view is shown before remove the splash screen window.
|
||||
// Once the copied splash screen view is onDrawn on decor view, use applyTransactionOnDraw
|
||||
// to ensure the transfer of surface view and hide starting window are happen at the same
|
||||
// frame.
|
||||
final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
|
||||
transaction.hide(startingWindowLeash);
|
||||
|
||||
decorView.getViewRootImpl().applyTransactionOnDraw(transaction);
|
||||
view.syncTransferSurfaceOnDraw();
|
||||
// Tell server we can remove the starting window
|
||||
decorView.postOnAnimation(() -> reportSplashscreenViewShown(token, view));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.content.res.Configuration;
|
||||
import android.os.IBinder;
|
||||
import android.util.MergedConfiguration;
|
||||
import android.view.DisplayAdjustments.FixedRotationAdjustments;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.SplashScreenView.SplashScreenViewParcelable;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -165,10 +166,8 @@ public abstract class ClientTransactionHandler {
|
||||
|
||||
/** Attach a splash screen window view to the top of the activity */
|
||||
public abstract void handleAttachSplashScreenView(@NonNull ActivityClientRecord r,
|
||||
@NonNull SplashScreenViewParcelable parcelable);
|
||||
|
||||
/** Hand over the splash screen window view to the activity */
|
||||
public abstract void handOverSplashScreenView(@NonNull ActivityClientRecord r);
|
||||
@NonNull SplashScreenViewParcelable parcelable,
|
||||
@NonNull SurfaceControl startingWindowLeash);
|
||||
|
||||
/** Perform activity launch. */
|
||||
public abstract Activity handleLaunchActivity(@NonNull ActivityClientRecord r,
|
||||
|
||||
@@ -16,17 +16,14 @@
|
||||
|
||||
package android.app.servertransaction;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityThread;
|
||||
import android.app.ClientTransactionHandler;
|
||||
import android.os.Parcel;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.SplashScreenView.SplashScreenViewParcelable;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Transfer a splash screen view to an Activity.
|
||||
* @hide
|
||||
@@ -34,31 +31,13 @@ import java.lang.annotation.RetentionPolicy;
|
||||
public class TransferSplashScreenViewStateItem extends ActivityTransactionItem {
|
||||
|
||||
private SplashScreenViewParcelable mSplashScreenViewParcelable;
|
||||
private @TransferRequest int mRequest;
|
||||
|
||||
@IntDef(value = {
|
||||
ATTACH_TO,
|
||||
HANDOVER_TO
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface TransferRequest {}
|
||||
// request client to attach the view on it.
|
||||
public static final int ATTACH_TO = 0;
|
||||
// tell client that you can handle the splash screen view.
|
||||
public static final int HANDOVER_TO = 1;
|
||||
private SurfaceControl mStartingWindowLeash;
|
||||
|
||||
@Override
|
||||
public void execute(@NonNull ClientTransactionHandler client,
|
||||
@NonNull ActivityThread.ActivityClientRecord r,
|
||||
PendingTransactionActions pendingActions) {
|
||||
switch (mRequest) {
|
||||
case ATTACH_TO:
|
||||
client.handleAttachSplashScreenView(r, mSplashScreenViewParcelable);
|
||||
break;
|
||||
case HANDOVER_TO:
|
||||
client.handOverSplashScreenView(r);
|
||||
break;
|
||||
}
|
||||
client.handleAttachSplashScreenView(r, mSplashScreenViewParcelable, mStartingWindowLeash);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -68,26 +47,27 @@ public class TransferSplashScreenViewStateItem extends ActivityTransactionItem {
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mRequest);
|
||||
dest.writeTypedObject(mSplashScreenViewParcelable, flags);
|
||||
dest.writeTypedObject(mStartingWindowLeash, flags);
|
||||
}
|
||||
|
||||
private TransferSplashScreenViewStateItem() {}
|
||||
private TransferSplashScreenViewStateItem(Parcel in) {
|
||||
mRequest = in.readInt();
|
||||
mSplashScreenViewParcelable = in.readTypedObject(SplashScreenViewParcelable.CREATOR);
|
||||
mStartingWindowLeash = in.readTypedObject(SurfaceControl.CREATOR);
|
||||
}
|
||||
|
||||
/** Obtain an instance initialized with provided params. */
|
||||
public static TransferSplashScreenViewStateItem obtain(@TransferRequest int state,
|
||||
@Nullable SplashScreenViewParcelable parcelable) {
|
||||
public static TransferSplashScreenViewStateItem obtain(
|
||||
@Nullable SplashScreenViewParcelable parcelable,
|
||||
@Nullable SurfaceControl startingWindowLeash) {
|
||||
TransferSplashScreenViewStateItem instance =
|
||||
ObjectPool.obtain(TransferSplashScreenViewStateItem.class);
|
||||
if (instance == null) {
|
||||
instance = new TransferSplashScreenViewStateItem();
|
||||
}
|
||||
instance.mRequest = state;
|
||||
instance.mSplashScreenViewParcelable = parcelable;
|
||||
instance.mStartingWindowLeash = startingWindowLeash;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -1889,6 +1889,24 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
|
||||
* @param p The SurfacePackage to embed.
|
||||
*/
|
||||
public void setChildSurfacePackage(@NonNull SurfaceControlViewHost.SurfacePackage p) {
|
||||
setChildSurfacePackage(p, false /* applyTransactionOnDraw */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to setChildSurfacePackage, but using the BLAST queue so the transaction can be
|
||||
* synchronized with the ViewRootImpl frame.
|
||||
* @hide
|
||||
*/
|
||||
public void setChildSurfacePackageOnDraw(
|
||||
@NonNull SurfaceControlViewHost.SurfacePackage p) {
|
||||
setChildSurfacePackage(p, true /* applyTransactionOnDraw */);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param applyTransactionOnDraw Whether to apply transaction at onDraw or immediately.
|
||||
*/
|
||||
private void setChildSurfacePackage(
|
||||
@NonNull SurfaceControlViewHost.SurfacePackage p, boolean applyTransactionOnDraw) {
|
||||
final SurfaceControl lastSc = mSurfacePackage != null ?
|
||||
mSurfacePackage.getSurfaceControl() : null;
|
||||
if (mSurfaceControl != null) {
|
||||
@@ -1896,13 +1914,20 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
|
||||
mTmpTransaction.reparent(lastSc, null);
|
||||
mSurfacePackage.release();
|
||||
}
|
||||
|
||||
reparentSurfacePackage(mTmpTransaction, p);
|
||||
mTmpTransaction.apply();
|
||||
applyTransaction(applyTransactionOnDraw);
|
||||
}
|
||||
mSurfacePackage = p;
|
||||
}
|
||||
|
||||
private void applyTransaction(boolean applyTransactionOnDraw) {
|
||||
if (applyTransactionOnDraw) {
|
||||
getViewRootImpl().applyTransactionOnDraw(mTmpTransaction);
|
||||
} else {
|
||||
mTmpTransaction.apply();
|
||||
}
|
||||
}
|
||||
|
||||
private void reparentSurfacePackage(SurfaceControl.Transaction t,
|
||||
SurfaceControlViewHost.SurfacePackage p) {
|
||||
final SurfaceControl sc = p.getSurfaceControl();
|
||||
|
||||
@@ -241,7 +241,6 @@ public interface SplashScreen {
|
||||
|
||||
public void handOverSplashScreenView(@NonNull IBinder token,
|
||||
@NonNull SplashScreenView splashScreenView) {
|
||||
transferSurface(splashScreenView);
|
||||
dispatchOnExitAnimation(token, splashScreenView);
|
||||
}
|
||||
|
||||
@@ -265,9 +264,5 @@ public interface SplashScreen {
|
||||
return impl != null && impl.mExitAnimationListener != null;
|
||||
}
|
||||
}
|
||||
|
||||
private void transferSurface(@NonNull SplashScreenView splashScreenView) {
|
||||
splashScreenView.transferSurface();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,7 +464,10 @@ public final class SplashScreenView extends FrameLayout {
|
||||
}
|
||||
|
||||
|
||||
void transferSurface() {
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void syncTransferSurfaceOnDraw() {
|
||||
if (mSurfacePackage == null) {
|
||||
return;
|
||||
}
|
||||
@@ -474,8 +477,8 @@ public final class SplashScreenView extends FrameLayout {
|
||||
String.format("SurfacePackage'surface reparented to %s", parent)));
|
||||
Log.d(TAG, "Transferring surface " + mSurfaceView.toString());
|
||||
}
|
||||
mSurfaceView.setChildSurfacePackage(mSurfacePackage);
|
||||
|
||||
mSurfaceView.setChildSurfacePackageOnDraw(mSurfacePackage);
|
||||
}
|
||||
|
||||
void initIconAnimation(Drawable iconDrawable, long duration) {
|
||||
@@ -533,10 +536,6 @@ public final class SplashScreenView extends FrameLayout {
|
||||
restoreSystemUIColors();
|
||||
mWindow = null;
|
||||
}
|
||||
if (mHostActivity != null) {
|
||||
mHostActivity.setSplashScreenView(null);
|
||||
mHostActivity = null;
|
||||
}
|
||||
mHasRemoved = true;
|
||||
}
|
||||
|
||||
@@ -582,7 +581,6 @@ public final class SplashScreenView extends FrameLayout {
|
||||
* @hide
|
||||
*/
|
||||
public void attachHostActivityAndSetSystemUIColors(Activity activity, Window window) {
|
||||
activity.setSplashScreenView(this);
|
||||
mHostActivity = activity;
|
||||
mWindow = window;
|
||||
final WindowManager.LayoutParams attr = window.getAttributes();
|
||||
|
||||
@@ -46,8 +46,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
import static android.app.WindowConfiguration.activityTypeToString;
|
||||
import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
|
||||
import static android.app.servertransaction.TransferSplashScreenViewStateItem.ATTACH_TO;
|
||||
import static android.app.servertransaction.TransferSplashScreenViewStateItem.HANDOVER_TO;
|
||||
import static android.content.Intent.ACTION_MAIN;
|
||||
import static android.content.Intent.CATEGORY_HOME;
|
||||
import static android.content.Intent.CATEGORY_LAUNCHER;
|
||||
@@ -2326,7 +2324,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
// unable to copy from shell, maybe it's not a splash screen. or something went wrong.
|
||||
// either way, abort and reset the sequence.
|
||||
if (parcelable == null
|
||||
|| mTransferringSplashScreenState != TRANSFER_SPLASH_SCREEN_COPYING) {
|
||||
|| mTransferringSplashScreenState != TRANSFER_SPLASH_SCREEN_COPYING
|
||||
|| mStartingWindow == null) {
|
||||
if (parcelable != null) {
|
||||
parcelable.clearIfNeeded();
|
||||
}
|
||||
@@ -2335,13 +2334,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
return;
|
||||
}
|
||||
// schedule attach splashScreen to client
|
||||
final SurfaceControl windowAnimationLeash = TaskOrganizerController
|
||||
.applyStartingWindowAnimation(mStartingWindow);
|
||||
try {
|
||||
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT;
|
||||
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
|
||||
TransferSplashScreenViewStateItem.obtain(ATTACH_TO, parcelable));
|
||||
TransferSplashScreenViewStateItem.obtain(parcelable,
|
||||
windowAnimationLeash));
|
||||
scheduleTransferSplashScreenTimeout();
|
||||
} catch (Exception e) {
|
||||
Slog.w(TAG, "onCopySplashScreenComplete fail: " + this);
|
||||
mStartingWindow.cancelAnimation();
|
||||
parcelable.clearIfNeeded();
|
||||
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
|
||||
}
|
||||
@@ -2351,14 +2354,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
removeTransferSplashScreenTimeout();
|
||||
// Client has draw the splash screen, so we can remove the starting window.
|
||||
if (mStartingWindow != null) {
|
||||
mStartingWindow.cancelAnimation();
|
||||
mStartingWindow.hide(false, false);
|
||||
}
|
||||
try {
|
||||
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
|
||||
TransferSplashScreenViewStateItem.obtain(HANDOVER_TO, null));
|
||||
} catch (Exception e) {
|
||||
Slog.w(TAG, "onSplashScreenAttachComplete fail: " + this);
|
||||
}
|
||||
// no matter what, remove the starting window.
|
||||
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
|
||||
removeStartingWindowAnimation(false /* prepareAnimation */);
|
||||
|
||||
@@ -422,8 +422,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
}
|
||||
|
||||
// Capture the animation surface control for activity's main window
|
||||
private static class StartingWindowAnimationAdaptor implements AnimationAdapter {
|
||||
private SurfaceControl mAnimationLeash;
|
||||
static class StartingWindowAnimationAdaptor implements AnimationAdapter {
|
||||
SurfaceControl mAnimationLeash;
|
||||
@Override
|
||||
public boolean getShowWallpaper() {
|
||||
return false;
|
||||
@@ -464,6 +464,13 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
static SurfaceControl applyStartingWindowAnimation(WindowContainer window) {
|
||||
final StartingWindowAnimationAdaptor adaptor = new StartingWindowAnimationAdaptor();
|
||||
window.startAnimation(window.getPendingTransaction(), adaptor, false,
|
||||
ANIMATION_TYPE_STARTING_REVEAL);
|
||||
return adaptor.mAnimationLeash;
|
||||
}
|
||||
|
||||
boolean addStartingWindow(Task task, ActivityRecord activity, int launchTheme,
|
||||
TaskSnapshot taskSnapshot) {
|
||||
final Task rootTask = task.getRootTask();
|
||||
@@ -507,12 +514,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
final WindowState mainWindow =
|
||||
topActivity.findMainWindow(false/* includeStartingApp */);
|
||||
if (mainWindow != null) {
|
||||
final StartingWindowAnimationAdaptor adaptor =
|
||||
new StartingWindowAnimationAdaptor();
|
||||
final SurfaceControl.Transaction t = mainWindow.getPendingTransaction();
|
||||
mainWindow.startAnimation(t, adaptor, false,
|
||||
ANIMATION_TYPE_STARTING_REVEAL);
|
||||
windowAnimationLeash = adaptor.mAnimationLeash;
|
||||
windowAnimationLeash = applyStartingWindowAnimation(mainWindow);
|
||||
mainFrame = mainWindow.getRelativeFrame();
|
||||
t.setPosition(windowAnimationLeash, mainFrame.left, mainFrame.top);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user