/* * Copyright (C) 2023 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.startingsurface; import static android.graphics.Color.WHITE; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; import android.app.ActivityManager; import android.content.Context; import android.content.pm.ActivityInfo; import android.graphics.PixelFormat; import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.Binder; import android.os.SystemClock; import android.view.Display; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.WindowManager; import android.widget.FrameLayout; import android.window.SplashScreenView; import android.window.StartingWindowInfo; import android.window.StartingWindowRemovalInfo; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; class WindowlessSplashWindowCreator extends AbsSplashWindowCreator { private final TransactionPool mTransactionPool; WindowlessSplashWindowCreator(SplashscreenContentDrawer contentDrawer, Context context, ShellExecutor splashScreenExecutor, DisplayManager displayManager, StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager, TransactionPool pool) { super(contentDrawer, context, splashScreenExecutor, displayManager, startingWindowRecordManager); mTransactionPool = pool; } void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, SurfaceControl rootSurface) { final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo; final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null ? windowInfo.targetActivityInfo : taskInfo.topActivityInfo; if (activityInfo == null || activityInfo.packageName == null) { return; } final int displayId = taskInfo.displayId; final Display display = mDisplayManager.getDisplay(displayId); if (display == null) { // Can't show splash screen on requested display, so skip showing at all. return; } final Context myContext = SplashscreenContentDrawer.createContext(mContext, windowInfo, 0 /* theme */, STARTING_WINDOW_TYPE_SPLASH_SCREEN, mDisplayManager); if (myContext == null) { return; } final StartingSurfaceDrawer.WindowlessStartingWindow wlw = new StartingSurfaceDrawer.WindowlessStartingWindow( taskInfo.configuration, rootSurface); final SurfaceControlViewHost viewHost = new SurfaceControlViewHost( myContext, display, wlw, "WindowlessSplashWindowCreator"); final String title = "Windowless Splash " + taskInfo.taskId; final WindowManager.LayoutParams lp = SplashscreenContentDrawer.createLayoutParameters( myContext, windowInfo, STARTING_WINDOW_TYPE_SPLASH_SCREEN, title, PixelFormat.TRANSLUCENT, new Binder()); final Rect windowBounds = taskInfo.configuration.windowConfiguration.getBounds(); lp.width = windowBounds.width(); lp.height = windowBounds.height(); final ActivityManager.TaskDescription taskDescription; if (taskInfo.taskDescription != null) { taskDescription = taskInfo.taskDescription; } else { taskDescription = new ActivityManager.TaskDescription(); taskDescription.setBackgroundColor(WHITE); } final FrameLayout rootLayout = new FrameLayout( mSplashscreenContentDrawer.createViewContextWrapper(mContext)); viewHost.setView(rootLayout, lp); final int bgColor = taskDescription.getBackgroundColor(); final SplashScreenView splashScreenView = mSplashscreenContentDrawer .makeSimpleSplashScreenContentView(myContext, windowInfo, bgColor); rootLayout.addView(splashScreenView); final SplashWindowRecord record = new SplashWindowRecord(viewHost, splashScreenView, wlw.mChildSurface, bgColor); mStartingWindowRecordManager.addRecord(taskInfo.taskId, record); windowInfo.notifyAddComplete(wlw.mChildSurface); } private class SplashWindowRecord extends StartingSurfaceDrawer.StartingWindowRecord { private SurfaceControlViewHost mViewHost; private final long mCreateTime; private SurfaceControl mChildSurface; private final SplashScreenView mSplashView; SplashWindowRecord(SurfaceControlViewHost viewHost, SplashScreenView splashView, SurfaceControl childSurface, int bgColor) { mViewHost = viewHost; mSplashView = splashView; mChildSurface = childSurface; mBGColor = bgColor; mCreateTime = SystemClock.uptimeMillis(); } @Override public boolean removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) { if (!immediately) { mSplashscreenContentDrawer.applyExitAnimation(mSplashView, info.windowAnimationLeash, info.mainFrame, this::release, mCreateTime, 0 /* roundedCornerRadius */); } else { release(); } return true; } void release() { if (mChildSurface != null) { final SurfaceControl.Transaction t = mTransactionPool.acquire(); t.remove(mChildSurface).apply(); mTransactionPool.release(t); mChildSurface = null; } if (mViewHost != null) { mViewHost.release(); mViewHost = null; } } } }