286 lines
10 KiB
Java
286 lines
10 KiB
Java
/*
|
|
* 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.IntDef;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.StyleRes;
|
|
import android.annotation.SuppressLint;
|
|
import android.annotation.UiThread;
|
|
import android.app.Activity;
|
|
import android.app.ActivityOptions;
|
|
import android.app.ActivityThread;
|
|
import android.app.AppGlobals;
|
|
import android.content.Context;
|
|
import android.content.res.Resources;
|
|
import android.os.IBinder;
|
|
import android.os.RemoteException;
|
|
import android.util.Log;
|
|
import android.util.Singleton;
|
|
import android.util.Slog;
|
|
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.util.ArrayList;
|
|
|
|
/**
|
|
* The interface that apps use to talk to the splash screen.
|
|
* <p>
|
|
* Each splash screen instance is bound to a particular {@link Activity}.
|
|
* To obtain a {@link SplashScreen} for an Activity, use
|
|
* <code>Activity.getSplashScreen()</code> to get the SplashScreen.</p>
|
|
*/
|
|
public interface SplashScreen {
|
|
/**
|
|
* The splash screen style is not defined.
|
|
* @hide
|
|
*/
|
|
int SPLASH_SCREEN_STYLE_UNDEFINED = -1;
|
|
/**
|
|
* Flag to be used with {@link ActivityOptions#setSplashScreenStyle}, to avoid showing the
|
|
* splash screen icon of the launched activity
|
|
*/
|
|
int SPLASH_SCREEN_STYLE_SOLID_COLOR = 0;
|
|
/**
|
|
* Flag to be used with {@link ActivityOptions#setSplashScreenStyle}, to show the splash screen
|
|
* icon of the launched activity.
|
|
*/
|
|
int SPLASH_SCREEN_STYLE_ICON = 1;
|
|
|
|
/** @hide */
|
|
@IntDef(prefix = { "SPLASH_SCREEN_STYLE_" }, value = {
|
|
SPLASH_SCREEN_STYLE_UNDEFINED,
|
|
SPLASH_SCREEN_STYLE_SOLID_COLOR,
|
|
SPLASH_SCREEN_STYLE_ICON
|
|
})
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@interface SplashScreenStyle {}
|
|
|
|
/**
|
|
* <p>Specifies whether an {@link Activity} wants to handle the splash screen animation on its
|
|
* own. Normally the splash screen will show on screen before the content of the activity has
|
|
* been drawn, and disappear when the activity is showing on the screen. With this listener set,
|
|
* the activity will receive {@link OnExitAnimationListener#onSplashScreenExit} callback if
|
|
* splash screen is showed, then the activity can create its own exit animation based on the
|
|
* SplashScreenView.</p>
|
|
*
|
|
* <p> Note that this method must be called before splash screen leave, so it only takes effect
|
|
* during or before {@link Activity#onResume}.</p>
|
|
*
|
|
* @param listener the listener for receive the splash screen with
|
|
*
|
|
* @see OnExitAnimationListener#onSplashScreenExit(SplashScreenView)
|
|
*/
|
|
@SuppressLint("ExecutorRegistration")
|
|
void setOnExitAnimationListener(@NonNull SplashScreen.OnExitAnimationListener listener);
|
|
|
|
/**
|
|
* Clear exist listener
|
|
* @see #setOnExitAnimationListener
|
|
*/
|
|
void clearOnExitAnimationListener();
|
|
|
|
|
|
/**
|
|
* Overrides the theme used for the {@link SplashScreen}s of this application.
|
|
* <p>
|
|
* By default, the {@link SplashScreen} uses the theme set in the manifest. This method
|
|
* overrides and persists the theme used for the {@link SplashScreen} of this application.
|
|
* <p>
|
|
* To reset to the default theme, set this the themeId to {@link Resources#ID_NULL}.
|
|
* <p>
|
|
* <b>Note:</b> Internally, the theme name is resolved and persisted. This means that the theme
|
|
* name must be stable across versions, otherwise it won't be found after your application is
|
|
* updated.
|
|
*
|
|
* @param themeId The ID of the splashscreen theme to be used in place of the one defined in
|
|
* the manifest.
|
|
*/
|
|
void setSplashScreenTheme(@StyleRes int themeId);
|
|
|
|
/**
|
|
* Listens for the splash screen exit event.
|
|
*/
|
|
interface OnExitAnimationListener {
|
|
/**
|
|
* When receiving this callback, the {@link SplashScreenView} object will be drawing on top
|
|
* of the activity. The {@link SplashScreenView} represents the splash screen view
|
|
* object, developer can make an exit animation based on this view.</p>
|
|
*
|
|
* <p>This method is never invoked if your activity clear the listener by
|
|
* {@link #clearOnExitAnimationListener}.
|
|
*
|
|
* @param view The view object which on top of this Activity.
|
|
* @see #setOnExitAnimationListener
|
|
* @see #clearOnExitAnimationListener
|
|
*/
|
|
@UiThread
|
|
void onSplashScreenExit(@NonNull SplashScreenView view);
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
class SplashScreenImpl implements SplashScreen {
|
|
private static final String TAG = "SplashScreenImpl";
|
|
|
|
private OnExitAnimationListener mExitAnimationListener;
|
|
private final IBinder mActivityToken;
|
|
private final SplashScreenManagerGlobal mGlobal;
|
|
|
|
public SplashScreenImpl(Context context) {
|
|
mActivityToken = context.getActivityToken();
|
|
mGlobal = SplashScreenManagerGlobal.getInstance();
|
|
}
|
|
|
|
@Override
|
|
public void setOnExitAnimationListener(
|
|
@NonNull SplashScreen.OnExitAnimationListener listener) {
|
|
if (mActivityToken == null) {
|
|
// This is not an activity.
|
|
return;
|
|
}
|
|
synchronized (mGlobal.mGlobalLock) {
|
|
if (listener != null) {
|
|
mExitAnimationListener = listener;
|
|
mGlobal.addImpl(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void clearOnExitAnimationListener() {
|
|
if (mActivityToken == null) {
|
|
// This is not an activity.
|
|
return;
|
|
}
|
|
synchronized (mGlobal.mGlobalLock) {
|
|
mExitAnimationListener = null;
|
|
mGlobal.removeImpl(this);
|
|
}
|
|
}
|
|
|
|
public void setSplashScreenTheme(@StyleRes int themeId) {
|
|
if (mActivityToken == null) {
|
|
Log.w(TAG, "Couldn't persist the starting theme. This instance is not an Activity");
|
|
return;
|
|
}
|
|
|
|
Activity activity = ActivityThread.currentActivityThread().getActivity(
|
|
mActivityToken);
|
|
if (activity == null) {
|
|
return;
|
|
}
|
|
String themeName = themeId != Resources.ID_NULL
|
|
? activity.getResources().getResourceName(themeId) : null;
|
|
|
|
try {
|
|
AppGlobals.getPackageManager().setSplashScreenTheme(
|
|
activity.getComponentName().getPackageName(),
|
|
themeName, activity.getUserId());
|
|
} catch (RemoteException e) {
|
|
Log.w(TAG, "Couldn't persist the starting theme", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class is only used internally to manage the activities for this process.
|
|
*
|
|
* @hide
|
|
*/
|
|
class SplashScreenManagerGlobal {
|
|
private static final String TAG = SplashScreen.class.getSimpleName();
|
|
private final Object mGlobalLock = new Object();
|
|
private final ArrayList<SplashScreenImpl> mImpls = new ArrayList<>();
|
|
|
|
private SplashScreenManagerGlobal() {
|
|
ActivityThread.currentActivityThread().registerSplashScreenManager(this);
|
|
}
|
|
|
|
public static SplashScreenManagerGlobal getInstance() {
|
|
return sInstance.get();
|
|
}
|
|
|
|
private static final Singleton<SplashScreenManagerGlobal> sInstance =
|
|
new Singleton<SplashScreenManagerGlobal>() {
|
|
@Override
|
|
protected SplashScreenManagerGlobal create() {
|
|
return new SplashScreenManagerGlobal();
|
|
}
|
|
};
|
|
|
|
private void addImpl(SplashScreenImpl impl) {
|
|
synchronized (mGlobalLock) {
|
|
mImpls.add(impl);
|
|
}
|
|
}
|
|
|
|
private void removeImpl(SplashScreenImpl impl) {
|
|
synchronized (mGlobalLock) {
|
|
mImpls.remove(impl);
|
|
}
|
|
}
|
|
|
|
private SplashScreenImpl findImpl(IBinder token) {
|
|
synchronized (mGlobalLock) {
|
|
for (SplashScreenImpl impl : mImpls) {
|
|
if (impl.mActivityToken == token) {
|
|
return impl;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void tokenDestroyed(IBinder token) {
|
|
synchronized (mGlobalLock) {
|
|
final SplashScreenImpl impl = findImpl(token);
|
|
if (impl != null) {
|
|
removeImpl(impl);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void handOverSplashScreenView(@NonNull IBinder token,
|
|
@NonNull SplashScreenView splashScreenView) {
|
|
dispatchOnExitAnimation(token, splashScreenView);
|
|
}
|
|
|
|
private void dispatchOnExitAnimation(IBinder token, SplashScreenView view) {
|
|
synchronized (mGlobalLock) {
|
|
final SplashScreenImpl impl = findImpl(token);
|
|
if (impl == null) {
|
|
return;
|
|
}
|
|
if (impl.mExitAnimationListener == null) {
|
|
Slog.e(TAG, "cannot dispatch onExitAnimation to listener " + token);
|
|
return;
|
|
}
|
|
impl.mExitAnimationListener.onSplashScreenExit(view);
|
|
}
|
|
}
|
|
|
|
public boolean containsExitListener(IBinder token) {
|
|
synchronized (mGlobalLock) {
|
|
final SplashScreenImpl impl = findImpl(token);
|
|
return impl != null && impl.mExitAnimationListener != null;
|
|
}
|
|
}
|
|
}
|
|
}
|