fix: Conflict incoming changes from 15-dev
This commit is contained in:
+11
@@ -33,11 +33,22 @@ class DisableSubpixelTextTransitionListener(private val rootView: ViewGroup?) :
|
||||
|
||||
override fun onTransitionStarted() {
|
||||
isTransitionInProgress = true
|
||||
getAllChildTextView(rootView, childrenTextViews)
|
||||
childrenTextViews.forEach { child ->
|
||||
val childTextView = child.get() ?: return@forEach
|
||||
childTextView.paintFlags = childTextView.paintFlags or Paint.SUBPIXEL_TEXT_FLAG
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTransitionFinished() {
|
||||
if (!isTransitionInProgress) return
|
||||
isTransitionInProgress = false
|
||||
childrenTextViews.forEach { child ->
|
||||
val childTextView = child.get() ?: return@forEach
|
||||
childTextView.paintFlags =
|
||||
childTextView.paintFlags and Paint.SUBPIXEL_TEXT_FLAG.inv()
|
||||
}
|
||||
childrenTextViews.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.systemui.shared.navigationbar;
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.companion.virtualdevice.flags.Flags;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.CanvasProperty;
|
||||
@@ -80,6 +81,7 @@ public class KeyButtonRipple extends Drawable {
|
||||
private final Interpolator mInterpolator = new LogInterpolator();
|
||||
private boolean mSupportHardware;
|
||||
private final View mTargetView;
|
||||
private final int mTapTimeoutMillis;
|
||||
private final Handler mHandler = new Handler();
|
||||
|
||||
private final HashSet<Animator> mRunningAnimations = new HashSet<>();
|
||||
@@ -101,6 +103,9 @@ public class KeyButtonRipple extends Drawable {
|
||||
mMaxWidthResource = maxWidthResource;
|
||||
mMaxWidth = ctx.getResources().getDimensionPixelSize(maxWidthResource);
|
||||
mTargetView = targetView;
|
||||
mTapTimeoutMillis = Flags.viewconfigurationApis()
|
||||
? ViewConfiguration.get(mTargetView.getContext()).getTapTimeoutMillis()
|
||||
: ViewConfiguration.getTapTimeout();
|
||||
}
|
||||
|
||||
public void updateResources() {
|
||||
@@ -338,7 +343,7 @@ public class KeyButtonRipple extends Drawable {
|
||||
if (mDelayTouchFeedback) {
|
||||
if (mRunningAnimations.isEmpty()) {
|
||||
mHandler.removeCallbacksAndMessages(null);
|
||||
mHandler.postDelayed(this::enterSoftware, ViewConfiguration.getTapTimeout());
|
||||
mHandler.postDelayed(this::enterSoftware, mTapTimeoutMillis);
|
||||
} else if (mVisible) {
|
||||
enterSoftware();
|
||||
}
|
||||
@@ -383,7 +388,7 @@ public class KeyButtonRipple extends Drawable {
|
||||
if (mDelayTouchFeedback) {
|
||||
if (mRunningAnimations.isEmpty()) {
|
||||
mHandler.removeCallbacksAndMessages(null);
|
||||
mHandler.postDelayed(this::enterHardware, ViewConfiguration.getTapTimeout());
|
||||
mHandler.postDelayed(this::enterHardware, mTapTimeoutMillis);
|
||||
} else if (mVisible) {
|
||||
enterHardware();
|
||||
}
|
||||
|
||||
@@ -31,11 +31,13 @@ import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
|
||||
import com.android.systemui.log.LogcatOnlyMessageBuffer;
|
||||
import com.android.systemui.log.core.LogLevel;
|
||||
import com.android.systemui.log.core.Logger;
|
||||
import com.android.systemui.plugins.Plugin;
|
||||
import com.android.systemui.plugins.PluginListener;
|
||||
import com.android.systemui.plugins.PluginManager;
|
||||
@@ -55,8 +57,6 @@ import java.util.concurrent.Executor;
|
||||
*/
|
||||
public class PluginActionManager<T extends Plugin> {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static final String TAG = "PluginActionManager";
|
||||
public static final String PLUGIN_PERMISSION = "com.android.systemui.permission.PLUGIN";
|
||||
|
||||
@@ -76,6 +76,7 @@ public class PluginActionManager<T extends Plugin> {
|
||||
private final Class<T> mPluginClass;
|
||||
private final Executor mMainExecutor;
|
||||
private final Executor mBgExecutor;
|
||||
private final Logger mLogger;
|
||||
|
||||
private PluginActionManager(
|
||||
Context context,
|
||||
@@ -104,17 +105,21 @@ public class PluginActionManager<T extends Plugin> {
|
||||
mPluginInstanceFactory = pluginInstanceFactory;
|
||||
mPrivilegedPlugins.addAll(privilegedPlugins);
|
||||
mIsDebuggable = debuggable;
|
||||
|
||||
String tag = String.format("%s[%s]", TAG, mAction);
|
||||
mLogger = new Logger(mListener.getLogBuffer() != null ? mListener.getLogBuffer() :
|
||||
new LogcatOnlyMessageBuffer(LogLevel.WARNING), tag);
|
||||
}
|
||||
|
||||
/** Load all plugins matching this instance's action. */
|
||||
public void loadAll() {
|
||||
if (DEBUG) Log.d(TAG, "startListening");
|
||||
mLogger.d("startListening");
|
||||
mBgExecutor.execute(() -> queryAll());
|
||||
}
|
||||
|
||||
/** Unload all plugins managed by this instance. */
|
||||
public void destroy() {
|
||||
if (DEBUG) Log.d(TAG, "stopListening");
|
||||
mLogger.d("stopListening");
|
||||
ArrayList<PluginInstance<T>> plugins = new ArrayList<>(mPluginInstances);
|
||||
for (PluginInstance<T> plugInstance : plugins) {
|
||||
mMainExecutor.execute(() -> onPluginDisconnected(plugInstance));
|
||||
@@ -185,7 +190,7 @@ public class PluginActionManager<T extends Plugin> {
|
||||
// Don't disable privileged plugins as they are a part of the OS.
|
||||
return false;
|
||||
}
|
||||
Log.w(TAG, "Disabling plugin " + pluginComponent.flattenToShortString());
|
||||
logFmt(LogLevel.WARNING, "Disabling plugin: %s", pluginComponent.flattenToShortString());
|
||||
mPluginEnabler.setDisabled(pluginComponent, reason);
|
||||
|
||||
return true;
|
||||
@@ -208,18 +213,18 @@ public class PluginActionManager<T extends Plugin> {
|
||||
}
|
||||
|
||||
private void onPluginConnected(PluginInstance<T> pluginInstance) {
|
||||
if (DEBUG) Log.d(TAG, "onPluginConnected");
|
||||
mLogger.d("onPluginConnected");
|
||||
PluginPrefs.setHasPlugins(mContext);
|
||||
pluginInstance.onCreate();
|
||||
}
|
||||
|
||||
private void onPluginDisconnected(PluginInstance<T> pluginInstance) {
|
||||
if (DEBUG) Log.d(TAG, "onPluginDisconnected");
|
||||
mLogger.d("onPluginDisconnected");
|
||||
pluginInstance.onDestroy();
|
||||
}
|
||||
|
||||
private void queryAll() {
|
||||
if (DEBUG) Log.d(TAG, "queryAll " + mAction);
|
||||
mLogger.d("queryAll");
|
||||
for (int i = mPluginInstances.size() - 1; i >= 0; i--) {
|
||||
PluginInstance<T> pluginInstance = mPluginInstances.get(i);
|
||||
mMainExecutor.execute(() -> onPluginDisconnected(pluginInstance));
|
||||
@@ -239,11 +244,11 @@ public class PluginActionManager<T extends Plugin> {
|
||||
}
|
||||
|
||||
private void queryPkg(String pkg) {
|
||||
if (DEBUG) Log.d(TAG, "queryPkg " + pkg + " " + mAction);
|
||||
logFmt(LogLevel.DEBUG, "queryPkg(%s)", pkg);
|
||||
if (mAllowMultiple || (mPluginInstances.size() == 0)) {
|
||||
handleQueryPlugins(pkg);
|
||||
} else {
|
||||
if (DEBUG) Log.d(TAG, "Too many of " + mAction);
|
||||
mLogger.d("Too many matching packages found");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,23 +260,23 @@ public class PluginActionManager<T extends Plugin> {
|
||||
intent.setPackage(pkgName);
|
||||
}
|
||||
List<ResolveInfo> result = mPm.queryIntentServices(intent, 0);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Found " + result.size() + " plugins");
|
||||
for (ResolveInfo info : result) {
|
||||
ComponentName name = new ComponentName(info.serviceInfo.packageName,
|
||||
info.serviceInfo.name);
|
||||
Log.d(TAG, " " + name);
|
||||
}
|
||||
}
|
||||
StringBuilder logSb = new StringBuilder();
|
||||
|
||||
logSb.append("Found ");
|
||||
logSb.append(result.size());
|
||||
logSb.append(" plugins");
|
||||
|
||||
if (result.size() > 1 && !mAllowMultiple) {
|
||||
// TODO: Show warning.
|
||||
Log.w(TAG, "Multiple plugins found for " + mAction);
|
||||
logSb.append(", but multiple plugins are disallowed.");
|
||||
logFmt(LogLevel.WARNING, "%s", logSb);
|
||||
return;
|
||||
}
|
||||
|
||||
for (ResolveInfo info : result) {
|
||||
ComponentName name = new ComponentName(info.serviceInfo.packageName,
|
||||
info.serviceInfo.name);
|
||||
logSb.append("\n " + name);
|
||||
PluginInstance<T> pluginInstance = loadPluginComponent(name);
|
||||
if (pluginInstance != null) {
|
||||
// add plugin before sending PLUGIN_CONNECTED message
|
||||
@@ -279,6 +284,7 @@ public class PluginActionManager<T extends Plugin> {
|
||||
mMainExecutor.execute(() -> onPluginConnected(pluginInstance));
|
||||
}
|
||||
}
|
||||
logFmt(LogLevel.DEBUG, "%s", logSb);
|
||||
}
|
||||
|
||||
private PluginInstance<T> loadPluginComponent(ComponentName component) {
|
||||
@@ -286,13 +292,11 @@ public class PluginActionManager<T extends Plugin> {
|
||||
// use these on production builds.
|
||||
if (!mIsDebuggable && !isPluginPrivileged(component)) {
|
||||
// Never ever ever allow these on production builds, they are only for prototyping.
|
||||
Log.w(TAG, "Plugin cannot be loaded on production build: " + component);
|
||||
logFmt(LogLevel.ERROR, "Plugin cannot be loaded on production build: %s", component);
|
||||
return null;
|
||||
}
|
||||
if (!mPluginEnabler.isEnabled(component)) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Plugin is not enabled, aborting load: " + component);
|
||||
}
|
||||
logFmt(LogLevel.WARNING, "Plugin is not enabled, aborting load: %s", component);
|
||||
return null;
|
||||
}
|
||||
String packageName = component.getPackageName();
|
||||
@@ -300,16 +304,15 @@ public class PluginActionManager<T extends Plugin> {
|
||||
// TODO: This probably isn't needed given that we don't have IGNORE_SECURITY on
|
||||
if (mPm.checkPermission(PLUGIN_PERMISSION, packageName)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
Log.d(TAG, "Plugin doesn't have permission: " + packageName);
|
||||
logFmt(LogLevel.ERROR, "Plugin doesn't have permission: %s", packageName);
|
||||
return null;
|
||||
}
|
||||
|
||||
ApplicationInfo appInfo = mPm.getApplicationInfo(packageName, 0);
|
||||
// TODO: Only create the plugin before version check if we need it for
|
||||
// legacy version check.
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "createPlugin: " + component);
|
||||
}
|
||||
// TODO: Only create the plugin before version
|
||||
// check if we need it for legacy version check.
|
||||
logFmt(LogLevel.DEBUG, "createPlugin: %s", component);
|
||||
|
||||
try {
|
||||
return mPluginInstanceFactory.create(
|
||||
mContext, appInfo, component,
|
||||
@@ -318,7 +321,7 @@ public class PluginActionManager<T extends Plugin> {
|
||||
reportInvalidVersion(component, component.getClassName(), e);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Log.w(TAG, "Couldn't load plugin: " + component, e);
|
||||
logFmt(LogLevel.ERROR, "Couldn't load plugin: %s", component, e);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -363,12 +366,31 @@ public class PluginActionManager<T extends Plugin> {
|
||||
nb.addAction(new Action.Builder(null, "Disable plugin", pi).build());
|
||||
mNotificationManager.notify(SystemMessage.NOTE_PLUGIN, nb.build());
|
||||
// TODO: Warn user.
|
||||
Log.w(TAG, "Error loading plugin; " + e.getMessage());
|
||||
mLogger.e("Error loading plugin", e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a {@link PluginActionManager}
|
||||
*/
|
||||
/** Format a log message */
|
||||
public void logFmt(LogLevel level, String formatStr, Object innerObj) {
|
||||
logFmt(level, formatStr, innerObj, null);
|
||||
}
|
||||
|
||||
/** Format a log message */
|
||||
public void logFmt(LogLevel level, String formatStr, Object innerObj, Throwable ex) {
|
||||
logFmt(mLogger, level, formatStr, innerObj, ex);
|
||||
}
|
||||
|
||||
/** Format a log message */
|
||||
public static void logFmt(Logger logger, LogLevel level,
|
||||
String formatStr, Object innerObj, Throwable ex) {
|
||||
logger.log(
|
||||
level, msg -> String.format(formatStr, msg.getStr1()),
|
||||
ex, msg -> {
|
||||
msg.setStr1(innerObj.toString());
|
||||
return kotlin.Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
/** Construct a {@link PluginActionManager} */
|
||||
public static class Factory {
|
||||
private final Context mContext;
|
||||
private final PackageManager mPackageManager;
|
||||
@@ -403,7 +425,7 @@ public class PluginActionManager<T extends Plugin> {
|
||||
}
|
||||
}
|
||||
|
||||
/** */
|
||||
/** Wrapper for PluginInstance contexts */
|
||||
public static class PluginContextWrapper extends ContextWrapper {
|
||||
private final ClassLoader mClassLoader;
|
||||
private LayoutInflater mInflater;
|
||||
|
||||
@@ -28,6 +28,9 @@ import android.util.Log;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.log.LogcatOnlyMessageBuffer;
|
||||
import com.android.systemui.log.core.LogLevel;
|
||||
import com.android.systemui.log.core.Logger;
|
||||
import com.android.systemui.plugins.Plugin;
|
||||
import com.android.systemui.plugins.PluginFragment;
|
||||
import com.android.systemui.plugins.PluginLifecycleManager;
|
||||
@@ -61,13 +64,14 @@ public class PluginInstance<T extends Plugin>
|
||||
private final ComponentName mComponentName;
|
||||
private final PluginFactory<T> mPluginFactory;
|
||||
private final String mTag;
|
||||
private final Logger mLogger;
|
||||
|
||||
private boolean mHasError = false;
|
||||
private BiConsumer<String, String> mLogConsumer = null;
|
||||
private Context mPluginContext;
|
||||
private T mPlugin;
|
||||
|
||||
/** */
|
||||
/** Constructor */
|
||||
public PluginInstance(
|
||||
Context appContext,
|
||||
PluginListener<T> listener,
|
||||
@@ -79,8 +83,9 @@ public class PluginInstance<T extends Plugin>
|
||||
mComponentName = componentName;
|
||||
mPluginFactory = pluginFactory;
|
||||
mPlugin = plugin;
|
||||
mTag = TAG + "[" + mComponentName.getShortClassName() + "]"
|
||||
+ '@' + Integer.toHexString(hashCode());
|
||||
mTag = String.format("%s[%s]@%h", TAG, mComponentName.getShortClassName(), hashCode());
|
||||
mLogger = new Logger(mListener.getLogBuffer() != null ? mListener.getLogBuffer() :
|
||||
new LogcatOnlyMessageBuffer(LogLevel.WARNING), mTag);
|
||||
|
||||
if (mPlugin != null) {
|
||||
mPluginContext = mPluginFactory.createPluginContext();
|
||||
@@ -92,24 +97,16 @@ public class PluginInstance<T extends Plugin>
|
||||
return mTag;
|
||||
}
|
||||
|
||||
/** */
|
||||
/** True if an error has been observed */
|
||||
public boolean hasError() {
|
||||
return mHasError;
|
||||
}
|
||||
|
||||
public void setLogFunc(BiConsumer logConsumer) {
|
||||
mLogConsumer = logConsumer;
|
||||
}
|
||||
|
||||
private void log(String message) {
|
||||
if (mLogConsumer != null) {
|
||||
mLogConsumer.accept(mTag, message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean onFail(String className, String methodName, Throwable failure) {
|
||||
Log.e(TAG, "Failure from " + mPlugin + ". Disabling Plugin.");
|
||||
PluginActionManager.logFmt(mLogger, LogLevel.ERROR,
|
||||
"Failure from %s. Disabling Plugin.", mPlugin.toString(), null);
|
||||
|
||||
mHasError = true;
|
||||
unloadPlugin();
|
||||
mListener.onPluginDetached(this);
|
||||
@@ -119,31 +116,31 @@ public class PluginInstance<T extends Plugin>
|
||||
/** Alerts listener and plugin that the plugin has been created. */
|
||||
public synchronized void onCreate() {
|
||||
if (mHasError) {
|
||||
log("Previous Fatal Exception detected for plugin class");
|
||||
mLogger.w("Previous Fatal Exception detected for plugin class");
|
||||
return;
|
||||
}
|
||||
|
||||
boolean loadPlugin = mListener.onPluginAttached(this);
|
||||
if (!loadPlugin) {
|
||||
if (mPlugin != null) {
|
||||
log("onCreate: auto-unload");
|
||||
mLogger.d("onCreate: auto-unload");
|
||||
unloadPlugin();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPlugin == null) {
|
||||
log("onCreate: auto-load");
|
||||
mLogger.d("onCreate: auto-load");
|
||||
loadPlugin();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!checkVersion()) {
|
||||
log("onCreate: version check failed");
|
||||
mLogger.d("onCreate: version check failed");
|
||||
return;
|
||||
}
|
||||
|
||||
log("onCreate: load callbacks");
|
||||
mLogger.i("onCreate: load callbacks");
|
||||
if (!(mPlugin instanceof PluginFragment)) {
|
||||
// Only call onCreate for plugins that aren't fragments, as fragments
|
||||
// will get the onCreate as part of the fragment lifecycle.
|
||||
@@ -156,11 +153,11 @@ public class PluginInstance<T extends Plugin>
|
||||
public synchronized void onDestroy() {
|
||||
if (mHasError) {
|
||||
// Detached in error handler
|
||||
log("onDestroy - no-op");
|
||||
mLogger.d("onDestroy - no-op");
|
||||
return;
|
||||
}
|
||||
|
||||
log("onDestroy");
|
||||
mLogger.i("onDestroy");
|
||||
unloadPlugin();
|
||||
mListener.onPluginDetached(this);
|
||||
}
|
||||
@@ -171,17 +168,15 @@ public class PluginInstance<T extends Plugin>
|
||||
return mHasError ? null : mPlugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and creates the plugin if it does not exist.
|
||||
*/
|
||||
/** Loads and creates the plugin if it does not exist. */
|
||||
public synchronized void loadPlugin() {
|
||||
if (mHasError) {
|
||||
log("Previous Fatal Exception detected for plugin class");
|
||||
mLogger.w("Previous Fatal Exception detected for plugin class");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPlugin != null) {
|
||||
log("Load request when already loaded");
|
||||
mLogger.d("Load request when already loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -189,16 +184,16 @@ public class PluginInstance<T extends Plugin>
|
||||
mPlugin = mPluginFactory.createPlugin(this);
|
||||
mPluginContext = mPluginFactory.createPluginContext();
|
||||
if (mPlugin == null || mPluginContext == null) {
|
||||
Log.e(mTag, "Requested load, but failed");
|
||||
mLogger.e("Requested load, but failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!checkVersion()) {
|
||||
log("loadPlugin: version check failed");
|
||||
mLogger.e("loadPlugin: version check failed");
|
||||
return;
|
||||
}
|
||||
|
||||
log("Loaded plugin; running callbacks");
|
||||
mLogger.e("Loaded plugin; running callbacks");
|
||||
if (!(mPlugin instanceof PluginFragment)) {
|
||||
// Only call onCreate for plugins that aren't fragments, as fragments
|
||||
// will get the onCreate as part of the fragment lifecycle.
|
||||
@@ -207,9 +202,7 @@ public class PluginInstance<T extends Plugin>
|
||||
mListener.onPluginLoaded(mPlugin, mPluginContext, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the plugin version, and permanently destroys the plugin instance on a failure
|
||||
*/
|
||||
/** Checks the plugin version, and permanently destroys the plugin instance on a failure */
|
||||
private synchronized boolean checkVersion() {
|
||||
if (mHasError) {
|
||||
return false;
|
||||
@@ -237,11 +230,11 @@ public class PluginInstance<T extends Plugin>
|
||||
*/
|
||||
public synchronized void unloadPlugin() {
|
||||
if (mPlugin == null) {
|
||||
log("Unload request when already unloaded");
|
||||
mLogger.d("Unload request when already unloaded");
|
||||
return;
|
||||
}
|
||||
|
||||
log("Unloading plugin, running callbacks");
|
||||
mLogger.i("Unloading plugin, running callbacks");
|
||||
mListener.onPluginUnloaded(mPlugin, this);
|
||||
if (!(mPlugin instanceof PluginFragment)) {
|
||||
// Only call onDestroy for plugins that aren't fragments, as fragments
|
||||
@@ -397,7 +390,7 @@ public class PluginInstance<T extends Plugin>
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple class to create a new instance. Useful for testing.
|
||||
* Simple class to create a new instance. Useful for testing.
|
||||
*
|
||||
* @param <T> The type of plugin this create.
|
||||
**/
|
||||
|
||||
@@ -101,7 +101,7 @@ oneway interface ILauncherProxy {
|
||||
/**
|
||||
* Sent when split keyboard shortcut is triggered to enter stage split.
|
||||
*/
|
||||
void enterStageSplitFromRunningApp(boolean leftOrTop) = 25;
|
||||
void enterStageSplitFromRunningApp(int displayId, boolean leftOrTop) = 25;
|
||||
|
||||
/**
|
||||
* Sent when the task bar stash state is toggled.
|
||||
@@ -159,4 +159,10 @@ oneway interface ILauncherProxy {
|
||||
* Sent when {@link TaskbarDelegate#onDisplayRemoveSystemDecorations} is called.
|
||||
*/
|
||||
void onDisplayRemoveSystemDecorations(int displayId) = 38;
|
||||
|
||||
/**
|
||||
* Sent when active action corner is received in {@link ActionCornerInteractor}. Please refer to
|
||||
* {@link ActionCornerConstants.Action} for all possible actions.
|
||||
*/
|
||||
void onActionCornerActivated(int action, int displayId) = 39;
|
||||
}
|
||||
|
||||
@@ -178,5 +178,10 @@ interface ISystemUiProxy {
|
||||
*/
|
||||
oneway void updateContextualEduStats(boolean isTrackpadGesture, String gestureType) = 58;
|
||||
|
||||
// Next id = 59
|
||||
/**
|
||||
* Sent after layout is performed for the "recents" button and it is visible on screen.
|
||||
*/
|
||||
oneway void notifyRecentsButtonPositionChanged(in Rect position) = 59;
|
||||
|
||||
// Next id = 60
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ data class ThumbnailData(
|
||||
"ThumbnailData",
|
||||
"Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE: " +
|
||||
"${snapshot.hardwareBuffer}",
|
||||
ex
|
||||
ex,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -69,10 +69,15 @@ data class ThumbnailData(
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun wrap(taskIds: IntArray?, snapshots: Array<TaskSnapshot>?): HashMap<Int, ThumbnailData> {
|
||||
fun wrap(
|
||||
taskIds: IntArray?,
|
||||
snapshots: Array<TaskSnapshot?>?,
|
||||
): HashMap<Int, ThumbnailData> {
|
||||
return hashMapOf<Int, ThumbnailData>().apply {
|
||||
if (taskIds != null && snapshots != null && taskIds.size == snapshots.size) {
|
||||
repeat(snapshots.size) { put(taskIds[it], fromSnapshot(snapshots[it])) }
|
||||
repeat(snapshots.size) {
|
||||
snapshots[it]?.let { snapshot -> put(taskIds[it], fromSnapshot(snapshot)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+34
-14
@@ -5,11 +5,18 @@ import static android.view.Surface.ROTATION_180;
|
||||
import static android.view.Surface.ROTATION_270;
|
||||
import static android.view.Surface.ROTATION_90;
|
||||
|
||||
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
|
||||
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
|
||||
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
|
||||
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.wm.shell.shared.split.SplitBounds;
|
||||
|
||||
/**
|
||||
* Utility class to position the thumbnail in the TaskView
|
||||
@@ -18,22 +25,20 @@ public class PreviewPositionHelper {
|
||||
|
||||
public static final float MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT = 0.1f;
|
||||
|
||||
/**
|
||||
* Specifies that a stage is positioned at the top half of the screen if
|
||||
* in portrait mode or at the left half of the screen if in landscape mode.
|
||||
* TODO(b/254378592): Remove after consolidation
|
||||
*/
|
||||
public static final int STAGE_POSITION_TOP_OR_LEFT = 0;
|
||||
|
||||
/**
|
||||
* Specifies that a stage is positioned at the bottom half of the screen if
|
||||
* in portrait mode or at the right half of the screen if in landscape mode.
|
||||
* TODO(b/254378592): Remove after consolidation
|
||||
*/
|
||||
public static final int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
|
||||
|
||||
private final Matrix mMatrix = new Matrix();
|
||||
private boolean mIsOrientationChanged;
|
||||
/**
|
||||
* Only used when this helper is being used for an app in split screen. Refers to the position
|
||||
* of the app in the pair.
|
||||
* See {@link com.android.wm.shell.shared.split.SplitScreenConstants#@SplitPosition}
|
||||
*/
|
||||
private int mSplitPosition = SPLIT_POSITION_UNDEFINED;
|
||||
/**
|
||||
* Guarded by enableFlexibleTwoAppSplit() flag, but this class doesn't have access so the
|
||||
* caller is responsible for checking. If the flag is disabled this will be null
|
||||
*/
|
||||
@Nullable
|
||||
private SplitBounds mSplitBounds;
|
||||
|
||||
public Matrix getMatrix() {
|
||||
return mMatrix;
|
||||
@@ -160,6 +165,16 @@ public class PreviewPositionHelper {
|
||||
}
|
||||
|
||||
thumbnailScale = targetW / (croppedWidth * scale);
|
||||
|
||||
if (mSplitBounds != null
|
||||
&& mSplitBounds.snapPosition == SNAP_TO_2_10_90
|
||||
&& mSplitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
|
||||
if (mSplitBounds.appsStackedVertically) {
|
||||
thumbnailClipHint.top += availableHeight - croppedHeight;
|
||||
} else {
|
||||
thumbnailClipHint.left += availableWidth - croppedWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isRotated) {
|
||||
@@ -209,6 +224,11 @@ public class PreviewPositionHelper {
|
||||
mMatrix.postTranslate(translateX, translateY);
|
||||
}
|
||||
|
||||
public void setSplitBounds(SplitBounds splitBounds, int stagePosition) {
|
||||
mSplitBounds = splitBounds;
|
||||
mSplitPosition = stagePosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* A factory that returns a new instance of the {@link PreviewPositionHelper}.
|
||||
* <p>{@link PreviewPositionHelper} is a stateful helper, and hence when using it in distinct
|
||||
|
||||
@@ -136,9 +136,9 @@ constructor(
|
||||
val sampledRegionWithOffset = convertBounds(screenLocationBounds)
|
||||
if (
|
||||
sampledRegionWithOffset.left < 0.0 ||
|
||||
sampledRegionWithOffset.right > 1.0 ||
|
||||
sampledRegionWithOffset.top < 0.0 ||
|
||||
sampledRegionWithOffset.bottom > 1.0
|
||||
sampledRegionWithOffset.right > 1.0 ||
|
||||
sampledRegionWithOffset.top < 0.0 ||
|
||||
sampledRegionWithOffset.bottom > 1.0
|
||||
) {
|
||||
if (DEBUG)
|
||||
Log.d(
|
||||
@@ -192,7 +192,7 @@ constructor(
|
||||
pw.println("screen width: ${displaySize.x}, screen height: ${displaySize.y}")
|
||||
pw.println(
|
||||
"sampledRegionWithOffset: ${convertBounds(
|
||||
calculateScreenLocation(sampledView) ?: RectF())}"
|
||||
calculateScreenLocation(sampledView) ?: RectF())}"
|
||||
)
|
||||
pw.println(
|
||||
"initialSampling for ${if (isLockscreen) "lockscreen" else "homescreen" }" +
|
||||
|
||||
+42
-11
@@ -19,6 +19,7 @@ package com.android.systemui.shared.rotation;
|
||||
import android.annotation.DimenRes;
|
||||
import android.annotation.IdRes;
|
||||
import android.annotation.LayoutRes;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.StringRes;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
@@ -79,8 +80,8 @@ public class FloatingRotationButton implements RotationButton {
|
||||
|
||||
private FloatingRotationButtonPositionCalculator mPositionCalculator;
|
||||
|
||||
private RotationButtonController mRotationButtonController;
|
||||
private RotationButtonUpdatesCallback mUpdatesCallback;
|
||||
@Nullable private RotationButtonController mRotationButtonController;
|
||||
@Nullable private RotationButtonUpdatesCallback mUpdatesCallback;
|
||||
private Position mPosition;
|
||||
|
||||
public FloatingRotationButton(Context context, @StringRes int contentDescriptionResource,
|
||||
@@ -132,14 +133,17 @@ public class FloatingRotationButton implements RotationButton {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRotationButtonController(RotationButtonController rotationButtonController) {
|
||||
public void setRotationButtonController(
|
||||
@Nullable RotationButtonController rotationButtonController) {
|
||||
mRotationButtonController = rotationButtonController;
|
||||
updateIcon(mRotationButtonController.getLightIconColor(),
|
||||
mRotationButtonController.getDarkIconColor());
|
||||
if (mRotationButtonController != null) {
|
||||
updateIcon(mRotationButtonController.getLightIconColor(),
|
||||
mRotationButtonController.getDarkIconColor());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback) {
|
||||
public void setUpdatesCallback(@Nullable RotationButtonUpdatesCallback updatesCallback) {
|
||||
mUpdatesCallback = updatesCallback;
|
||||
}
|
||||
|
||||
@@ -188,6 +192,28 @@ public class FloatingRotationButton implements RotationButton {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
setRotationButtonController(null);
|
||||
setOnClickListener(null);
|
||||
setOnHoverListener(null);
|
||||
setUpdatesCallback(null);
|
||||
|
||||
if (mKeyButtonContainer.isAttachedToWindow()) {
|
||||
mWindowManager.removeViewImmediate(mKeyButtonContainer);
|
||||
}
|
||||
|
||||
mKeyButtonView.animate().cancel();
|
||||
mAnimatedDrawable.stop();
|
||||
mKeyButtonView.setImageDrawable(null);
|
||||
// Calling AnimatedDrawable#stop() and ImageView.setImageDrawable(null) above will not let
|
||||
// RenderThread clear ref to view (via AnimatedVectorDrawable$VectorDrawableAnimatorRT)
|
||||
// quick enough for LeakCanary to ignore the leak. To mute LeakCanary, a workaround is to
|
||||
// clear the mKeyButtonView.mParent so that LeakCanary won't complain the leak on
|
||||
// mKeyButtonContainer.
|
||||
mKeyButtonContainer.removeView(mKeyButtonView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return mIsShowing;
|
||||
@@ -195,9 +221,12 @@ public class FloatingRotationButton implements RotationButton {
|
||||
|
||||
@Override
|
||||
public void updateIcon(int lightIconColor, int darkIconColor) {
|
||||
mAnimatedDrawable = (AnimatedVectorDrawable) mKeyButtonView.getContext()
|
||||
.getDrawable(mRotationButtonController.getIconResId());
|
||||
mAnimatedDrawable.setBounds(0, 0, mKeyButtonView.getWidth(), mKeyButtonView.getHeight());
|
||||
if (mRotationButtonController != null) {
|
||||
mAnimatedDrawable = (AnimatedVectorDrawable) mKeyButtonView.getContext()
|
||||
.getDrawable(mRotationButtonController.getIconResId());
|
||||
mAnimatedDrawable.setBounds(
|
||||
0, 0, mKeyButtonView.getWidth(), mKeyButtonView.getHeight());
|
||||
}
|
||||
mKeyButtonView.setImageDrawable(mAnimatedDrawable);
|
||||
mKeyButtonView.setColors(lightIconColor, darkIconColor);
|
||||
}
|
||||
@@ -251,8 +280,10 @@ public class FloatingRotationButton implements RotationButton {
|
||||
updateDimensionResources();
|
||||
|
||||
if (mIsShowing) {
|
||||
updateIcon(mRotationButtonController.getLightIconColor(),
|
||||
mRotationButtonController.getDarkIconColor());
|
||||
if (mRotationButtonController != null) {
|
||||
updateIcon(mRotationButtonController.getLightIconColor(),
|
||||
mRotationButtonController.getDarkIconColor());
|
||||
}
|
||||
final LayoutParams layoutParams = adjustViewPositionAndCreateLayoutParams();
|
||||
mWindowManager.updateViewLayout(mKeyButtonContainer, layoutParams);
|
||||
if (mAnimatedDrawable != null) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.shared.rotation;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
|
||||
@@ -25,14 +26,17 @@ import android.view.View;
|
||||
* one in contextual for 3 button nav and a floating rotation button for gestural.
|
||||
*/
|
||||
public interface RotationButton {
|
||||
default void setRotationButtonController(RotationButtonController rotationButtonController) { }
|
||||
default void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback) { }
|
||||
default void setRotationButtonController(
|
||||
@Nullable RotationButtonController rotationButtonController) { }
|
||||
default void setUpdatesCallback(
|
||||
@Nullable RotationButtonUpdatesCallback updatesCallback) { }
|
||||
|
||||
default View getCurrentView() {
|
||||
return null;
|
||||
}
|
||||
default boolean show() { return false; }
|
||||
default boolean hide() { return false; }
|
||||
default void onDestroy() {}
|
||||
default boolean isVisible() {
|
||||
return false;
|
||||
}
|
||||
|
||||
+68
-11
@@ -17,6 +17,8 @@
|
||||
package com.android.systemui.shared.rotation;
|
||||
|
||||
import static android.content.pm.PackageManager.FEATURE_PC;
|
||||
import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
|
||||
import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
|
||||
@@ -36,6 +38,8 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.devicestate.DeviceState;
|
||||
import android.hardware.devicestate.DeviceStateManager;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
@@ -67,8 +71,10 @@ import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdates
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListeners;
|
||||
import com.android.window.flags2.Flags;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
@@ -94,7 +100,7 @@ public class RotationButtonController {
|
||||
private final UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
|
||||
private final ViewRippler mViewRippler = new ViewRippler();
|
||||
private final Supplier<Integer> mWindowRotationProvider;
|
||||
private RotationButton mRotationButton;
|
||||
@Nullable private RotationButton mRotationButton;
|
||||
|
||||
private boolean mIsRecentsAnimationRunning;
|
||||
private boolean mDocked;
|
||||
@@ -201,6 +207,14 @@ public class RotationButtonController {
|
||||
mRotationButton.setUpdatesCallback(updatesCallback);
|
||||
}
|
||||
|
||||
private void clearRotationButton() {
|
||||
if (mRotationButton == null) {
|
||||
return;
|
||||
}
|
||||
mRotationButton.onDestroy();
|
||||
mRotationButton = null;
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
@@ -230,6 +244,7 @@ public class RotationButtonController {
|
||||
*/
|
||||
public void onDestroy() {
|
||||
unregisterListeners();
|
||||
clearRotationButton();
|
||||
}
|
||||
|
||||
public void registerListeners(boolean registerRotationWatcher) {
|
||||
@@ -286,12 +301,23 @@ public class RotationButtonController {
|
||||
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
|
||||
}
|
||||
|
||||
public void setRotationLockedAtAngle(
|
||||
/**
|
||||
* Sets device rotation to {@code rotationSuggestion} if {@code isLocked} is true and
|
||||
* {@link Flags#enableDeviceStateAutoRotateSettingRefactor()} is disabled.
|
||||
* <p> When {@link Flags#enableDeviceStateAutoRotateSettingRefactor()} is enabled, the rotation
|
||||
* change in system server is conditional on auto-rotate still being OFF.
|
||||
*/
|
||||
public void setRotationAtAngle(
|
||||
@Nullable Boolean isLocked, int rotationSuggestion, String caller) {
|
||||
if (isLocked == null) {
|
||||
// Ignore if we can't read the setting for the current user
|
||||
return;
|
||||
}
|
||||
if (isFoldable() && Flags.enableDeviceStateAutoRotateSettingRefactor()) {
|
||||
RotationPolicy.setRotationAtAngleIfAllowed(rotationSuggestion, caller);
|
||||
return;
|
||||
}
|
||||
|
||||
RotationPolicy.setRotationLockAtAngle(mContext, /* enabled= */ isLocked,
|
||||
/* rotation= */ rotationSuggestion, caller);
|
||||
}
|
||||
@@ -301,6 +327,8 @@ public class RotationButtonController {
|
||||
}
|
||||
|
||||
void setRotateSuggestionButtonState(final boolean visible, final boolean force) {
|
||||
if (mRotationButton == null) return;
|
||||
|
||||
// At any point the button can become invisible because an a11y service became active.
|
||||
// Similarly, a call to make the button visible may be rejected because an a11y service is
|
||||
// active. Must account for this.
|
||||
@@ -361,7 +389,9 @@ public class RotationButtonController {
|
||||
fadeOut.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mRotationButton.hide();
|
||||
if (mRotationButton != null) {
|
||||
mRotationButton.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -371,7 +401,9 @@ public class RotationButtonController {
|
||||
}
|
||||
|
||||
public void setDarkIntensity(float darkIntensity) {
|
||||
mRotationButton.setDarkIntensity(darkIntensity);
|
||||
if (mRotationButton != null) {
|
||||
mRotationButton.setDarkIntensity(darkIntensity);
|
||||
}
|
||||
}
|
||||
|
||||
public void setRecentsAnimationRunning(boolean running) {
|
||||
@@ -400,6 +432,10 @@ public class RotationButtonController {
|
||||
}
|
||||
|
||||
public void onRotationProposal(int rotation, boolean isValid) {
|
||||
if (mRotationButton == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isUserSetupComplete = Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
|
||||
if (!isUserSetupComplete && OEM_DISALLOW_ROTATION_IN_SUW) {
|
||||
@@ -473,10 +509,10 @@ public class RotationButtonController {
|
||||
}
|
||||
// The isVisible check makes the rotation button disappear when we are not locked
|
||||
// (e.g. for tabletop auto-rotate).
|
||||
if (isRotationLocked || mRotationButton.isVisible()) {
|
||||
if (isRotationLocked || (mRotationButton != null && mRotationButton.isVisible())) {
|
||||
// Do not allow a change in rotation to set user rotation when docked.
|
||||
if (shouldOverrideUserLockPrefs(rotation) && isRotationLocked && !mDocked) {
|
||||
setRotationLockedAtAngle(true, rotation, /* caller= */
|
||||
setRotationAtAngle(true, rotation, /* caller= */
|
||||
"RotationButtonController#onRotationWatcherChanged");
|
||||
}
|
||||
setRotateSuggestionButtonState(false /* visible */, true /* forced */);
|
||||
@@ -512,10 +548,9 @@ public class RotationButtonController {
|
||||
|
||||
public void onTaskbarStateChange(boolean visible, boolean stashed) {
|
||||
mTaskBarVisible = visible;
|
||||
if (getRotationButton() == null) {
|
||||
return;
|
||||
if (mRotationButton != null) {
|
||||
mRotationButton.onTaskbarStateChanged(visible, stashed);
|
||||
}
|
||||
getRotationButton().onTaskbarStateChanged(visible, stashed);
|
||||
}
|
||||
|
||||
private void showPendingRotationButtonIfNeeded() {
|
||||
@@ -574,6 +609,7 @@ public class RotationButtonController {
|
||||
"%s\tmDarkIconColor=0x%s", prefix, Integer.toHexString(mDarkIconColor)));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public RotationButton getRotationButton() {
|
||||
return mRotationButton;
|
||||
}
|
||||
@@ -581,7 +617,7 @@ public class RotationButtonController {
|
||||
private void onRotateSuggestionClick(View v) {
|
||||
mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED);
|
||||
incrementNumAcceptedRotationSuggestionsIfNeeded();
|
||||
setRotationLockedAtAngle(
|
||||
setRotationAtAngle(
|
||||
RotationPolicyUtil.isRotationLocked(mContext), mLastRotationSuggestion,
|
||||
/* caller= */ "RotationButtonController#onRotateSuggestionClick");
|
||||
Log.i(TAG, "onRotateSuggestionClick() mLastRotationSuggestion=" + mLastRotationSuggestion);
|
||||
@@ -635,7 +671,7 @@ public class RotationButtonController {
|
||||
// Don't reschedule if a hide animator is running
|
||||
if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return;
|
||||
// Don't reschedule if not visible
|
||||
if (!mRotationButton.isVisible()) return;
|
||||
if (mRotationButton == null || !mRotationButton.isVisible()) return;
|
||||
}
|
||||
|
||||
// Stop any pending removal
|
||||
@@ -670,6 +706,27 @@ public class RotationButtonController {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFoldable() {
|
||||
if (android.hardware.devicestate.feature.flags.Flags.deviceStatePropertyMigration()) {
|
||||
final DeviceStateManager deviceStateManager = mContext.getSystemService(
|
||||
DeviceStateManager.class);
|
||||
if (deviceStateManager == null) return false;
|
||||
List<DeviceState> deviceStates = deviceStateManager.getSupportedDeviceStates();
|
||||
for (int i = 0; i < deviceStates.size(); i++) {
|
||||
DeviceState state = deviceStates.get(i);
|
||||
if (state.hasProperty(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)
|
||||
|| state.hasProperty(
|
||||
PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return mContext.getResources().getIntArray(
|
||||
com.android.internal.R.array.config_foldedDeviceStates).length != 0;
|
||||
}
|
||||
}
|
||||
|
||||
private class TaskStackListenerImpl implements TaskStackChangeListener {
|
||||
// Invalidate any rotation suggestion on task change or activity orientation change
|
||||
// Note: all callbacks happen on main thread
|
||||
|
||||
@@ -18,16 +18,13 @@ package com.android.systemui.shared.system;
|
||||
|
||||
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
|
||||
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
|
||||
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
|
||||
import static android.app.ActivityTaskManager.getService;
|
||||
import static app.lawnchair.compat.LawnchairQuickstepCompat.ATLEAST_R;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityClient;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.RecentTaskInfo;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.ActivityTaskManager;
|
||||
@@ -36,12 +33,8 @@ import android.app.WindowConfiguration;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.DeadSystemException;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
@@ -55,9 +48,6 @@ import com.android.internal.app.IVoiceInteractionManagerService;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class ActivityManagerWrapper {
|
||||
@@ -83,17 +73,13 @@ public class ActivityManagerWrapper {
|
||||
/**
|
||||
* @return the current user's id.
|
||||
*/
|
||||
public int getCurrentUserId() throws DeadSystemException {
|
||||
public int getCurrentUserId() {
|
||||
UserInfo ui;
|
||||
try {
|
||||
ui = ActivityManager.getService().getCurrentUser();
|
||||
return ui != null ? ui.id : 0;
|
||||
} catch (RemoteException e) {
|
||||
if (ATLEAST_R) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
} else {
|
||||
throw new DeadSystemException();
|
||||
}
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,24 +135,9 @@ public class ActivityManagerWrapper {
|
||||
public @NonNull ThumbnailData getTaskThumbnail(int taskId, boolean isLowResolution) {
|
||||
TaskSnapshot snapshot = null;
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
Method getTaskSnapshotMethod = getService().getClass().getMethod(
|
||||
"getTaskSnapshot",
|
||||
int.class, // taskId
|
||||
boolean.class, // isLowResolution
|
||||
boolean.class // isTranslucent (added in Android 14)
|
||||
);
|
||||
|
||||
snapshot = (TaskSnapshot) getTaskSnapshotMethod.invoke(
|
||||
getService(), taskId, isLowResolution, false);
|
||||
|
||||
} else {
|
||||
snapshot = getService().getTaskSnapshot(taskId, isLowResolution);
|
||||
}
|
||||
snapshot = getService().getTaskSnapshot(taskId, isLowResolution);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to retrieve task snapshot", e);
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
Log.e(TAG, "Failed to invoke getTaskSnapshot", e);
|
||||
}
|
||||
if (snapshot != null) {
|
||||
return ThumbnailData.fromSnapshot(snapshot);
|
||||
@@ -337,18 +308,6 @@ public class ActivityManagerWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the system supports freeform multi-window.
|
||||
*/
|
||||
public boolean supportsFreeformMultiWindow(Context context) {
|
||||
final boolean freeformDevOption = Settings.Global.getInt(context.getContentResolver(),
|
||||
Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
|
||||
return ActivityTaskManager.supportsMultiWindow(context)
|
||||
&& (context.getPackageManager().hasSystemFeature(
|
||||
PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
|
||||
|| freeformDevOption);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the running task represents the home task
|
||||
*/
|
||||
|
||||
+2
-35
@@ -20,7 +20,6 @@ import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
|
||||
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
@@ -33,8 +32,6 @@ import android.view.InputEvent;
|
||||
import android.view.WindowManagerGlobal;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Manages the input consumer that allows the SystemUI to directly receive input.
|
||||
@@ -142,14 +139,7 @@ public class InputConsumerController {
|
||||
if (mInputEventReceiver == null) {
|
||||
final InputChannel inputChannel = new InputChannel();
|
||||
try {
|
||||
// Hook for Android P (9) to VANILLA_ICE_CREAM (15), where the method signature changed
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
|
||||
&& Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
hookDestroyInputConsumer(mWindowManager);
|
||||
} else {
|
||||
mWindowManager.destroyInputConsumer(mToken, DEFAULT_DISPLAY);
|
||||
}
|
||||
|
||||
mWindowManager.destroyInputConsumer(mToken, DEFAULT_DISPLAY);
|
||||
mWindowManager.createInputConsumer(mToken, mName, DEFAULT_DISPLAY, inputChannel);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to create input consumer", e);
|
||||
@@ -162,36 +152,13 @@ public class InputConsumerController {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* IWindowManager @destroyInputConsumer reflection
|
||||
*/
|
||||
private void hookDestroyInputConsumer(IWindowManager mWindowManager) throws RemoteException {
|
||||
try {
|
||||
Class<?> iWindowManagerClass = Class.forName("android.view.IWindowManager");
|
||||
Method destroyInputConsumerMethod = iWindowManagerClass.getMethod("destroyInputConsumer", String.class, int.class);
|
||||
destroyInputConsumerMethod.invoke(mWindowManager, mName, DEFAULT_DISPLAY);
|
||||
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException |
|
||||
InvocationTargetException e) {
|
||||
Log.e(TAG, "Failed to invoke destroyInputConsumer", e);
|
||||
mWindowManager.destroyInputConsumer(mToken, DEFAULT_DISPLAY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the input consumer.
|
||||
*/
|
||||
public void unregisterInputConsumer() {
|
||||
if (mInputEventReceiver != null) {
|
||||
try {
|
||||
// Hook for Android P (9) to VANILLA_ICE_CREAM (15), where the method signature changed
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
|
||||
&& Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
hookDestroyInputConsumer(mWindowManager);
|
||||
} else {
|
||||
mWindowManager.destroyInputConsumer(mToken, DEFAULT_DISPLAY);
|
||||
}
|
||||
mWindowManager.destroyInputConsumer(mToken, DEFAULT_DISPLAY);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to destroy input consumer", e);
|
||||
}
|
||||
|
||||
+6
-6
@@ -32,7 +32,7 @@ public final class InteractionJankMonitorWrapper {
|
||||
* @param cujType the specific {@link Cuj.CujType}.
|
||||
*/
|
||||
public static void begin(View v, @Cuj.CujType int cujType) {
|
||||
if (true) return; // LC-Ignored
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return;
|
||||
InteractionJankMonitor.getInstance().begin(v, cujType);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public final class InteractionJankMonitorWrapper {
|
||||
* @param timeout duration to cancel the instrumentation in ms
|
||||
*/
|
||||
public static void begin(View v, @Cuj.CujType int cujType, long timeout) {
|
||||
if (true) return; // LC-Ignored
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return;
|
||||
Configuration.Builder builder =
|
||||
Configuration.Builder.withView(cujType, v)
|
||||
.setTimeout(timeout);
|
||||
@@ -59,7 +59,7 @@ public final class InteractionJankMonitorWrapper {
|
||||
* @param tag the tag to distinguish different flow of same type CUJ.
|
||||
*/
|
||||
public static void begin(View v, @Cuj.CujType int cujType, String tag) {
|
||||
if (true) return; // LC-Ignored
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return;
|
||||
Configuration.Builder builder =
|
||||
Configuration.Builder.withView(cujType, v);
|
||||
if (!TextUtils.isEmpty(tag)) {
|
||||
@@ -74,7 +74,7 @@ public final class InteractionJankMonitorWrapper {
|
||||
* @param cujType the specific {@link Cuj.CujType}.
|
||||
*/
|
||||
public static void end(@Cuj.CujType int cujType) {
|
||||
if (true) return; // LC-Ignored
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return;
|
||||
InteractionJankMonitor.getInstance().end(cujType);
|
||||
}
|
||||
|
||||
@@ -82,13 +82,13 @@ public final class InteractionJankMonitorWrapper {
|
||||
* Cancel the trace session.
|
||||
*/
|
||||
public static void cancel(@Cuj.CujType int cujType) {
|
||||
if (true) return; // LC-Ignored
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return;
|
||||
InteractionJankMonitor.getInstance().cancel(cujType);
|
||||
}
|
||||
|
||||
/** Return true if currently instrumenting a trace session. */
|
||||
public static boolean isInstrumenting(@Cuj.CujType int cujType) {
|
||||
if (true) return true; // LC-Ignored
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return false;
|
||||
return InteractionJankMonitor.getInstance().isInstrumenting(cujType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,11 +416,13 @@ public class QuickStepContract {
|
||||
public static boolean sRecentsDisabled = false;
|
||||
public static boolean sHasCustomCornerRadius = false;
|
||||
public static float sCustomCornerRadius = 0f;
|
||||
|
||||
|
||||
/**
|
||||
* Corner radius that should be used on windows in order to cover the display.
|
||||
* These values are expressed in pixels because they should not respect display or font
|
||||
* scaling. The corner radius may change when folding/unfolding the device.
|
||||
*
|
||||
* @param context A display associated context.
|
||||
*/
|
||||
public static float getWindowCornerRadius(Context context) {
|
||||
// LC-Wrapped
|
||||
|
||||
+1
-21
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.systemui.shared.system;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
@@ -27,8 +26,6 @@ import android.window.WindowAnimationState;
|
||||
import com.android.internal.os.IResultReceiver;
|
||||
import com.android.wm.shell.recents.IRecentsAnimationController;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class RecentsAnimationControllerCompat {
|
||||
|
||||
private static final String TAG = RecentsAnimationControllerCompat.class.getSimpleName();
|
||||
@@ -75,24 +72,7 @@ public class RecentsAnimationControllerCompat {
|
||||
*/
|
||||
public void finish(boolean toHome, boolean sendUserLeaveHint, IResultReceiver finishCb) {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
mAnimationController.finish(toHome, sendUserLeaveHint, finishCb);
|
||||
} else {
|
||||
try {
|
||||
Method finishMethod = mAnimationController.getClass().getMethod(
|
||||
"finish", boolean.class, boolean.class);
|
||||
finishMethod.invoke(mAnimationController, toHome, sendUserLeaveHint);
|
||||
|
||||
if (finishCb != null) {
|
||||
finishCb.send(0, null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to finish recents animation via reflection", e);
|
||||
if (finishCb != null) {
|
||||
finishCb.send(0, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
mAnimationController.finish(toHome, sendUserLeaveHint, finishCb);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to finish recents animation", e);
|
||||
try {
|
||||
|
||||
+8
-1
@@ -28,7 +28,14 @@ class UncaughtExceptionPreHandlerManager @Inject constructor() {
|
||||
* Verifies that the global handler is set in Thread. If not, sets is up.
|
||||
*/
|
||||
private fun checkGlobalHandlerSetup() {
|
||||
// LC-Ignored
|
||||
val currentHandler = Thread.getDefaultUncaughtExceptionHandler()
|
||||
if (currentHandler != globalUncaughtExceptionPreHandler) {
|
||||
if (currentHandler is GlobalUncaughtExceptionHandler) {
|
||||
throw IllegalStateException("Two UncaughtExceptionPreHandlerManagers created")
|
||||
}
|
||||
currentHandler?.let { addHandler(it) }
|
||||
Thread.setDefaultUncaughtExceptionHandler(globalUncaughtExceptionPreHandler)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+25
-12
@@ -18,6 +18,7 @@ import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import android.os.Looper
|
||||
import android.os.Process
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.dagger.qualifiers.UiBackground
|
||||
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
|
||||
@@ -25,6 +26,7 @@ import com.android.systemui.unfold.config.UnfoldTransitionConfig
|
||||
import com.android.systemui.unfold.dagger.UnfoldBg
|
||||
import com.android.systemui.unfold.dagger.UnfoldMain
|
||||
import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
|
||||
import com.android.systemui.unfold.dagger.UnfoldTracking
|
||||
import com.android.systemui.unfold.updates.FoldProvider
|
||||
import com.android.systemui.unfold.util.CurrentActivityTypeProvider
|
||||
import dagger.Binds
|
||||
@@ -33,7 +35,10 @@ import dagger.Provides
|
||||
import java.util.concurrent.Executor
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.android.asCoroutineDispatcher
|
||||
import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import kotlinx.coroutines.plus
|
||||
|
||||
/**
|
||||
* Dagger module with system-only dependencies for the unfold animation. The code that is used to
|
||||
@@ -45,25 +50,20 @@ import kotlinx.coroutines.android.asCoroutineDispatcher
|
||||
abstract class SystemUnfoldSharedModule {
|
||||
|
||||
@Binds
|
||||
abstract fun activityTypeProvider(executor: ActivityManagerActivityTypeProvider):
|
||||
CurrentActivityTypeProvider
|
||||
abstract fun activityTypeProvider(
|
||||
executor: ActivityManagerActivityTypeProvider
|
||||
): CurrentActivityTypeProvider
|
||||
|
||||
@Binds
|
||||
abstract fun config(config: ResourceUnfoldTransitionConfig): UnfoldTransitionConfig
|
||||
@Binds abstract fun config(config: ResourceUnfoldTransitionConfig): UnfoldTransitionConfig
|
||||
|
||||
@Binds
|
||||
abstract fun foldState(provider: DeviceStateManagerFoldProvider): FoldProvider
|
||||
@Binds abstract fun foldState(provider: DeviceStateManagerFoldProvider): FoldProvider
|
||||
|
||||
@Binds
|
||||
abstract fun deviceStateRepository(provider: DeviceStateRepositoryImpl): DeviceStateRepository
|
||||
|
||||
@Binds
|
||||
@UnfoldMain
|
||||
abstract fun mainExecutor(@Main executor: Executor): Executor
|
||||
@Binds @UnfoldMain abstract fun mainExecutor(@Main executor: Executor): Executor
|
||||
|
||||
@Binds
|
||||
@UnfoldMain
|
||||
abstract fun mainHandler(@Main handler: Handler): Handler
|
||||
@Binds @UnfoldMain abstract fun mainHandler(@Main handler: Handler): Handler
|
||||
|
||||
@Binds
|
||||
@UnfoldSingleThreadBg
|
||||
@@ -92,5 +92,18 @@ abstract class SystemUnfoldSharedModule {
|
||||
.apply { start() }
|
||||
.looper
|
||||
}
|
||||
|
||||
@Provides
|
||||
@UnfoldTracking
|
||||
@Singleton
|
||||
fun unfoldTrackingContext(
|
||||
@UnfoldSingleThreadBg singleThreadBgExecutor: Executor,
|
||||
@Application applicationScope: CoroutineScope,
|
||||
): CoroutineScope {
|
||||
// tracking depends on being executed on a single thread so when changing it, ensure all
|
||||
// consumers are not accessing shared state
|
||||
val backgroundDispatcher = singleThreadBgExecutor.asCoroutineDispatcher()
|
||||
return applicationScope + backgroundDispatcher
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user