Merge "Introduce IMMI#getEnabledInputMethodListAsUser()" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 5ef7759..7a1add3 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -242,6 +242,12 @@
aconfig_declarations: "com.android.text.flags-aconfig",
}
+rust_aconfig_library {
+ name: "libandroid_text_flags_rust",
+ crate_name: "android_text_flags",
+ aconfig_declarations: "com.android.text.flags-aconfig",
+}
+
// Location
aconfig_declarations {
name: "android.location.flags-aconfig",
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 36b1eab..6df971a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2105,8 +2105,7 @@
@Override
public void scheduleTaskFragmentTransaction(@NonNull ITaskFragmentOrganizer organizer,
@NonNull TaskFragmentTransaction transaction) throws RemoteException {
- // TODO(b/260873529): ITaskFragmentOrganizer can be cleanup to be a IBinder token
- // after flag removal.
+ // TODO(b/352665082): ITaskFragmentOrganizer can be cleanup to be a IBinder token
organizer.onTransactionReady(transaction);
}
diff --git a/core/java/android/app/servertransaction/ObjectPool.java b/core/java/android/app/servertransaction/ObjectPool.java
index 598bd8a..e86ca37 100644
--- a/core/java/android/app/servertransaction/ObjectPool.java
+++ b/core/java/android/app/servertransaction/ObjectPool.java
@@ -16,70 +16,39 @@
package android.app.servertransaction;
-import com.android.window.flags.Flags;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
/**
* An object pool that can provide reused objects if available.
+ *
* @hide
+ * @deprecated This class is deprecated. Directly create new instances of objects instead of
+ * obtaining them from this pool.
+ * TODO(b/311089192): Clean up usages of the pool.
*/
+@Deprecated
class ObjectPool {
- private static final Object sPoolSync = new Object();
- private static final Map<Class, ArrayList<? extends ObjectPoolItem>> sPoolMap =
- new HashMap<>();
-
- private static final int MAX_POOL_SIZE = 50;
-
/**
* Obtain an instance of a specific class from the pool
- * @param itemClass The class of the object we're looking for.
+ *
+ * @param ignoredItemClass The class of the object we're looking for.
* @return An instance or null if there is none.
+ * @deprecated This method is deprecated. Directly create new instances of objects instead of
+ * obtaining them from this pool.
*/
- public static <T extends ObjectPoolItem> T obtain(Class<T> itemClass) {
- if (Flags.disableObjectPool()) {
- return null;
- }
- synchronized (sPoolSync) {
- @SuppressWarnings("unchecked")
- final ArrayList<T> itemPool = (ArrayList<T>) sPoolMap.get(itemClass);
- if (itemPool != null && !itemPool.isEmpty()) {
- return itemPool.remove(itemPool.size() - 1);
- }
- return null;
- }
+ @Deprecated
+ public static <T extends ObjectPoolItem> T obtain(Class<T> ignoredItemClass) {
+ return null;
}
/**
* Recycle the object to the pool. The object should be properly cleared before this.
- * @param item The object to recycle.
+ *
+ * @param ignoredItem The object to recycle.
* @see ObjectPoolItem#recycle()
+ * @deprecated This method is deprecated. The object pool is no longer used, so there's
+ * no need to recycle objects.
*/
- public static <T extends ObjectPoolItem> void recycle(T item) {
- if (Flags.disableObjectPool()) {
- return;
- }
- synchronized (sPoolSync) {
- @SuppressWarnings("unchecked")
- ArrayList<T> itemPool = (ArrayList<T>) sPoolMap.get(item.getClass());
- if (itemPool == null) {
- itemPool = new ArrayList<>();
- sPoolMap.put(item.getClass(), itemPool);
- }
- // Check if the item is already in the pool
- final int size = itemPool.size();
- for (int i = 0; i < size; i++) {
- if (itemPool.get(i) == item) {
- throw new IllegalStateException("Trying to recycle already recycled item");
- }
- }
-
- if (size < MAX_POOL_SIZE) {
- itemPool.add(item);
- }
- }
+ @Deprecated
+ public static <T extends ObjectPoolItem> void recycle(T ignoredItem) {
}
}
diff --git a/core/java/android/app/servertransaction/ObjectPoolItem.java b/core/java/android/app/servertransaction/ObjectPoolItem.java
index 17bd4f3..0141f6e 100644
--- a/core/java/android/app/servertransaction/ObjectPoolItem.java
+++ b/core/java/android/app/servertransaction/ObjectPoolItem.java
@@ -18,12 +18,20 @@
/**
* Base interface for all lifecycle items that can be put in object pool.
+ *
* @hide
+ * @deprecated This interface is deprecated. Objects should no longer be pooled.
+ * TODO(b/311089192): Clean up usages of this interface.
*/
+@Deprecated
public interface ObjectPoolItem {
/**
* Clear the contents of the item and putting it to a pool. The implementation should call
* {@link ObjectPool#recycle(ObjectPoolItem)} passing itself.
+ *
+ * @deprecated This method is deprecated. The object pool is no longer used, so there's
+ * no need to recycle objects.
*/
+ @Deprecated
void recycle();
}
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 0be2d3e3..e95c6a4 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -277,7 +277,7 @@
if (service != null) {
return service;
} else {
- return Binder.allowBlocking(getIServiceManager().checkService(name));
+ return Binder.allowBlocking(getIServiceManager().checkService(name).getBinder());
}
} catch (RemoteException e) {
Log.e(TAG, "error in checkService", e);
@@ -425,7 +425,7 @@
private static IBinder rawGetService(String name) throws RemoteException {
final long start = sStatLogger.getTime();
- final IBinder binder = getIServiceManager().getService(name);
+ final IBinder binder = getIServiceManager().getService(name).getBinder();
final int time = (int) sStatLogger.logDurationStat(Stats.GET_SERVICE, start);
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 7b91dd5..6c9a5c7 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -58,12 +58,12 @@
}
@UnsupportedAppUsage
- public IBinder getService(String name) throws RemoteException {
+ public Service getService(String name) throws RemoteException {
// Same as checkService (old versions of servicemanager had both methods).
- return mServiceManager.checkService(name);
+ return checkService(name);
}
- public IBinder checkService(String name) throws RemoteException {
+ public Service checkService(String name) throws RemoteException {
return mServiceManager.checkService(name);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2562c8e..ff38920 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11075,6 +11075,13 @@
public static final String MANDATORY_BIOMETRICS = "mandatory_biometrics";
/**
+ * Whether or not requirements for mandatory biometrics is satisfied.
+ * @hide
+ */
+ public static final String MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED =
+ "mandatory_biometrics_requirements_satisfied";
+
+ /**
* Whether or not active unlock triggers on wake.
* @hide
*/
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index e4fc1cd..fbeab84 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -787,7 +787,6 @@
*/
public void setInteractive(boolean interactive) {
mInteractive = interactive;
- updateAccessibilityMessage();
}
/**
@@ -1641,9 +1640,9 @@
if (mWindow == null) return;
if (mDreamAccessibility == null) {
final View rootView = mWindow.getDecorView();
- mDreamAccessibility = new DreamAccessibility(this, rootView);
+ mDreamAccessibility = new DreamAccessibility(this, rootView, this::wakeUp);
}
- mDreamAccessibility.updateAccessibilityConfiguration(isInteractive());
+ mDreamAccessibility.updateAccessibilityConfiguration();
}
private boolean getWindowFlagValue(int flag, boolean defaultValue) {
diff --git a/core/java/android/service/dreams/utils/DreamAccessibility.java b/core/java/android/service/dreams/utils/DreamAccessibility.java
index c38f41b..f504ff7 100644
--- a/core/java/android/service/dreams/utils/DreamAccessibility.java
+++ b/core/java/android/service/dreams/utils/DreamAccessibility.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.os.Bundle;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -32,22 +33,22 @@
private final Context mContext;
private final View mView;
private final View.AccessibilityDelegate mAccessibilityDelegate;
+ private final Runnable mDismissCallback;
- public DreamAccessibility(@NonNull Context context, @NonNull View view) {
+ public DreamAccessibility(@NonNull Context context, @NonNull View view,
+ @NonNull Runnable dismissCallback) {
mContext = context;
mView = view;
mAccessibilityDelegate = createNewAccessibilityDelegate(mContext);
+ mDismissCallback = dismissCallback;
}
/**
- * @param interactive
- * Removes and add accessibility configuration depending if the dream is interactive or not
+ * Adds default accessibility configuration if none exist on the dream
*/
- public void updateAccessibilityConfiguration(Boolean interactive) {
- if (!interactive) {
+ public void updateAccessibilityConfiguration() {
+ if (mView.getAccessibilityDelegate() == null) {
addAccessibilityConfiguration();
- } else {
- removeCustomAccessibilityAction();
}
}
@@ -58,31 +59,28 @@
mView.setAccessibilityDelegate(mAccessibilityDelegate);
}
- /**
- * Removes Configured the accessibility actions for the given root view.
- */
- private void removeCustomAccessibilityAction() {
- if (mView.getAccessibilityDelegate() == mAccessibilityDelegate) {
- mView.setAccessibilityDelegate(null);
- }
- }
-
private View.AccessibilityDelegate createNewAccessibilityDelegate(Context context) {
return new View.AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
- for (AccessibilityNodeInfo.AccessibilityAction action : info.getActionList()) {
- if (action.getId() == AccessibilityNodeInfo.ACTION_CLICK) {
- info.removeAction(action);
- break;
- }
- }
info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfo.ACTION_CLICK,
+ AccessibilityNodeInfo.ACTION_DISMISS,
context.getResources().getString(R.string.dream_accessibility_action_click)
));
}
+
+ @Override
+ public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ switch(action){
+ case AccessibilityNodeInfo.ACTION_DISMISS:
+ if (mDismissCallback != null) {
+ mDismissCallback.run();
+ }
+ break;
+ }
+ return true;
+ }
};
}
}
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/text/ClientFlags.java
index 5d84d17..b07534f 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/core/java/android/text/ClientFlags.java
@@ -68,4 +68,11 @@
public static boolean fixMisalignedContextMenu() {
return TextFlags.isFeatureEnabled(Flags.FLAG_FIX_MISALIGNED_CONTEXT_MENU);
}
+
+ /**
+ * @see Flags#clearFontVariationSettings()
+ */
+ public static boolean clearFontVariationSettings() {
+ return TextFlags.isFeatureEnabled(Flags.FLAG_CLEAR_FONT_VARIATION_SETTINGS);
+ }
}
diff --git a/core/java/android/text/TextFlags.java b/core/java/android/text/TextFlags.java
index 9e02460..4dca284 100644
--- a/core/java/android/text/TextFlags.java
+++ b/core/java/android/text/TextFlags.java
@@ -61,6 +61,7 @@
Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE,
Flags.FLAG_ICU_BIDI_MIGRATION,
Flags.FLAG_FIX_MISALIGNED_CONTEXT_MENU,
+ Flags.FLAG_CLEAR_FONT_VARIATION_SETTINGS,
};
/**
@@ -75,6 +76,7 @@
Flags.fixLineHeightForLocale(),
Flags.icuBidiMigration(),
Flags.fixMisalignedContextMenu(),
+ Flags.clearFontVariationSettings(),
};
/**
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index 8836c8a..02c63db 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -220,3 +220,23 @@
is_fixed_read_only: true
bug: "346915432"
}
+
+flag {
+ name: "clear_font_variation_settings"
+ namespace: "text"
+ description: "The font variation settings must be cleared when the new Typeface is set"
+ bug: "353609778"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "portuguese_hyphenator"
+ namespace: "text"
+ description: "Portuguese taiored hyphenator"
+ bug: "344656282"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index fedbe4a..42d66ce 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -366,13 +366,8 @@
Log.e(TAG, "Received invalid input event");
return;
}
- try {
- vri.processingBackKey(true);
- vri.enqueueInputEvent(keyEvent, null /* receiver */, 0 /* flags */,
- true /* processImmediately */);
- } finally {
- vri.processingBackKey(false);
- }
+ vri.enqueueInputEvent(keyEvent, null /* receiver */, 0 /* flags */,
+ true /* processImmediately */);
});
}
};
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3a1d833..2f204f9 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -727,8 +727,6 @@
boolean mUpcomingWindowFocus;
@GuardedBy("this")
boolean mUpcomingInTouchMode;
- // While set, allow this VRI to handle back key without drop it.
- private boolean mProcessingBackKey;
/**
* Compatibility {@link OnBackInvokedCallback} for windowless window, to forward the back
* key event host app.
@@ -7269,7 +7267,7 @@
// Find a reason for dropping or canceling the event.
final String reason;
// The embedded window is focused, allow this VRI to handle back key.
- if (!mAttachInfo.mHasWindowFocus && !(mProcessingBackKey && isBack(q.mEvent))
+ if (!mAttachInfo.mHasWindowFocus && !isBack(q.mEvent)
&& !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)
&& !isAutofillUiShowing()) {
// This is a non-pointer event and the window doesn't currently have input focus
@@ -11218,11 +11216,6 @@
mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, listener).sendToTarget();
}
- // Make this VRI able to process back key without drop it.
- void processingBackKey(boolean processing) {
- mProcessingBackKey = processing;
- }
-
/**
* Collect and include any ScrollCaptureCallback instances registered with the window.
*
@@ -12554,15 +12547,8 @@
* @return whether the event was handled (i.e. onKeyPreIme consumed it if preImeOnly=true)
*/
public boolean injectBackKeyEvents(boolean preImeOnly) {
- boolean consumed;
- try {
- processingBackKey(true);
- sendBackKeyEvent(KeyEvent.ACTION_DOWN, preImeOnly);
- consumed = sendBackKeyEvent(KeyEvent.ACTION_UP, preImeOnly);
- } finally {
- processingBackKey(false);
- }
- return consumed;
+ sendBackKeyEvent(KeyEvent.ACTION_DOWN, preImeOnly);
+ return sendBackKeyEvent(KeyEvent.ACTION_UP, preImeOnly);
}
private boolean sendBackKeyEvent(int action, boolean preImeOnly) {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 9512347..0dadbe3 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -7438,8 +7438,7 @@
// If the user interacts with a visible element it is safe to assume they consent that
// something is going to start.
opts.setPendingIntentBackgroundActivityStartMode(
- ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- opts.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true);
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
return Pair.create(intent, opts);
}
}
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 48fb2b3..f739622 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -98,6 +98,13 @@
}
flag {
+ name: "scrolling_from_letterbox"
+ namespace: "large_screen_experiences_app_compat"
+ description: "Whether to enable app scrolling from gestures from letterbox area"
+ bug: "353697519"
+}
+
+flag {
name: "app_compat_refactoring"
namespace: "large_screen_experiences_app_compat"
description: "Whether the changes about app compat refactoring are enabled./n"
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index ae9d757..13d465f 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -3,16 +3,6 @@
# Project link: https://gantry.corp.google.com/projects/android_platform_windowing_sdk/changes
-# Using a fixed read only flag because there are ClientTransaction scheduling before
-# WindowManagerService creation.
-flag {
- namespace: "windowing_sdk"
- name: "bundle_client_transaction_flag"
- description: "To bundle multiple ClientTransactionItems into one ClientTransaction"
- bug: "260873529"
- is_fixed_read_only: true
-}
-
flag {
namespace: "windowing_sdk"
name: "activity_embedding_overlay_presentation_flag"
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 918235b..e429cfc 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -21,12 +21,7 @@
import static android.app.servertransaction.TestUtils.referrerIntentList;
import static android.app.servertransaction.TestUtils.resultInfoList;
-import static com.android.window.flags.Flags.FLAG_DISABLE_OBJECT_POOL;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertSame;
import android.annotation.NonNull;
import android.app.ActivityOptions;
@@ -41,14 +36,11 @@
import android.os.IBinder;
import android.os.PersistableBundle;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.flag.junit.FlagsParameterization;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.window.ActivityWindowInfo;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.window.flags.Flags;
-
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,12 +48,8 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-import java.util.List;
import java.util.function.Supplier;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
/**
* Tests for {@link ObjectPool}.
*
@@ -71,31 +59,19 @@
* <p>This test class is a part of Window Manager Service tests and specified in
* {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
*/
-@RunWith(ParameterizedAndroidJunit4.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
public class ObjectPoolTests {
- @Parameters(name = "{0}")
- public static List<FlagsParameterization> getParams() {
- return FlagsParameterization.allCombinationsOf(FLAG_DISABLE_OBJECT_POOL);
- }
-
@Rule
public final MockitoRule mocks = MockitoJUnit.rule();
- @Rule
- public SetFlagsRule mSetFlagsRule;
-
@Mock
private IApplicationThread mApplicationThread;
@Mock
private IBinder mActivityToken;
- public ObjectPoolTests(FlagsParameterization flags) {
- mSetFlagsRule = new SetFlagsRule(flags);
- }
-
// 1. Check if two obtained objects from pool are not the same.
// 2. Check if the state of the object is cleared after recycling.
// 3. Check if the same object is obtained from pool after recycling.
@@ -219,30 +195,11 @@
item.recycle();
final ObjectPoolItem item2 = obtain.get();
- if (Flags.disableObjectPool()) {
- assertNotSame(item, item2); // Different instance.
- } else {
- assertSame(item, item2);
- }
+ assertNotSame(item, item2); // Different instance.
// Create new object when the pool is empty.
final ObjectPoolItem item3 = obtain.get();
assertNotSame(item, item3);
- if (Flags.disableObjectPool()) {
- // Skip recycle if flag enabled, compare unnecessary.
- return;
- }
- assertEquals(item, item3);
-
- // Reset fields after recycle.
- item.recycle();
-
- assertNotEquals(item, item3);
-
- // Recycled objects are equal.
- item3.recycle();
-
- assertEquals(item, item3);
}
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index df95a91..b83931f 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -35,6 +35,7 @@
import android.graphics.fonts.FontVariationAxis;
import android.os.Build;
import android.os.LocaleList;
+import android.text.ClientFlags;
import android.text.GraphicsOperations;
import android.text.SpannableString;
import android.text.SpannedString;
@@ -1540,8 +1541,21 @@
* @return typeface
*/
public Typeface setTypeface(Typeface typeface) {
+ return setTypefaceInternal(typeface, true);
+ }
+
+ private Typeface setTypefaceInternal(Typeface typeface, boolean clearFontVariationSettings) {
final long typefaceNative = typeface == null ? 0 : typeface.native_instance;
nSetTypeface(mNativePaint, typefaceNative);
+
+ if (ClientFlags.clearFontVariationSettings()) {
+ if (clearFontVariationSettings && !Objects.equals(mTypeface, typeface)) {
+ // We cannot call setFontVariationSetting with empty string or null because it calls
+ // setTypeface method. To avoid recursive setTypeface call, manually resetting
+ // mFontVariationSettings.
+ mFontVariationSettings = null;
+ }
+ }
mTypeface = typeface;
return typeface;
}
@@ -2037,6 +2051,14 @@
* </li>
* </ul>
*
+ * Note: This method replaces the Typeface previously set to this instance.
+ * Until API {@link Build.VERSION_CODES.VANILLA_ICE_CREAM}, any caller of
+ * {@link #setTypeface(Typeface)} should call this method with empty settings, then call
+ * {@link #setTypeface(Typeface)}, then call this method with preferred variation settings.
+ * The device API more than {@link Build.VERSION_CODES.VANILLA_ICE_CREAM}, the
+ * {@link #setTypeface(Typeface)} method clears font variation settings. So caller of
+ * {@link #setTypeface(Typeface)} should call this method again for applying variation settings.
+ *
* @param fontVariationSettings font variation settings. You can pass null or empty string as
* no variation settings.
*
@@ -2059,8 +2081,8 @@
if (settings == null || settings.length() == 0) {
mFontVariationSettings = null;
- setTypeface(Typeface.createFromTypefaceWithVariation(mTypeface,
- Collections.emptyList()));
+ setTypefaceInternal(Typeface.createFromTypefaceWithVariation(mTypeface,
+ Collections.emptyList()), false);
return true;
}
@@ -2078,7 +2100,8 @@
return false;
}
mFontVariationSettings = settings;
- setTypeface(Typeface.createFromTypefaceWithVariation(targetTypeface, filteredAxes));
+ setTypefaceInternal(Typeface.createFromTypefaceWithVariation(targetTypeface, filteredAxes),
+ false);
return true;
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index ecf4720..7f11fea 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -39,6 +39,8 @@
import androidx.window.extensions.layout.WindowLayoutComponent;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
+import com.android.window.flags.Flags;
+
import java.util.Objects;
@@ -55,11 +57,9 @@
*/
private static final int NO_LEVEL_OVERRIDE = -1;
- /**
- * The min version of the WM Extensions that must be supported in the current platform version.
- */
- @VisibleForTesting
- static final int EXTENSIONS_VERSION_CURRENT_PLATFORM = 6;
+ private static final int EXTENSIONS_VERSION_V7 = 7;
+
+ private static final int EXTENSIONS_VERSION_V6 = 6;
private final Object mLock = new Object();
private volatile DeviceStateManagerFoldingFeatureProducer mFoldingFeatureProducer;
@@ -67,7 +67,6 @@
private volatile SplitController mSplitController;
private volatile WindowAreaComponent mWindowAreaComponent;
- private final int mVersion = EXTENSIONS_VERSION_CURRENT_PLATFORM;
private final boolean mIsActivityEmbeddingEnabled;
WindowExtensionsImpl() {
@@ -76,9 +75,22 @@
Log.i(TAG, generateLogMessage());
}
+ /**
+ * The min version of the WM Extensions that must be supported in the current platform version.
+ */
+ @VisibleForTesting
+ static int getExtensionsVersionCurrentPlatform() {
+ if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
+ // Activity Embedding animation customization is the only major feature for v7.
+ return EXTENSIONS_VERSION_V7;
+ } else {
+ return EXTENSIONS_VERSION_V6;
+ }
+ }
+
private String generateLogMessage() {
final StringBuilder logBuilder = new StringBuilder("Initializing Window Extensions, "
- + "vendor API level=" + mVersion);
+ + "vendor API level=" + getExtensionsVersionCurrentPlatform());
final int levelOverride = getLevelOverride();
if (levelOverride != NO_LEVEL_OVERRIDE) {
logBuilder.append(", override to ").append(levelOverride);
@@ -91,7 +103,12 @@
@Override
public int getVendorApiLevel() {
final int levelOverride = getLevelOverride();
- return (levelOverride != NO_LEVEL_OVERRIDE) ? levelOverride : mVersion;
+ return hasLevelOverride() ? levelOverride : getExtensionsVersionCurrentPlatform();
+ }
+
+ @VisibleForTesting
+ boolean hasLevelOverride() {
+ return getLevelOverride() != NO_LEVEL_OVERRIDE;
}
private int getLevelOverride() {
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
index c5aaddc..92f4814 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
@@ -16,7 +16,7 @@
package androidx.window.extensions;
-import static androidx.window.extensions.WindowExtensionsImpl.EXTENSIONS_VERSION_CURRENT_PLATFORM;
+import static androidx.window.extensions.WindowExtensionsImpl.getExtensionsVersionCurrentPlatform;
import static com.google.common.truth.Truth.assertThat;
@@ -59,7 +59,8 @@
@Test
public void testGetVendorApiLevel_extensionsEnabled_matchesCurrentVersion() {
assumeTrue(WindowManager.hasWindowExtensionsEnabled());
- assertThat(mVersion).isEqualTo(EXTENSIONS_VERSION_CURRENT_PLATFORM);
+ assumeFalse(((WindowExtensionsImpl) mExtensions).hasLevelOverride());
+ assertThat(mVersion).isEqualTo(getExtensionsVersionCurrentPlatform());
}
@Test
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
index f0d80a0..d3fc49b 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
@@ -39,8 +39,6 @@
/**
* Determines state of flag based on the actual flag and desktop mode developer option overrides.
- *
- * Note, this method makes sure that a constant developer toggle overrides is read until reboot.
*/
fun isEnabled(context: Context): Boolean =
if (!Flags.showDesktopWindowingDevOption() ||
@@ -65,7 +63,7 @@
?: run {
val override = getToggleOverrideFromSystem(context)
// Cache toggle override the first time we encounter context. Override does not change
- // with context, as context is just used to fetch System Property and Settings.Global
+ // with context, as context is just used to fetch Settings.Global
cachedToggleOverride = override
Log.d(TAG, "Toggle override initialized to: $override")
override
@@ -74,29 +72,13 @@
return override
}
- private fun getToggleOverrideFromSystem(context: Context): ToggleOverride {
- // A non-persistent System Property is used to store override to ensure it remains
- // constant till reboot.
- val overrideFromSystemProperties: ToggleOverride? =
- System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, null).convertToToggleOverride()
- return overrideFromSystemProperties
- ?: run {
- // Read Setting Global if System Property is not present (just after reboot)
- // or not valid (user manually changed the value)
- val overrideFromSettingsGlobal =
- convertToToggleOverrideWithFallback(
- Settings.Global.getInt(
- context.contentResolver,
- Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
- ToggleOverride.OVERRIDE_UNSET.setting),
- ToggleOverride.OVERRIDE_UNSET)
- // Initialize System Property
- System.setProperty(
- SYSTEM_PROPERTY_OVERRIDE_KEY, overrideFromSettingsGlobal.setting.toString())
-
- overrideFromSettingsGlobal
- }
- }
+ private fun getToggleOverrideFromSystem(context: Context): ToggleOverride =
+ convertToToggleOverrideWithFallback(
+ Settings.Global.getInt(
+ context.contentResolver,
+ Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
+ ToggleOverride.OVERRIDE_UNSET.setting),
+ ToggleOverride.OVERRIDE_UNSET)
/**
* Override state of desktop mode developer option toggle.
@@ -113,27 +95,12 @@
OVERRIDE_ON(1)
}
- private fun String?.convertToToggleOverride(): ToggleOverride? {
- val intValue = this?.toIntOrNull() ?: return null
- return settingToToggleOverrideMap[intValue]
- ?: run {
- Log.w(TAG, "Unknown toggleOverride int $intValue")
- null
- }
- }
-
companion object {
private const val TAG = "DesktopModeFlags"
/**
- * Key for non-persistent System Property which is used to store desktop windowing developer
- * option overrides.
- */
- private const val SYSTEM_PROPERTY_OVERRIDE_KEY = "sys.wmshell.desktopmode.dev_toggle_override"
-
- /**
* Local cache for toggle override, which is initialized once on its first access. It needs to
- * be refreshed only on reboots as overridden state takes effect on reboots.
+ * be refreshed only on reboots as overridden state is expected to take effect on reboots.
*/
private var cachedToggleOverride: ToggleOverride? = null
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
index 64a1b0c..140d776 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
@@ -30,6 +30,7 @@
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Size;
+import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
@@ -42,9 +43,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
@@ -69,26 +68,36 @@
@Retention(RetentionPolicy.SOURCE)
public @interface StashType {}
+ public static final int NAMED_KCA_LAUNCHER_SHELF = 0;
+ public static final int NAMED_KCA_TABLETOP_MODE = 1;
+
+ @IntDef(prefix = { "NAMED_KCA_" }, value = {
+ NAMED_KCA_LAUNCHER_SHELF,
+ NAMED_KCA_TABLETOP_MODE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NamedKca {}
+
private static final String TAG = PipBoundsState.class.getSimpleName();
- private final @NonNull Rect mBounds = new Rect();
- private final @NonNull Rect mMovementBounds = new Rect();
- private final @NonNull Rect mNormalBounds = new Rect();
- private final @NonNull Rect mExpandedBounds = new Rect();
- private final @NonNull Rect mNormalMovementBounds = new Rect();
- private final @NonNull Rect mExpandedMovementBounds = new Rect();
- private final @NonNull PipDisplayLayoutState mPipDisplayLayoutState;
+ @NonNull private final Rect mBounds = new Rect();
+ @NonNull private final Rect mMovementBounds = new Rect();
+ @NonNull private final Rect mNormalBounds = new Rect();
+ @NonNull private final Rect mExpandedBounds = new Rect();
+ @NonNull private final Rect mNormalMovementBounds = new Rect();
+ @NonNull private final Rect mExpandedMovementBounds = new Rect();
+ @NonNull private final PipDisplayLayoutState mPipDisplayLayoutState;
private final Point mMaxSize = new Point();
private final Point mMinSize = new Point();
- private final @NonNull Context mContext;
+ @NonNull private final Context mContext;
private float mAspectRatio;
private int mStashedState = STASH_TYPE_NONE;
private int mStashOffset;
- private @Nullable PipReentryState mPipReentryState;
+ @Nullable private PipReentryState mPipReentryState;
private final LauncherState mLauncherState = new LauncherState();
- private final @NonNull SizeSpecSource mSizeSpecSource;
- private @Nullable ComponentName mLastPipComponentName;
- private final @NonNull MotionBoundsState mMotionBoundsState = new MotionBoundsState();
+ @NonNull private final SizeSpecSource mSizeSpecSource;
+ @Nullable private ComponentName mLastPipComponentName;
+ @NonNull private final MotionBoundsState mMotionBoundsState = new MotionBoundsState();
private boolean mIsImeShowing;
private int mImeHeight;
private boolean mIsShelfShowing;
@@ -120,12 +129,18 @@
* as unrestricted keep clear area. Values in this map would be appended to
* {@link #getUnrestrictedKeepClearAreas()} and this is meant for internal usage only.
*/
- private final Map<String, Rect> mNamedUnrestrictedKeepClearAreas = new HashMap<>();
+ private final SparseArray<Rect> mNamedUnrestrictedKeepClearAreas = new SparseArray<>();
- private @Nullable Runnable mOnMinimalSizeChangeCallback;
- private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
- private List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
- private List<Consumer<Float>> mOnAspectRatioChangedCallbacks = new ArrayList<>();
+ @Nullable private Runnable mOnMinimalSizeChangeCallback;
+ @Nullable private TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
+ private final List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
+ private final List<Consumer<Float>> mOnAspectRatioChangedCallbacks = new ArrayList<>();
+
+ /**
+ * This is used to set the launcher shelf height ahead of non-auto-enter-pip animation,
+ * to avoid the race condition. See also {@link #NAMED_KCA_LAUNCHER_SHELF}.
+ */
+ public final Rect mCachedLauncherShelfHeightKeepClearArea = new Rect();
// the size of the current bounds relative to the max size spec
private float mBoundsScale;
@@ -430,17 +445,32 @@
mUnrestrictedKeepClearAreas.addAll(unrestrictedAreas);
}
- /** Add a named unrestricted keep clear area. */
- public void addNamedUnrestrictedKeepClearArea(@NonNull String name, Rect unrestrictedArea) {
- mNamedUnrestrictedKeepClearAreas.put(name, unrestrictedArea);
+ /** Set a named unrestricted keep clear area. */
+ public void setNamedUnrestrictedKeepClearArea(
+ @NamedKca int tag, @Nullable Rect unrestrictedArea) {
+ if (unrestrictedArea == null) {
+ mNamedUnrestrictedKeepClearAreas.remove(tag);
+ } else {
+ mNamedUnrestrictedKeepClearAreas.put(tag, unrestrictedArea);
+ if (tag == NAMED_KCA_LAUNCHER_SHELF) {
+ mCachedLauncherShelfHeightKeepClearArea.set(unrestrictedArea);
+ }
+ }
}
- /** Remove a named unrestricted keep clear area. */
- public void removeNamedUnrestrictedKeepClearArea(@NonNull String name) {
- mNamedUnrestrictedKeepClearAreas.remove(name);
+ /**
+ * Forcefully set the keep-clear-area for launcher shelf height if applicable.
+ * This is used for entering PiP in button navigation mode to make sure the destination bounds
+ * calculation includes the shelf height, to avoid race conditions that such callback is sent
+ * from Launcher after the entering animation is started.
+ */
+ public void mayUseCachedLauncherShelfHeight() {
+ if (!mCachedLauncherShelfHeightKeepClearArea.isEmpty()) {
+ setNamedUnrestrictedKeepClearArea(
+ NAMED_KCA_LAUNCHER_SHELF, mCachedLauncherShelfHeightKeepClearArea);
+ }
}
-
/**
* @return restricted keep clear areas.
*/
@@ -454,9 +484,12 @@
*/
@NonNull
public Set<Rect> getUnrestrictedKeepClearAreas() {
- if (mNamedUnrestrictedKeepClearAreas.isEmpty()) return mUnrestrictedKeepClearAreas;
+ if (mNamedUnrestrictedKeepClearAreas.size() == 0) return mUnrestrictedKeepClearAreas;
final Set<Rect> unrestrictedAreas = new ArraySet<>(mUnrestrictedKeepClearAreas);
- unrestrictedAreas.addAll(mNamedUnrestrictedKeepClearAreas.values());
+ for (int i = 0; i < mNamedUnrestrictedKeepClearAreas.size(); i++) {
+ final int key = mNamedUnrestrictedKeepClearAreas.keyAt(i);
+ unrestrictedAreas.add(mNamedUnrestrictedKeepClearAreas.get(key));
+ }
return unrestrictedAreas;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 80f6a63..700742a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -534,7 +534,8 @@
MultiInstanceHelper multiInstanceHelper,
@ShellMainThread ShellExecutor mainExecutor,
Optional<DesktopTasksLimiter> desktopTasksLimiter,
- Optional<RecentTasksController> recentTasksController) {
+ Optional<RecentTasksController> recentTasksController,
+ InteractionJankMonitor interactionJankMonitor) {
return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
dragAndDropController, transitions, keyguardManager, enterDesktopTransitionHandler,
@@ -542,7 +543,8 @@
dragToDesktopTransitionHandler, desktopModeTaskRepository,
desktopModeLoggerTransitionObserver, launchAdjacentController,
recentsTransitionHandler, multiInstanceHelper,
- mainExecutor, desktopTasksLimiter, recentTasksController.orElse(null));
+ mainExecutor, desktopTasksLimiter, recentTasksController.orElse(null),
+ interactionJankMonitor);
}
@WMSingleton
@@ -568,9 +570,10 @@
Context context,
Transitions transitions,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
- Optional<DesktopTasksLimiter> desktopTasksLimiter) {
+ Optional<DesktopTasksLimiter> desktopTasksLimiter,
+ InteractionJankMonitor interactionJankMonitor) {
return new DragToDesktopTransitionHandler(context, transitions,
- rootTaskDisplayAreaOrganizer);
+ rootTaskDisplayAreaOrganizer, interactionJankMonitor);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index de901b5..9fd2c27 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -49,6 +49,9 @@
import android.window.WindowContainerTransaction
import androidx.annotation.BinderThread
import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.policy.ScreenDecorationsUtils
import com.android.internal.protolog.ProtoLog
import com.android.window.flags.Flags
@@ -123,7 +126,8 @@
private val multiInstanceHelper: MultiInstanceHelper,
@ShellMainThread private val mainExecutor: ShellExecutor,
private val desktopTasksLimiter: Optional<DesktopTasksLimiter>,
- private val recentTasksController: RecentTasksController?
+ private val recentTasksController: RecentTasksController?,
+ private val interactionJankMonitor: InteractionJankMonitor
) :
RemoteCallable<DesktopTasksController>,
Transitions.TransitionHandler,
@@ -378,12 +382,15 @@
fun startDragToDesktop(
taskInfo: RunningTaskInfo,
dragToDesktopValueAnimator: MoveToDesktopAnimator,
+ taskSurface: SurfaceControl,
) {
ProtoLog.v(
WM_SHELL_DESKTOP_MODE,
"DesktopTasksController: startDragToDesktop taskId=%d",
taskInfo.taskId
)
+ interactionJankMonitor.begin(taskSurface, context,
+ CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
dragToDesktopTransitionHandler.startDragToDesktopTransition(
taskInfo.taskId,
dragToDesktopValueAnimator
@@ -1340,13 +1347,19 @@
fun onDragPositioningEndThroughStatusBar(
inputCoordinates: PointF,
taskInfo: RunningTaskInfo,
+ taskSurface: SurfaceControl,
): IndicatorType {
+ // End the drag_hold CUJ interaction.
+ interactionJankMonitor.end(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
val indicator = getVisualIndicator() ?: return IndicatorType.NO_INDICATOR
val indicatorType = indicator.updateIndicatorType(inputCoordinates, taskInfo.windowingMode)
when (indicatorType) {
IndicatorType.TO_DESKTOP_INDICATOR -> {
val displayLayout = displayController.getDisplayLayout(taskInfo.displayId)
?: return IndicatorType.NO_INDICATOR
+ // Start a new jank interaction for the drag release to desktop window animation.
+ interactionJankMonitor.begin(taskSurface, context,
+ CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE, "to_desktop")
if (Flags.enableWindowingDynamicInitialBounds()) {
finalizeDragToDesktop(taskInfo, calculateInitialBounds(displayLayout, taskInfo))
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index ddee8fa..9e79eddb0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -30,6 +30,9 @@
import android.window.WindowContainerToken
import android.window.WindowContainerTransaction
import com.android.internal.protolog.ProtoLog
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
+import com.android.internal.jank.InteractionJankMonitor
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
@@ -57,17 +60,20 @@
private val context: Context,
private val transitions: Transitions,
private val taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
- private val transactionSupplier: Supplier<SurfaceControl.Transaction>
+ private val interactionJankMonitor: InteractionJankMonitor,
+ private val transactionSupplier: Supplier<SurfaceControl.Transaction>,
) : TransitionHandler {
constructor(
context: Context,
transitions: Transitions,
- rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ interactionJankMonitor: InteractionJankMonitor
) : this(
context,
transitions,
rootTaskDisplayAreaOrganizer,
+ interactionJankMonitor,
Supplier { SurfaceControl.Transaction() }
)
@@ -567,6 +573,8 @@
onTaskResizeAnimationListener.onAnimationEnd(state.draggedTaskId)
startTransitionFinishCb.onTransitionFinished(null /* null */)
clearState()
+ interactionJankMonitor.end(
+ CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE)
}
}
)
@@ -604,6 +612,10 @@
"DragToDesktop: onTransitionConsumed() start transition aborted"
)
state.startAborted = true
+ // Cancel CUJ interaction if the transition is aborted.
+ interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
+ } else if (state.cancelTransitionToken != transition) {
+ interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 9bb9d86..a52141c5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -1020,6 +1020,9 @@
mPipMenuController.attach(leash);
}
+ // Make sure we have the launcher shelf into destination bounds calculation
+ // before the animator starts.
+ mPipBoundsState.mayUseCachedLauncherShelfHeight();
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
final Rect currentBounds = pipChange.getStartAbsBounds();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 0cb7e17..7451d22 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -646,9 +646,9 @@
});
mTabletopModeController.registerOnTabletopModeChangedListener((isInTabletopMode) -> {
- final String tag = "tabletop-mode";
if (!isInTabletopMode) {
- mPipBoundsState.removeNamedUnrestrictedKeepClearArea(tag);
+ mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+ PipBoundsState.NAMED_KCA_TABLETOP_MODE, null);
return;
}
@@ -657,14 +657,16 @@
if (mTabletopModeController.getPreferredHalfInTabletopMode()
== TabletopModeController.PREFERRED_TABLETOP_HALF_TOP) {
// Prefer top, avoid the bottom half of the display.
- mPipBoundsState.addNamedUnrestrictedKeepClearArea(tag, new Rect(
- displayBounds.left, displayBounds.centerY(),
- displayBounds.right, displayBounds.bottom));
+ mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+ PipBoundsState.NAMED_KCA_TABLETOP_MODE, new Rect(
+ displayBounds.left, displayBounds.centerY(),
+ displayBounds.right, displayBounds.bottom));
} else {
// Prefer bottom, avoid the top half of the display.
- mPipBoundsState.addNamedUnrestrictedKeepClearArea(tag, new Rect(
- displayBounds.left, displayBounds.top,
- displayBounds.right, displayBounds.centerY()));
+ mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+ PipBoundsState.NAMED_KCA_TABLETOP_MODE, new Rect(
+ displayBounds.left, displayBounds.top,
+ displayBounds.right, displayBounds.centerY()));
}
// Try to move the PiP window if we have entered PiP mode.
@@ -916,10 +918,12 @@
0, mPipBoundsState.getDisplayBounds().bottom - height,
mPipBoundsState.getDisplayBounds().right,
mPipBoundsState.getDisplayBounds().bottom);
- mPipBoundsState.addNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG, rect);
+ mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+ PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, rect);
updatePipPositionForKeepClearAreas();
} else {
- mPipBoundsState.removeNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG);
+ mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+ PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, null);
// postpone moving in response to hide of Launcher in case there's another change
mMainExecutor.removeCallbacks(mMovePipInResponseToKeepClearAreasChangeCallback);
mMainExecutor.executeDelayed(
@@ -968,8 +972,8 @@
int launcherRotation, Rect hotseatKeepClearArea) {
// preemptively add the keep clear area for Hotseat, so that it is taken into account
// when calculating the entry destination bounds of PiP window
- mPipBoundsState.addNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG,
- hotseatKeepClearArea);
+ mPipBoundsState.setNamedUnrestrictedKeepClearArea(
+ PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, hotseatKeepClearArea);
onDisplayRotationChangedNotInPip(mContext, launcherRotation);
// cache current min/max size
Point minSize = mPipBoundsState.getMinSize();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index b3dab85..48d17ec6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -68,6 +68,7 @@
DismissSession mPendingDismiss = null;
EnterSession mPendingEnter = null;
TransitSession mPendingResize = null;
+ TransitSession mPendingRemotePassthrough = null;
private IBinder mAnimatingTransition = null;
private OneShotRemoteHandler mActiveRemoteHandler = null;
@@ -320,6 +321,11 @@
return mPendingResize != null && mPendingResize.mTransition == transition;
}
+ boolean isPendingPassThrough(IBinder transition) {
+ return mPendingRemotePassthrough != null &&
+ mPendingRemotePassthrough.mTransition == transition;
+ }
+
@Nullable
private TransitSession getPendingTransition(IBinder transition) {
if (isPendingEnter(transition)) {
@@ -331,6 +337,9 @@
} else if (isPendingResize(transition)) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved resize transition");
return mPendingResize;
+ } else if (isPendingPassThrough(transition)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved passThrough transition");
+ return mPendingRemotePassthrough;
}
return null;
}
@@ -378,6 +387,19 @@
extraTransitType, resizeAnim);
}
+ /** Sets a transition to enter split. */
+ void setRemotePassThroughTransition(@NonNull IBinder transition,
+ @Nullable RemoteTransition remoteTransition) {
+ mPendingRemotePassthrough = new TransitSession(
+ transition, null, null,
+ remoteTransition, Transitions.TRANSIT_SPLIT_PASSTHROUGH);
+
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ + " deduced remote passthrough split screen");
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setRemotePassThrough: transitType=%d remote=%s",
+ Transitions.TRANSIT_SPLIT_PASSTHROUGH, remoteTransition);
+ }
+
/** Starts a transition to dismiss split. */
IBinder startDismissTransition(WindowContainerTransaction wct,
Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
@@ -474,6 +496,12 @@
mPendingResize.onConsumed(aborted);
mPendingResize = null;
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for resize transition");
+ } else if (isPendingPassThrough(transition)) {
+ mPendingRemotePassthrough.onConsumed(aborted);
+ mPendingRemotePassthrough.mRemoteHandler.onTransitionConsumed(transition, aborted,
+ finishT);
+ mPendingRemotePassthrough = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for passThrough transition");
}
// TODO: handle transition consumed for active remote handler
@@ -495,6 +523,10 @@
mPendingResize.onFinished(wct, mFinishTransaction);
mPendingResize = null;
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for resize transition");
+ } else if (isPendingPassThrough(mAnimatingTransition)) {
+ mPendingRemotePassthrough.onFinished(wct, mFinishTransaction);
+ mPendingRemotePassthrough = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for passThrough transition");
}
mActiveRemoteHandler = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index a4f32c4..d7ee563 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -2710,7 +2710,7 @@
@Nullable TransitionRequestInfo request) {
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
if (triggerTask == null) {
- if (isSplitScreenVisible()) {
+ if (isSplitActive()) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d display rotation",
request.getDebugId());
// Check if the display is rotating.
@@ -2720,6 +2720,10 @@
&& displayChange.getStartRotation() != displayChange.getEndRotation()) {
mSplitLayout.setFreezeDividerWindow(true);
}
+ if (request.getRemoteTransition() != null) {
+ mSplitTransitions.setRemotePassThroughTransition(transition,
+ request.getRemoteTransition());
+ }
// Still want to monitor everything while in split-screen, so return non-null.
return new WindowContainerTransaction();
} else {
@@ -3046,6 +3050,13 @@
notifySplitAnimationFinished();
return true;
}
+ } else if (mSplitTransitions.isPendingPassThrough(transition)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startAnimation: passThrough transition=%d", info.getDebugId());
+ mSplitTransitions.mPendingRemotePassthrough.mRemoteHandler.startAnimation(transition,
+ info, startTransaction, finishTransaction, finishCallback);
+ notifySplitAnimationFinished();
+ return true;
}
return startPendingAnimation(transition, info, startTransaction, finishTransaction,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index fc8b1d2..874cca5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -190,6 +190,9 @@
// TRANSIT_FIRST_CUSTOM + 17
TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_DRAG_RESIZE;
+ /** Remote Transition that split accepts but ultimately needs to be animated by the remote. */
+ public static final int TRANSIT_SPLIT_PASSTHROUGH = TRANSIT_FIRST_CUSTOM + 18;
+
/** Transition type for desktop mode transitions. */
public static final int TRANSIT_DESKTOP_MODE_TYPES =
WindowManager.TRANSIT_FIRST_CUSTOM + 100;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 8312aef..0e8fd7c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -970,7 +970,9 @@
relevantDecor.updateHoverAndPressStatus(ev);
DesktopModeVisualIndicator.IndicatorType resultType =
mDesktopTasksController.onDragPositioningEndThroughStatusBar(
- new PointF(ev.getRawX(), ev.getRawY()), relevantDecor.mTaskInfo);
+ new PointF(ev.getRawX(), ev.getRawY()),
+ relevantDecor.mTaskInfo,
+ relevantDecor.mTaskSurface);
// If we are entering split select, handle will no longer be visible and
// should not be receiving any input.
if (resultType == TO_SPLIT_LEFT_INDICATOR
@@ -1010,7 +1012,7 @@
mContext, mDragToDesktopAnimationStartBounds,
relevantDecor.mTaskInfo, relevantDecor.mTaskSurface);
mDesktopTasksController.startDragToDesktop(relevantDecor.mTaskInfo,
- mMoveToDesktopAnimator);
+ mMoveToDesktopAnimator, relevantDecor.mTaskSurface);
}
}
if (mMoveToDesktopAnimator != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
index da26898..3fd3656 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
@@ -19,7 +19,11 @@
import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_HOVER_ENTER;
+import static android.view.MotionEvent.ACTION_HOVER_EXIT;
+import static android.view.MotionEvent.ACTION_HOVER_MOVE;
import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
import android.graphics.PointF;
@@ -43,7 +47,7 @@
private final PointF mInputDownPoint = new PointF();
private int mTouchSlop;
private boolean mIsDragEvent;
- private int mDragPointerId;
+ private int mDragPointerId = -1;
private boolean mResultOfDownAction;
@@ -67,7 +71,7 @@
*
* @return the result returned by {@link #mEventHandler}, or the result when
* {@link #mEventHandler} handles the previous down event if the event shouldn't be passed
- */
+ */
boolean onMotionEvent(View v, MotionEvent ev) {
final boolean isTouchScreen =
(ev.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
@@ -86,10 +90,14 @@
return mResultOfDownAction;
}
case ACTION_MOVE: {
- if (ev.findPointerIndex(mDragPointerId) == -1) {
- mDragPointerId = ev.getPointerId(0);
+ if (mDragPointerId == -1) {
+ // The primary pointer was lifted, ignore the rest of the gesture.
+ return mResultOfDownAction;
}
final int dragPointerIndex = ev.findPointerIndex(mDragPointerId);
+ if (dragPointerIndex == -1) {
+ throw new IllegalStateException("Failed to find primary pointer!");
+ }
if (!mIsDragEvent) {
float dx = ev.getRawX(dragPointerIndex) - mInputDownPoint.x;
float dy = ev.getRawY(dragPointerIndex) - mInputDownPoint.y;
@@ -99,22 +107,52 @@
}
// The event handler should only be notified about 'move' events if a drag has been
// detected.
- if (mIsDragEvent) {
- return mEventHandler.handleMotionEvent(v, ev);
- } else {
+ if (!mIsDragEvent) {
return mResultOfDownAction;
}
+ return mEventHandler.handleMotionEvent(v,
+ getSinglePointerEvent(ev, mDragPointerId));
+ }
+ case ACTION_HOVER_ENTER:
+ case ACTION_HOVER_MOVE:
+ case ACTION_HOVER_EXIT: {
+ return mEventHandler.handleMotionEvent(v,
+ getSinglePointerEvent(ev, mDragPointerId));
+ }
+ case ACTION_POINTER_UP: {
+ if (mDragPointerId == -1) {
+ // The primary pointer was lifted, ignore the rest of the gesture.
+ return mResultOfDownAction;
+ }
+ if (mDragPointerId != ev.getPointerId(ev.getActionIndex())) {
+ // Ignore a secondary pointer being lifted.
+ return mResultOfDownAction;
+ }
+ // The primary pointer is being lifted.
+ final int dragPointerId = mDragPointerId;
+ mDragPointerId = -1;
+ return mEventHandler.handleMotionEvent(v, getSinglePointerEvent(ev, dragPointerId));
}
case ACTION_UP:
case ACTION_CANCEL: {
+ final int dragPointerId = mDragPointerId;
resetState();
- return mEventHandler.handleMotionEvent(v, ev);
+ if (dragPointerId == -1) {
+ // The primary pointer was lifted, ignore the rest of the gesture.
+ return mResultOfDownAction;
+ }
+ return mEventHandler.handleMotionEvent(v, getSinglePointerEvent(ev, dragPointerId));
}
default:
- return mEventHandler.handleMotionEvent(v, ev);
+ // Ignore other events.
+ return mResultOfDownAction;
}
}
+ private static MotionEvent getSinglePointerEvent(MotionEvent ev, int pointerId) {
+ return ev.getPointerCount() > 1 ? ev.split(1 << pointerId) : ev;
+ }
+
void setTouchSlop(int touchSlop) {
mTouchSlop = touchSlop;
}
@@ -129,4 +167,4 @@
interface MotionEventHandler {
boolean handleMotionEvent(@Nullable View v, MotionEvent ev);
}
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 8421365..37510ef4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -66,6 +66,7 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.ExtendedMockito.never
import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.internal.jank.InteractionJankMonitor
import com.android.window.flags.Flags
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
import com.android.wm.shell.MockToken
@@ -166,6 +167,9 @@
@Mock lateinit var desktopModeLoggerTransitionObserver: DesktopModeLoggerTransitionObserver
@Mock lateinit var desktopModeVisualIndicator: DesktopModeVisualIndicator
@Mock lateinit var recentTasksController: RecentTasksController
+ @Mock
+ private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
+ @Mock private lateinit var mockSurface: SurfaceControl
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var controller: DesktopTasksController
@@ -248,7 +252,8 @@
multiInstanceHelper,
shellExecutor,
Optional.of(desktopTasksLimiter),
- recentTasksController)
+ recentTasksController,
+ mockInteractionJankMonitor)
}
@After
@@ -2016,7 +2021,7 @@
val task = setUpFullscreenTask()
setUpLandscapeDisplay()
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
val wct = getLatestDragToDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
}
@@ -2032,7 +2037,7 @@
val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
setUpLandscapeDisplay()
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
val wct = getLatestDragToDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
}
@@ -2049,7 +2054,7 @@
setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_PORTRAIT, shouldLetterbox = true)
setUpLandscapeDisplay()
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
val wct = getLatestDragToDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_PORTRAIT_BOUNDS)
}
@@ -2066,7 +2071,7 @@
setUpFullscreenTask(isResizable = false, screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
setUpLandscapeDisplay()
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
val wct = getLatestDragToDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
}
@@ -2086,7 +2091,7 @@
shouldLetterbox = true)
setUpLandscapeDisplay()
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
val wct = getLatestDragToDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
}
@@ -2102,7 +2107,7 @@
val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT)
setUpPortraitDisplay()
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
val wct = getLatestDragToDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
}
@@ -2121,7 +2126,7 @@
screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
setUpPortraitDisplay()
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
val wct = getLatestDragToDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
}
@@ -2141,7 +2146,7 @@
shouldLetterbox = true)
setUpPortraitDisplay()
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
val wct = getLatestDragToDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_LANDSCAPE_BOUNDS)
}
@@ -2161,7 +2166,7 @@
screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
setUpPortraitDisplay()
- spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task, mockSurface)
val wct = getLatestDragToDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
}
@@ -2182,7 +2187,7 @@
shouldLetterbox = true)
setUpPortraitDisplay()
- spyController.onDragPositioningEndThroughStatusBar(PointF(200f, 200f), task)
+ spyController.onDragPositioningEndThroughStatusBar(PointF(200f, 200f), task, mockSurface)
val wct = getLatestDragToDesktopWct()
assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
index bbf523b..e4e2bd2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -15,6 +15,7 @@
import android.window.TransitionInfo.FLAG_IS_WALLPAPER
import android.window.WindowContainerTransaction
import androidx.test.filters.SmallTest
+import com.android.internal.jank.InteractionJankMonitor
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
@@ -51,6 +52,8 @@
@Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
@Mock private lateinit var splitScreenController: SplitScreenController
@Mock private lateinit var dragAnimator: MoveToDesktopAnimator
+ @Mock
+ private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
private val transactionSupplier = Supplier { mock<SurfaceControl.Transaction>() }
@@ -63,7 +66,8 @@
context,
transitions,
taskDisplayAreaOrganizer,
- transactionSupplier
+ mockInteractionJankMonitor,
+ transactionSupplier,
)
.apply { setSplitScreenController(splitScreenController) }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt
index b1d62f4..dd19d76 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt
@@ -184,108 +184,6 @@
}
@Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
- @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- fun isEnabled_noSystemProperty_overrideOn_featureFlagOff_returnsTrueAndStoresPropertyOn() {
- System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
- setOverride(OVERRIDE_ON.setting)
-
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
- // Store System Property if not present
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(OVERRIDE_ON.setting.toString())
- }
-
- @Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- fun isEnabled_noSystemProperty_overrideUnset_featureFlagOn_returnsTrueAndStoresPropertyUnset() {
- System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
- setOverride(OVERRIDE_UNSET.setting)
-
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
- // Store System Property if not present
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(OVERRIDE_UNSET.setting.toString())
- }
-
- @Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
- @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- fun isEnabled_noSystemProperty_overrideUnset_featureFlagOff_returnsFalseAndStoresPropertyUnset() {
- System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
- setOverride(OVERRIDE_UNSET.setting)
-
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
- // Store System Property if not present
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(OVERRIDE_UNSET.setting.toString())
- }
-
- @Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- @Suppress("ktlint:standard:max-line-length")
- fun isEnabled_systemPropertyNotInteger_overrideOff_featureFlagOn_returnsFalseAndStoresPropertyOff() {
- System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "abc")
- setOverride(OVERRIDE_OFF.setting)
-
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
- // Store System Property if currently invalid
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(OVERRIDE_OFF.setting.toString())
- }
-
- @Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- @Suppress("ktlint:standard:max-line-length")
- fun isEnabled_systemPropertyInvalidInteger_overrideOff_featureFlagOn_returnsFalseAndStoresPropertyOff() {
- System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "-2")
- setOverride(OVERRIDE_OFF.setting)
-
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
- // Store System Property if currently invalid
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(OVERRIDE_OFF.setting.toString())
- }
-
- @Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- fun isEnabled_systemPropertyOff_overrideOn_featureFlagOn_returnsFalseAndDoesNotUpdateProperty() {
- System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_OFF.setting.toString())
- setOverride(OVERRIDE_ON.setting)
-
- // Have a consistent override until reboot
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(OVERRIDE_OFF.setting.toString())
- }
-
- @Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
- @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- fun isEnabled_systemPropertyOn_overrideOff_featureFlagOff_returnsTrueAndDoesNotUpdateProperty() {
- System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_ON.setting.toString())
- setOverride(OVERRIDE_OFF.setting)
-
- // Have a consistent override until reboot
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(OVERRIDE_ON.setting.toString())
- }
-
- @Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- @Suppress("ktlint:standard:max-line-length")
- fun isEnabled_systemPropertyUnset_overrideOff_featureFlagOn_returnsTrueAndDoesNotUpdateProperty() {
- System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_UNSET.setting.toString())
- setOverride(OVERRIDE_OFF.setting)
-
- // Have a consistent override until reboot
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(OVERRIDE_UNSET.setting.toString())
- }
-
- @Test
@EnableFlags(
FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION,
FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
@@ -445,12 +343,5 @@
DesktopModeFlags::class.java.getDeclaredField("cachedToggleOverride")
cachedToggleOverride.isAccessible = true
cachedToggleOverride.set(null, null)
-
- // Clear override cache stored in System property
- System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
- }
-
- private companion object {
- const val SYSTEM_PROPERTY_OVERRIDE_KEY = "sys.wmshell.desktopmode.dev_toggle_override"
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 37ef788..22b408c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -51,8 +51,10 @@
import android.app.ActivityManager;
import android.os.Handler;
import android.os.IBinder;
+import android.os.RemoteException;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
+import android.window.IRemoteTransition;
import android.window.RemoteTransition;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -328,6 +330,32 @@
@Test
@UiThreadTest
+ public void testRemotePassThroughInvoked() throws RemoteException {
+ RemoteTransition remoteWrapper = mock(RemoteTransition.class);
+ IRemoteTransition remoteTransition = mock(IRemoteTransition.class);
+ IBinder remoteBinder = mock(IBinder.class);
+ doReturn(remoteBinder).when(remoteTransition).asBinder();
+ doReturn(remoteTransition).when(remoteWrapper).getRemoteTransition();
+
+ TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_CHANGE, null,
+ remoteWrapper);
+ IBinder transition = mock(IBinder.class);
+ mMainStage.activate(new WindowContainerTransaction(), false);
+ mStageCoordinator.handleRequest(transition, request);
+ TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CHANGE, 0)
+ .build();
+ boolean accepted = mStageCoordinator.startAnimation(transition, info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(Transitions.TransitionFinishCallback.class));
+ assertTrue(accepted);
+
+ verify(remoteTransition, times(1)).startAnimation(any(),
+ any(), any(), any());
+ }
+
+ @Test
+ @UiThreadTest
public void testEnterRecentsAndRestore() {
enterSplit();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
index 3fbab0f..56224b4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt
@@ -85,6 +85,23 @@
}
@Test
+ fun testNoMove_mouse_passesDownAndUp() {
+ assertTrue(dragDetector.onMotionEvent(
+ createMotionEvent(MotionEvent.ACTION_DOWN, isTouch = false)))
+ verify(eventHandler).handleMotionEvent(any(), argThat {
+ return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+ it.source == InputDevice.SOURCE_MOUSE
+ })
+
+ assertTrue(dragDetector.onMotionEvent(
+ createMotionEvent(MotionEvent.ACTION_UP, isTouch = false)))
+ verify(eventHandler).handleMotionEvent(any(), argThat {
+ return@argThat it.action == MotionEvent.ACTION_UP && it.x == X && it.y == Y &&
+ it.source == InputDevice.SOURCE_MOUSE
+ })
+ }
+
+ @Test
fun testMoveInSlop_touch_passesDownAndUp() {
`when`(eventHandler.handleMotionEvent(any(), argThat {
return@argThat it.action == MotionEvent.ACTION_DOWN
@@ -166,6 +183,52 @@
}
@Test
+ fun testDownMoveDown_shouldIgnoreTheSecondDownMotion() {
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+ verify(eventHandler).handleMotionEvent(any(), argThat {
+ return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+ it.source == InputDevice.SOURCE_TOUCHSCREEN
+ })
+
+ val newX = X + SLOP + 1
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y)))
+ verify(eventHandler).handleMotionEvent(any(), argThat {
+ return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+ it.source == InputDevice.SOURCE_TOUCHSCREEN
+ })
+
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+ verify(eventHandler).handleMotionEvent(any(), argThat {
+ return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+ it.source == InputDevice.SOURCE_TOUCHSCREEN
+ })
+ }
+
+ @Test
+ fun testDownMouseMoveDownTouch_shouldIgnoreTheTouchDownMotion() {
+ assertTrue(dragDetector.onMotionEvent(
+ createMotionEvent(MotionEvent.ACTION_DOWN, isTouch = false)))
+ verify(eventHandler).handleMotionEvent(any(), argThat {
+ return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y &&
+ it.source == InputDevice.SOURCE_MOUSE
+ })
+
+ val newX = X + SLOP + 1
+ assertTrue(dragDetector.onMotionEvent(
+ createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y, isTouch = false)))
+ verify(eventHandler).handleMotionEvent(any(), argThat {
+ return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+ it.source == InputDevice.SOURCE_MOUSE
+ })
+
+ assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN)))
+ verify(eventHandler).handleMotionEvent(any(), argThat {
+ return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y &&
+ it.source == InputDevice.SOURCE_MOUSE
+ })
+ }
+
+ @Test
fun testPassesHoverEnter() {
`when`(eventHandler.handleMotionEvent(any(), argThat {
it.action == MotionEvent.ACTION_HOVER_ENTER
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index ad963dd..93118aea 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -40,6 +40,7 @@
namespace android {
namespace uirenderer {
+std::mutex TestUtils::sMutex;
std::unordered_map<int, TestUtils::CallCounts> TestUtils::sMockFunctorCounts{};
SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end) {
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 0ede902..8ab2b16 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -305,22 +305,26 @@
.onSync =
[](int functor, void* client_data, const WebViewSyncData& data) {
expectOnRenderThread("onSync");
+ std::scoped_lock lock(sMutex);
sMockFunctorCounts[functor].sync++;
},
.onContextDestroyed =
[](int functor, void* client_data) {
expectOnRenderThread("onContextDestroyed");
+ std::scoped_lock lock(sMutex);
sMockFunctorCounts[functor].contextDestroyed++;
},
.onDestroyed =
[](int functor, void* client_data) {
expectOnRenderThread("onDestroyed");
+ std::scoped_lock lock(sMutex);
sMockFunctorCounts[functor].destroyed++;
},
.removeOverlays =
[](int functor, void* data,
void (*mergeTransaction)(ASurfaceTransaction*)) {
expectOnRenderThread("removeOverlays");
+ std::scoped_lock lock(sMutex);
sMockFunctorCounts[functor].removeOverlays++;
},
};
@@ -329,6 +333,7 @@
callbacks.gles.draw = [](int functor, void* client_data, const DrawGlInfo& params,
const WebViewOverlayData& overlay_params) {
expectOnRenderThread("draw");
+ std::scoped_lock lock(sMutex);
sMockFunctorCounts[functor].glesDraw++;
};
break;
@@ -336,15 +341,18 @@
callbacks.vk.initialize = [](int functor, void* data,
const VkFunctorInitParams& params) {
expectOnRenderThread("initialize");
+ std::scoped_lock lock(sMutex);
sMockFunctorCounts[functor].vkInitialize++;
};
callbacks.vk.draw = [](int functor, void* data, const VkFunctorDrawParams& params,
const WebViewOverlayData& overlayParams) {
expectOnRenderThread("draw");
+ std::scoped_lock lock(sMutex);
sMockFunctorCounts[functor].vkDraw++;
};
callbacks.vk.postDraw = [](int functor, void* data) {
expectOnRenderThread("postDraw");
+ std::scoped_lock lock(sMutex);
sMockFunctorCounts[functor].vkPostDraw++;
};
break;
@@ -352,11 +360,16 @@
return callbacks;
}
- static CallCounts& countsForFunctor(int functor) { return sMockFunctorCounts[functor]; }
+ static CallCounts copyCountsForFunctor(int functor) {
+ std::scoped_lock lock(sMutex);
+ return sMockFunctorCounts[functor];
+ }
static SkFont defaultFont();
private:
+ // guards sMockFunctorCounts
+ static std::mutex sMutex;
static std::unordered_map<int, CallCounts> sMockFunctorCounts;
static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index e727ea8..690a60a4 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -239,19 +239,21 @@
TestUtils::runOnRenderThreadUnmanaged([&] (RenderThread&) {
TestUtils::syncHierarchyPropertiesAndDisplayList(node);
});
- auto& counts = TestUtils::countsForFunctor(functor);
+ auto counts = TestUtils::copyCountsForFunctor(functor);
EXPECT_EQ(1, counts.sync);
EXPECT_EQ(0, counts.destroyed);
TestUtils::recordNode(*node, [&](Canvas& canvas) {
canvas.drawWebViewFunctor(functor);
});
+ counts = TestUtils::copyCountsForFunctor(functor);
EXPECT_EQ(1, counts.sync);
EXPECT_EQ(0, counts.destroyed);
TestUtils::runOnRenderThreadUnmanaged([&] (RenderThread&) {
TestUtils::syncHierarchyPropertiesAndDisplayList(node);
});
+ counts = TestUtils::copyCountsForFunctor(functor);
EXPECT_EQ(2, counts.sync);
EXPECT_EQ(0, counts.destroyed);
@@ -265,6 +267,7 @@
});
// Fence on any remaining post'd work
TestUtils::runOnRenderThreadUnmanaged([] (RenderThread&) {});
+ counts = TestUtils::copyCountsForFunctor(functor);
EXPECT_EQ(2, counts.sync);
EXPECT_EQ(1, counts.destroyed);
}
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 064d42e..26b4729 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -101,7 +101,7 @@
SkCanvas dummyCanvas;
int functor1 = TestUtils::createMockFunctor();
- auto& counts = TestUtils::countsForFunctor(functor1);
+ auto counts = TestUtils::copyCountsForFunctor(functor1);
skiaDL.mChildFunctors.push_back(
skiaDL.allocateDrawable<GLFunctorDrawable>(functor1, &dummyCanvas));
WebViewFunctor_release(functor1);
@@ -118,6 +118,7 @@
});
});
+ counts = TestUtils::copyCountsForFunctor(functor1);
EXPECT_EQ(counts.sync, 1);
EXPECT_EQ(counts.destroyed, 0);
EXPECT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
@@ -126,6 +127,7 @@
TestUtils::runOnRenderThread([](auto&) {
// Fence
});
+ counts = TestUtils::copyCountsForFunctor(functor1);
EXPECT_EQ(counts.destroyed, 1);
}
diff --git a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
index 5e8f13d..09ce98a 100644
--- a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
+++ b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
@@ -40,7 +40,7 @@
TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
// Empty, don't care
});
- auto& counts = TestUtils::countsForFunctor(functor);
+ auto counts = TestUtils::copyCountsForFunctor(functor);
// We never initialized, so contextDestroyed == 0
EXPECT_EQ(0, counts.contextDestroyed);
EXPECT_EQ(1, counts.destroyed);
@@ -59,7 +59,7 @@
TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
// fence
});
- auto& counts = TestUtils::countsForFunctor(functor);
+ auto counts = TestUtils::copyCountsForFunctor(functor);
EXPECT_EQ(0, counts.sync);
EXPECT_EQ(0, counts.contextDestroyed);
EXPECT_EQ(0, counts.destroyed);
@@ -69,6 +69,7 @@
handle->sync(syncData);
});
+ counts = TestUtils::copyCountsForFunctor(functor);
EXPECT_EQ(1, counts.sync);
TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
@@ -76,6 +77,7 @@
handle->sync(syncData);
});
+ counts = TestUtils::copyCountsForFunctor(functor);
EXPECT_EQ(2, counts.sync);
handle.clear();
@@ -84,6 +86,7 @@
// fence
});
+ counts = TestUtils::copyCountsForFunctor(functor);
EXPECT_EQ(2, counts.sync);
EXPECT_EQ(0, counts.contextDestroyed);
EXPECT_EQ(1, counts.destroyed);
@@ -98,7 +101,6 @@
auto handle = WebViewFunctorManager::instance().handleFor(functor);
ASSERT_TRUE(handle);
WebViewFunctor_release(functor);
- auto& counts = TestUtils::countsForFunctor(functor);
for (int i = 0; i < 5; i++) {
TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
WebViewSyncData syncData;
@@ -112,6 +114,7 @@
TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
// fence
});
+ auto counts = TestUtils::copyCountsForFunctor(functor);
EXPECT_EQ(5, counts.sync);
EXPECT_EQ(10, counts.glesDraw);
EXPECT_EQ(1, counts.contextDestroyed);
@@ -127,13 +130,13 @@
auto handle = WebViewFunctorManager::instance().handleFor(functor);
ASSERT_TRUE(handle);
WebViewFunctor_release(functor);
- auto& counts = TestUtils::countsForFunctor(functor);
TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
WebViewSyncData syncData;
handle->sync(syncData);
DrawGlInfo drawInfo;
handle->drawGl(drawInfo);
});
+ auto counts = TestUtils::copyCountsForFunctor(functor);
EXPECT_EQ(1, counts.sync);
EXPECT_EQ(1, counts.glesDraw);
EXPECT_EQ(0, counts.contextDestroyed);
@@ -141,6 +144,7 @@
TestUtils::runOnRenderThreadUnmanaged([](auto& rt) {
rt.destroyRenderingContext();
});
+ counts = TestUtils::copyCountsForFunctor(functor);
EXPECT_EQ(1, counts.sync);
EXPECT_EQ(1, counts.glesDraw);
EXPECT_EQ(1, counts.contextDestroyed);
@@ -151,6 +155,7 @@
DrawGlInfo drawInfo;
handle->drawGl(drawInfo);
});
+ counts = TestUtils::copyCountsForFunctor(functor);
EXPECT_EQ(2, counts.sync);
EXPECT_EQ(2, counts.glesDraw);
EXPECT_EQ(1, counts.contextDestroyed);
@@ -159,6 +164,7 @@
TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
// fence
});
+ counts = TestUtils::copyCountsForFunctor(functor);
EXPECT_EQ(2, counts.sync);
EXPECT_EQ(2, counts.glesDraw);
EXPECT_EQ(2, counts.contextDestroyed);
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index d6345ce..f36344a 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@
allprojects {
extra["androidTop"] = androidTop
- extra["jetpackComposeVersion"] = "1.7.0-beta02"
+ extra["jetpackComposeVersion"] = "1.7.0-beta05"
}
subprojects {
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index a842009..1cca73a 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,7 +15,7 @@
#
[versions]
-agp = "8.5.0"
+agp = "8.5.1"
compose-compiler = "1.5.11"
dexmaker-mockito = "2.28.3"
jvm = "17"
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.8-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.8-bin.zip
deleted file mode 100644
index 77e6ad3..0000000
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.8-bin.zip
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip
new file mode 100644
index 0000000..9a97e46
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
index e644113..2c35211 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index 91d2a3a..9f29c77 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -16,6 +16,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=gradle-8.8-bin.zip
+distributionUrl=gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/packages/SettingsLib/Spa/gradlew b/packages/SettingsLib/Spa/gradlew
index b740cf1..f5feea6 100755
--- a/packages/SettingsLib/Spa/gradlew
+++ b/packages/SettingsLib/Spa/gradlew
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+# SPDX-License-Identifier: Apache-2.0
+#
##############################################################################
#
@@ -84,7 +86,8 @@
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
-APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
+' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 492d7c0..ce3d96e 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -53,14 +53,14 @@
dependencies {
api(project(":SettingsLibColor"))
- api("androidx.appcompat:appcompat:1.7.0-rc01")
- api("androidx.compose.material3:material3:1.3.0-beta02")
+ api("androidx.appcompat:appcompat:1.7.0")
+ api("androidx.compose.material3:material3:1.3.0-beta04")
api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.8.0-beta02")
+ api("androidx.navigation:navigation-compose:2.8.0-beta05")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.11.0")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 5f23651..2b8b23e 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -282,5 +282,6 @@
Settings.Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS,
Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS,
Settings.Secure.MANDATORY_BIOMETRICS,
+ Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index c8da8af..cc5302b 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -441,5 +441,7 @@
VALIDATORS.put(Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, NONE_NEGATIVE_LONG_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_MOUSE_KEYS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.MANDATORY_BIOMETRICS, new InclusiveIntegerRangeValidator(0, 1));
+ VALIDATORS.put(Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
+ new InclusiveIntegerRangeValidator(0, 1));
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 1c02d3f..68e968f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -1004,6 +1004,7 @@
}
.thenIf(viewModel.isEditMode) {
Modifier.semantics {
+ onClick(clickActionLabel, null)
contentDescription = accessibilityLabel
val deleteAction =
CustomAccessibilityAction(removeWidgetActionLabel) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt
index 620892a..b4c1a2e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalPopupSection.kt
@@ -50,7 +50,6 @@
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
@@ -102,8 +101,6 @@
val interactionSource = remember { MutableInteractionSource() }
val focusRequester = remember { FocusRequester() }
- val context = LocalContext.current
-
LaunchedEffect(Unit) {
// Adding a delay to ensure the animation completes before requesting focus
delay(250)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 665be53..5b328b8 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -52,11 +52,19 @@
* and [onStop] methods.
*/
internal interface DragController {
- /** Drag the current scene by [delta] pixels. */
- fun onDrag(delta: Float)
+ /**
+ * Drag the current scene by [delta] pixels.
+ *
+ * @return the consumed [delta]
+ */
+ fun onDrag(delta: Float): Float
- /** Starts a transition to a target scene. */
- fun onStop(velocity: Float, canChangeScene: Boolean)
+ /**
+ * Starts a transition to a target scene.
+ *
+ * @return the consumed [velocity]
+ */
+ fun onStop(velocity: Float, canChangeScene: Boolean): Float
}
internal class DraggableHandlerImpl(
@@ -272,8 +280,10 @@
*
* @return the consumed delta
*/
- override fun onDrag(delta: Float) {
- if (delta == 0f || !isDrivingTransition || swipeTransition.isFinishing) return
+ override fun onDrag(delta: Float): Float {
+ if (delta == 0f || !isDrivingTransition || swipeTransition.isFinishing) {
+ return 0f
+ }
swipeTransition.dragOffset += delta
val (fromScene, acceleratedOffset) =
@@ -289,7 +299,7 @@
if (result == null) {
onStop(velocity = delta, canChangeScene = true)
- return
+ return 0f
}
if (
@@ -314,6 +324,8 @@
updateTransition(swipeTransition)
}
+
+ return delta
}
/**
@@ -351,10 +363,10 @@
}
}
- override fun onStop(velocity: Float, canChangeScene: Boolean) {
+ override fun onStop(velocity: Float, canChangeScene: Boolean): Float {
// The state was changed since the drag started; don't do anything.
if (!isDrivingTransition || swipeTransition.isFinishing) {
- return
+ return 0f
}
// Important: Make sure that all the code here references the current transition when
@@ -440,7 +452,7 @@
if (result == null) {
// We will not animate
swipeTransition.snapToScene(fromScene.key)
- return
+ return 0f
}
val newSwipeTransition =
@@ -462,6 +474,9 @@
animateTo(targetScene = fromScene, targetOffset = 0f)
}
}
+
+ // The onStop animation consumes any remaining velocity.
+ return velocity
}
/**
@@ -1081,17 +1096,13 @@
// TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is
// initiated in a nested child.
controller.onDrag(delta = offsetAvailable)
-
- offsetAvailable
},
onStop = { velocityAvailable ->
val controller = dragController ?: error("Should be called after onStart")
- controller.onStop(velocity = velocityAvailable, canChangeScene = canChangeScene)
-
- dragController = null
- // The onDragStopped animation consumes any remaining velocity.
- velocityAvailable
+ controller
+ .onStop(velocity = velocityAvailable, canChangeScene = canChangeScene)
+ .also { dragController = null }
},
)
}
@@ -1106,7 +1117,7 @@
internal const val OffsetVisibilityThreshold = 0.5f
private object NoOpDragController : DragController {
- override fun onDrag(delta: Float) {}
+ override fun onDrag(delta: Float) = 0f
- override fun onStop(velocity: Float, canChangeScene: Boolean) {}
+ override fun onStop(velocity: Float, canChangeScene: Boolean) = 0f
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index 7a5a84e..c8bbb14 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -212,7 +212,8 @@
draggableHandler: DraggableHandler,
startedPosition: Offset = Offset.Zero,
overSlop: Float = 0f,
- pointersDown: Int = 1
+ pointersDown: Int = 1,
+ expectedConsumed: Boolean = true,
): DragController {
val dragController =
draggableHandler.onDragStarted(
@@ -222,17 +223,23 @@
)
// MultiPointerDraggable will always call onDelta with the initial overSlop right after
- dragController.onDragDelta(pixels = overSlop)
+ dragController.onDragDelta(pixels = overSlop, expectedConsumed = expectedConsumed)
return dragController
}
- fun DragController.onDragDelta(pixels: Float) {
- onDrag(delta = pixels)
+ fun DragController.onDragDelta(pixels: Float, expectedConsumed: Boolean = true) {
+ val consumed = onDrag(delta = pixels)
+ assertThat(consumed).isEqualTo(if (expectedConsumed) pixels else 0f)
}
- fun DragController.onDragStopped(velocity: Float, canChangeScene: Boolean = true) {
- onStop(velocity, canChangeScene)
+ fun DragController.onDragStopped(
+ velocity: Float,
+ canChangeScene: Boolean = true,
+ expectedConsumed: Boolean = true
+ ) {
+ val consumed = onStop(velocity, canChangeScene)
+ assertThat(consumed).isEqualTo(if (expectedConsumed) velocity else 0f)
}
fun NestedScrollConnection.scroll(
@@ -360,10 +367,18 @@
@Test
fun onDragStartedWithoutActionsInBothDirections_stayIdle() = runGestureTest {
- onDragStarted(horizontalDraggableHandler, overSlop = up(fractionOfScreen = 0.3f))
+ onDragStarted(
+ horizontalDraggableHandler,
+ overSlop = up(fractionOfScreen = 0.3f),
+ expectedConsumed = false,
+ )
assertIdle(currentScene = SceneA)
- onDragStarted(horizontalDraggableHandler, overSlop = down(fractionOfScreen = 0.3f))
+ onDragStarted(
+ horizontalDraggableHandler,
+ overSlop = down(fractionOfScreen = 0.3f),
+ expectedConsumed = false,
+ )
assertIdle(currentScene = SceneA)
}
@@ -489,19 +504,19 @@
// start accelaratedScroll and scroll over to B -> null
val dragController2 = onDragStartedImmediately()
- dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f))
- dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f))
+ dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = false)
+ dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = false)
// here onDragStopped is already triggered, but subsequent onDelta/onDragStopped calls may
// still be called. Make sure that they don't crash or change the scene
- dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f))
+ dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = false)
dragController2.onDragStopped(velocity = 0f)
advanceUntilIdle()
assertIdle(SceneB)
// These events can still come in after the animation has settled
- dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f))
+ dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = false)
dragController2.onDragStopped(velocity = 0f)
assertIdle(SceneB)
}
@@ -845,7 +860,7 @@
assertThat(progress).isEqualTo(0.2f)
// this should be ignored, we are scrolling now!
- dragController.onDragStopped(-velocityThreshold)
+ dragController.onDragStopped(-velocityThreshold, expectedConsumed = false)
assertTransition(currentScene = SceneA)
nestedScroll.scroll(available = -offsetY10)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index ecafb17..b98400a 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -49,6 +49,21 @@
class MultiPointerDraggableTest {
@get:Rule val rule = createComposeRule()
+ private class SimpleDragController(
+ val onDrag: () -> Unit,
+ val onStop: () -> Unit,
+ ) : DragController {
+ override fun onDrag(delta: Float): Float {
+ onDrag()
+ return delta
+ }
+
+ override fun onStop(velocity: Float, canChangeScene: Boolean): Float {
+ onStop()
+ return velocity
+ }
+ }
+
@Test
fun cancellingPointerCallsOnDragStopped() {
val size = 200f
@@ -70,15 +85,10 @@
startDragImmediately = { false },
onDragStarted = { _, _, _ ->
started = true
- object : DragController {
- override fun onDrag(delta: Float) {
- dragged = true
- }
-
- override fun onStop(velocity: Float, canChangeScene: Boolean) {
- stopped = true
- }
- }
+ SimpleDragController(
+ onDrag = { dragged = true },
+ onStop = { stopped = true },
+ )
},
)
)
@@ -142,15 +152,10 @@
startDragImmediately = { true },
onDragStarted = { _, _, _ ->
started = true
- object : DragController {
- override fun onDrag(delta: Float) {
- dragged = true
- }
-
- override fun onStop(velocity: Float, canChangeScene: Boolean) {
- stopped = true
- }
- }
+ SimpleDragController(
+ onDrag = { dragged = true },
+ onStop = { stopped = true },
+ )
},
)
.pointerInput(Unit) {
@@ -218,15 +223,10 @@
startDragImmediately = { false },
onDragStarted = { _, _, _ ->
started = true
- object : DragController {
- override fun onDrag(delta: Float) {
- dragged = true
- }
-
- override fun onStop(velocity: Float, canChangeScene: Boolean) {
- stopped = true
- }
- }
+ SimpleDragController(
+ onDrag = { dragged = true },
+ onStop = { stopped = true },
+ )
},
)
) {
@@ -341,15 +341,10 @@
startDragImmediately = { false },
onDragStarted = { _, _, _ ->
started = true
- object : DragController {
- override fun onDrag(delta: Float) {
- dragged = true
- }
-
- override fun onStop(velocity: Float, canChangeScene: Boolean) {
- stopped = true
- }
- }
+ SimpleDragController(
+ onDrag = { dragged = true },
+ onStop = { stopped = true },
+ )
},
)
) {
@@ -447,15 +442,10 @@
startDragImmediately = { false },
onDragStarted = { _, _, _ ->
verticalStarted = true
- object : DragController {
- override fun onDrag(delta: Float) {
- verticalDragged = true
- }
-
- override fun onStop(velocity: Float, canChangeScene: Boolean) {
- verticalStopped = true
- }
- }
+ SimpleDragController(
+ onDrag = { verticalDragged = true },
+ onStop = { verticalStopped = true },
+ )
},
)
.multiPointerDraggable(
@@ -464,15 +454,10 @@
startDragImmediately = { false },
onDragStarted = { _, _, _ ->
horizontalStarted = true
- object : DragController {
- override fun onDrag(delta: Float) {
- horizontalDragged = true
- }
-
- override fun onStop(velocity: Float, canChangeScene: Boolean) {
- horizontalStopped = true
- }
- }
+ SimpleDragController(
+ onDrag = { horizontalDragged = true },
+ onStop = { horizontalStopped = true },
+ )
},
)
)
@@ -567,11 +552,10 @@
},
onDragStarted = { _, _, _ ->
started = true
- object : DragController {
- override fun onDrag(delta: Float) {}
-
- override fun onStop(velocity: Float, canChangeScene: Boolean) {}
- }
+ SimpleDragController(
+ onDrag = { /* do nothing */ },
+ onStop = { /* do nothing */ },
+ )
},
)
) {}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
index 693fcda..18839e6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
@@ -22,6 +22,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.classifier.falsingManager
import com.android.systemui.haptics.vibratorHelper
import com.android.systemui.kosmos.testScope
@@ -52,6 +53,7 @@
private val vibratorHelper = kosmos.vibratorHelper
private val qsTile = kosmos.qsTileFactory.createTile("Test Tile")
@Mock private lateinit var callback: QSLongPressEffect.Callback
+ @Mock private lateinit var controller: ActivityTransitionAnimator.Controller
private val effectDuration = 400
private val lowTickDuration = 12
@@ -218,8 +220,9 @@
// GIVEN that the animation completes
longPressEffect.handleAnimationComplete()
- // THEN the effect ends in the idle state.
+ // THEN the effect ends in the idle state and the reversed callback is used.
assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE)
+ verify(callback, times(1)).onEffectFinishedReversing()
}
@Test
@@ -348,6 +351,23 @@
assertThat(clickState).isEqualTo(QSLongPressEffect.State.IDLE)
}
+ @Test
+ fun onLongClickTransitionCancelled_whileInLongClickState_reversesEffect() =
+ testWhileInState(QSLongPressEffect.State.LONG_CLICKED) {
+ // GIVEN a transition controller delegate
+ val delegate = longPressEffect.createTransitionControllerDelegate(controller)
+
+ // WHEN the activity launch animation is cancelled
+ val newOccludedState = false
+ delegate.onTransitionAnimationCancelled(newOccludedState)
+
+ // THEN the effect reverses and ends in RUNNING_BACKWARDS_FROM_CANCEL
+ assertThat(longPressEffect.state)
+ .isEqualTo(QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_CANCEL)
+ verify(callback, times(1)).onReverseAnimator(false)
+ verify(controller).onTransitionAnimationCancelled(newOccludedState)
+ }
+
private fun testWithScope(initialize: Boolean = true, test: suspend TestScope.() -> Unit) =
with(kosmos) {
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index a3959d2..42cd5ec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.app.StatusBarManager
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
@@ -25,9 +26,13 @@
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
import com.android.systemui.Flags
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
+import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
+import com.android.systemui.communal.domain.interactor.CommunalSceneTransitionInteractor
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneTransitionInteractor
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.flags.BrokenWithSceneContainer
@@ -122,15 +127,22 @@
private val fromGlanceableHubTransitionInteractor by lazy {
kosmos.fromGlanceableHubTransitionInteractor
}
+ private val communalSceneTransitionInteractor by lazy {
+ kosmos.communalSceneTransitionInteractor
+ }
private val powerInteractor by lazy { kosmos.powerInteractor }
private val communalInteractor by lazy { kosmos.communalInteractor }
+ private val communalSceneInteractor by lazy { kosmos.communalSceneInteractor }
companion object {
@JvmStatic
@Parameters(name = "{0}")
fun getParams(): List<FlagsParameterization> {
- return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ return FlagsParameterization.allCombinationsOf(
+ FLAG_COMMUNAL_SCENE_KTF_REFACTOR,
+ )
+ .andSceneContainer()
}
}
@@ -163,6 +175,7 @@
fromOccludedTransitionInteractor.start()
fromAlternateBouncerTransitionInteractor.start()
fromGlanceableHubTransitionInteractor.start()
+ communalSceneTransitionInteractor.start()
}
@Test
@@ -636,6 +649,7 @@
@Test
@DisableSceneContainer
+ @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun dozingToGlanceableHub() =
testScope.runTest {
// GIVEN a prior transition has run to DOZING
@@ -770,6 +784,7 @@
@Test
@BrokenWithSceneContainer(339465026)
+ @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun goneToGlanceableHub() =
testScope.runTest {
// GIVEN a prior transition has run to GONE
@@ -799,6 +814,29 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
+ @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+ fun goneToGlanceableHub_communalKtfRefactor() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to GONE
+ runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
+
+ // WHEN the glanceable hub is shown
+ communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ to = KeyguardState.GLANCEABLE_HUB,
+ from = KeyguardState.GONE,
+ ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+ animatorAssertion = { it.isNull() }
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
@DisableSceneContainer
fun alternateBouncerToPrimaryBouncer() =
testScope.runTest {
@@ -941,6 +979,11 @@
@Test
fun alternateBouncerToGlanceableHub() =
testScope.runTest {
+ // GIVEN the device is idle on the glanceable hub
+ communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ runCurrent()
+ clearInvocations(transitionRepository)
+
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
bouncerRepository.setAlternateVisible(true)
runTransitionAndSetWakefulness(
@@ -951,19 +994,11 @@
// GIVEN the primary bouncer isn't showing and device not sleeping
bouncerRepository.setPrimaryShow(false)
- // GIVEN the device is idle on the glanceable hub
- val idleTransitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(CommunalScenes.Communal)
- )
- communalInteractor.setTransitionState(idleTransitionState)
- runCurrent()
-
// WHEN the alternateBouncer stops showing
bouncerRepository.setAlternateVisible(false)
advanceTimeBy(200L)
- // THEN a transition to LOCKSCREEN should occur
+ // THEN a transition to GLANCEABLE_HUB should occur
assertThat(transitionRepository)
.startedTransition(
ownerName = FromAlternateBouncerTransitionInteractor::class.simpleName,
@@ -1063,17 +1098,16 @@
@DisableSceneContainer
fun primaryBouncerToGlanceableHub() =
testScope.runTest {
+ // GIVEN the device is idle on the glanceable hub
+ communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ runCurrent()
+
// GIVEN a prior transition has run to PRIMARY_BOUNCER
bouncerRepository.setPrimaryShow(true)
- runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER)
-
- // GIVEN the device is idle on the glanceable hub
- val idleTransitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(CommunalScenes.Communal)
- )
- communalInteractor.setTransitionState(idleTransitionState)
- runCurrent()
+ runTransitionAndSetWakefulness(
+ KeyguardState.GLANCEABLE_HUB,
+ KeyguardState.PRIMARY_BOUNCER
+ )
// WHEN the primaryBouncer stops showing
bouncerRepository.setPrimaryShow(false)
@@ -1095,27 +1129,26 @@
@DisableSceneContainer
fun primaryBouncerToGlanceableHubWhileDreaming() =
testScope.runTest {
+ // GIVEN the device is idle on the glanceable hub
+ communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ runCurrent()
+
// GIVEN a prior transition has run to PRIMARY_BOUNCER
bouncerRepository.setPrimaryShow(true)
- runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER)
+ runTransitionAndSetWakefulness(
+ KeyguardState.GLANCEABLE_HUB,
+ KeyguardState.PRIMARY_BOUNCER
+ )
// GIVEN that we are dreaming and occluded
keyguardRepository.setDreaming(true)
keyguardRepository.setKeyguardOccluded(true)
- // GIVEN the device is idle on the glanceable hub
- val idleTransitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(CommunalScenes.Communal)
- )
- communalInteractor.setTransitionState(idleTransitionState)
- runCurrent()
-
// WHEN the primaryBouncer stops showing
bouncerRepository.setPrimaryShow(false)
runCurrent()
- // THEN a transition to LOCKSCREEN should occur
+ // THEN a transition to GLANCEABLE_HUB should occur
assertThat(transitionRepository)
.startedTransition(
ownerName = FromPrimaryBouncerTransitionInteractor::class.simpleName,
@@ -1219,6 +1252,7 @@
@Test
@BrokenWithSceneContainer(339465026)
+ @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun occludedToGlanceableHub() =
testScope.runTest {
// GIVEN a device on lockscreen
@@ -1256,6 +1290,7 @@
@Test
@BrokenWithSceneContainer(339465026)
+ @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun occludedToGlanceableHubWhenInitiallyOnHub() =
testScope.runTest {
// GIVEN a device on lockscreen and communal is available
@@ -1293,6 +1328,37 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
+ @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+ fun occludedToGlanceableHub_communalKtfRefactor() =
+ testScope.runTest {
+ // GIVEN a device on lockscreen and communal is available
+ keyguardRepository.setKeyguardShowing(true)
+ kosmos.setCommunalAvailable(true)
+ runCurrent()
+
+ // GIVEN a prior transition has run to OCCLUDED from GLANCEABLE_HUB
+ runTransitionAndSetWakefulness(KeyguardState.GLANCEABLE_HUB, KeyguardState.OCCLUDED)
+ keyguardRepository.setKeyguardOccluded(true)
+ runCurrent()
+
+ // WHEN occlusion ends
+ keyguardRepository.setKeyguardOccluded(false)
+ runCurrent()
+
+ // THEN a transition to GLANCEABLE_HUB should occur
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.GLANCEABLE_HUB,
+ animatorAssertion = { it.isNull() },
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
fun occludedToAlternateBouncer() =
testScope.runTest {
// GIVEN a prior transition has run to OCCLUDED
@@ -1511,6 +1577,7 @@
@Test
@DisableSceneContainer
+ @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun dreamingToGlanceableHub() =
testScope.runTest {
// GIVEN a prior transition has run to DREAMING
@@ -1550,6 +1617,47 @@
}
@Test
+ @DisableSceneContainer
+ @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+ fun dreamingToGlanceableHub_communalKtfRefactor() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to DREAMING
+ keyguardRepository.setDreaming(true)
+ runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DREAMING)
+ runCurrent()
+
+ // WHEN a transition to the glanceable hub starts
+ val currentScene = CommunalScenes.Blank
+ val targetScene = CommunalScenes.Communal
+
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = currentScene,
+ toScene = targetScene,
+ currentScene = flowOf(targetScene),
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ communalSceneInteractor.setTransitionState(transitionState)
+ progress.value = .1f
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.GLANCEABLE_HUB,
+ animatorAssertion = { it.isNull() }, // transition should be manually animated
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
@BrokenWithSceneContainer(339465026)
fun lockscreenToOccluded() =
testScope.runTest {
@@ -1679,6 +1787,7 @@
@Test
@DisableSceneContainer
+ @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun lockscreenToGlanceableHub() =
testScope.runTest {
// GIVEN a prior transition has run to LOCKSCREEN
@@ -1737,6 +1846,48 @@
@Test
@DisableSceneContainer
+ @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+ fun lockscreenToGlanceableHub_communalKtfRefactor() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to LOCKSCREEN
+ runTransitionAndSetWakefulness(KeyguardState.AOD, KeyguardState.LOCKSCREEN)
+ runCurrent()
+
+ // WHEN a glanceable hub transition starts
+ val currentScene = CommunalScenes.Blank
+ val targetScene = CommunalScenes.Communal
+
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = currentScene,
+ toScene = targetScene,
+ currentScene = flowOf(targetScene),
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ communalSceneInteractor.setTransitionState(transitionState)
+ progress.value = .1f
+ runCurrent()
+
+ // THEN a transition from LOCKSCREEN => GLANCEABLE_HUB should occur
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GLANCEABLE_HUB,
+ animatorAssertion = { it.isNull() }, // transition should be manually animated
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
+ @DisableSceneContainer
+ @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun glanceableHubToLockscreen() =
testScope.runTest {
// GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1792,6 +1943,48 @@
@Test
@DisableSceneContainer
+ @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+ fun glanceableHubToLockscreen_communalKtfRefactor() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to GLANCEABLE_HUB
+ communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ runCurrent()
+ clearInvocations(transitionRepository)
+
+ // WHEN a transition away from glanceable hub starts
+ val currentScene = CommunalScenes.Communal
+ val targetScene = CommunalScenes.Blank
+
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = currentScene,
+ toScene = targetScene,
+ currentScene = flowOf(targetScene),
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ communalSceneInteractor.setTransitionState(transitionState)
+ progress.value = .1f
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.LOCKSCREEN,
+ animatorAssertion = { it.isNull() }, // transition should be manually animated
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
+ @DisableSceneContainer
+ @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun glanceableHubToDozing() =
testScope.runTest {
// GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1814,6 +2007,31 @@
@Test
@DisableSceneContainer
+ @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+ fun glanceableHubToDozing_communalKtfRefactor() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to GLANCEABLE_HUB
+ communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ runCurrent()
+ clearInvocations(transitionRepository)
+
+ // WHEN the device begins to sleep
+ powerInteractor.setAsleepForTest()
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.DOZING,
+ animatorAssertion = { it.isNull() },
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
+ @DisableSceneContainer
fun glanceableHubToPrimaryBouncer() =
testScope.runTest {
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
@@ -1858,6 +2076,7 @@
@Test
@BrokenWithSceneContainer(339465026)
+ @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun glanceableHubToOccluded() =
testScope.runTest {
// GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1888,7 +2107,33 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
+ @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+ fun glanceableHubToOccluded_communalKtfRefactor() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to GLANCEABLE_HUB
+ communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ runCurrent()
+ clearInvocations(transitionRepository)
+
+ // WHEN the keyguard is occluded
+ keyguardRepository.setKeyguardOccluded(true)
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.OCCLUDED,
+ animatorAssertion = { it.isNull() },
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
@DisableSceneContainer
+ @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun glanceableHubToGone() =
testScope.runTest {
// GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1911,6 +2156,32 @@
@Test
@DisableSceneContainer
+ @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+ fun glanceableHubToGone_communalKtfRefactor() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to GLANCEABLE_HUB
+ communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ runCurrent()
+ clearInvocations(transitionRepository)
+
+ // WHEN keyguard goes away
+ keyguardRepository.setKeyguardGoingAway(true)
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.GONE,
+ animatorAssertion = { it.isNull() },
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
+ @DisableSceneContainer
+ @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun glanceableHubToDreaming() =
testScope.runTest {
// GIVEN that we are dreaming and not dozing
@@ -1939,7 +2210,7 @@
isUserInputOngoing = flowOf(false),
)
)
- communalInteractor.setTransitionState(transitionState)
+ communalSceneInteractor.setTransitionState(transitionState)
runCurrent()
assertThat(transitionRepository)
@@ -1953,6 +2224,52 @@
coroutineContext.cancelChildren()
}
+ @Test
+ @DisableSceneContainer
+ @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+ fun glanceableHubToDreaming_communalKtfRefactor() =
+ testScope.runTest {
+ // GIVEN that we are dreaming and not dozing
+ keyguardRepository.setDreaming(true)
+ keyguardRepository.setDozeTransitionModel(
+ DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+ )
+ runCurrent()
+
+ // GIVEN a prior transition has run to GLANCEABLE_HUB
+ communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ runCurrent()
+ clearInvocations(transitionRepository)
+
+ // WHEN a transition away from glanceable hub starts
+ val currentScene = CommunalScenes.Communal
+ val targetScene = CommunalScenes.Blank
+
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = currentScene,
+ toScene = targetScene,
+ currentScene = flowOf(targetScene),
+ progress = flowOf(0f, 0.1f),
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ communalSceneInteractor.setTransitionState(transitionState)
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.DREAMING,
+ animatorAssertion = { it.isNull() }, // transition should be manually animated
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
private suspend fun TestScope.runTransitionAndSetWakefulness(
from: KeyguardState,
to: KeyguardState
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
index aa9cbd0..45cfe36 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
@@ -52,7 +52,7 @@
@Application private val applicationScope: CoroutineScope,
private val communalSceneRepository: CommunalSceneRepository,
) {
- val _isLaunchingWidget = MutableStateFlow(false)
+ private val _isLaunchingWidget = MutableStateFlow(false)
/** Whether a widget launch is currently in progress. */
val isLaunchingWidget: StateFlow<Boolean> = _isLaunchingWidget.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
index c44eb47..491c73d 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
@@ -16,9 +16,15 @@
package com.android.systemui.haptics.qs
+import android.content.ComponentName
import android.os.VibrationEffect
import android.service.quicksettings.Tile
+import android.view.View
import androidx.annotation.VisibleForTesting
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.DelegateTransitionAnimatorController
+import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
@@ -58,6 +64,7 @@
/** The [QSTile] and [Expandable] used to perform a long-click and click actions */
var qsTile: QSTile? = null
var expandable: Expandable? = null
+ private set
/** Haptic effects */
private val durations =
@@ -125,8 +132,10 @@
}
fun handleAnimationStart() {
- vibrate(longPressHint)
- setState(State.RUNNING_FORWARD)
+ if (state == State.TIMEOUT_WAIT) {
+ vibrate(longPressHint)
+ setState(State.RUNNING_FORWARD)
+ }
}
/** This function is called both when an animator completes or gets cancelled */
@@ -147,7 +156,10 @@
setState(getStateForClick())
qsTile?.click(expandable)
}
- State.RUNNING_BACKWARDS_FROM_CANCEL -> setState(State.IDLE)
+ State.RUNNING_BACKWARDS_FROM_CANCEL -> {
+ callback?.onEffectFinishedReversing()
+ setState(State.IDLE)
+ }
else -> {}
}
}
@@ -222,13 +234,58 @@
fun resetState() = setState(State.IDLE)
+ fun createExpandableFromView(view: View) {
+ expandable =
+ object : Expandable {
+ override fun activityTransitionController(
+ launchCujType: Int?,
+ cookie: ActivityTransitionAnimator.TransitionCookie?,
+ component: ComponentName?,
+ returnCujType: Int?,
+ ): ActivityTransitionAnimator.Controller? {
+ val delegatedController =
+ ActivityTransitionAnimator.Controller.fromView(
+ view,
+ launchCujType,
+ cookie,
+ component,
+ returnCujType,
+ )
+ return delegatedController?.let { createTransitionControllerDelegate(it) }
+ }
+
+ override fun dialogTransitionController(
+ cuj: DialogCuj?,
+ ): DialogTransitionAnimator.Controller? =
+ DialogTransitionAnimator.Controller.fromView(view, cuj)
+ }
+ }
+
+ @VisibleForTesting
+ fun createTransitionControllerDelegate(
+ controller: ActivityTransitionAnimator.Controller
+ ): DelegateTransitionAnimatorController {
+ val delegated =
+ object : DelegateTransitionAnimatorController(controller) {
+ override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) {
+ if (state == State.LONG_CLICKED) {
+ setState(State.RUNNING_BACKWARDS_FROM_CANCEL)
+ callback?.onReverseAnimator(false)
+ }
+ delegate.onTransitionAnimationCancelled(newKeyguardOccludedState)
+ }
+ }
+ return delegated
+ }
+
enum class State {
IDLE, /* The effect is idle waiting for touch input */
TIMEOUT_WAIT, /* The effect is waiting for a tap timeout period */
RUNNING_FORWARD, /* The effect is running normally */
/* The effect was interrupted by an ACTION_UP and is now running backwards */
RUNNING_BACKWARDS_FROM_UP,
- /* The effect was interrupted by an ACTION_CANCEL and is now running backwards */
+ /* The effect was cancelled by an ACTION_CANCEL or a shade collapse and is now running
+ backwards */
RUNNING_BACKWARDS_FROM_CANCEL,
CLICKED, /* The effect has ended with a click */
LONG_CLICKED, /* The effect has ended with a long-click */
@@ -247,7 +304,7 @@
fun onStartAnimator()
/** Reverse the effect animator */
- fun onReverseAnimator()
+ fun onReverseAnimator(playHaptics: Boolean = true)
/** Cancel the effect animator */
fun onCancelAnimator()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 59ec87a..e5ccc4a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -119,7 +119,9 @@
// needed. Also, don't react to wake and unlock events, as we'll be
// receiving a call to #dismissAod() shortly when the authentication
// completes.
- !maybeStartTransitionToOccludedOrInsecureCamera() &&
+ !maybeStartTransitionToOccludedOrInsecureCamera { state, reason ->
+ startTransitionTo(state, ownerReason = reason)
+ } &&
!isWakeAndUnlock(biometricUnlockState.mode) &&
!primaryBouncerShowing
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 8f50b03..8ef138e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -20,8 +20,11 @@
import android.annotation.SuppressLint
import android.app.DreamManager
import com.android.app.animation.Interpolators
+import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -166,7 +169,7 @@
}
} else if (occluded) {
startTransitionTo(KeyguardState.OCCLUDED)
- } else if (isIdleOnCommunal) {
+ } else if (isIdleOnCommunal && !communalSceneKtfRefactor()) {
if (SceneContainerFlag.isEnabled) {
// TODO(b/336576536): Check if adaptation for scene framework is needed
} else {
@@ -183,7 +186,7 @@
if (SceneContainerFlag.isEnabled) {
// TODO(b/336576536): Check if adaptation for scene framework is needed
} else {
- startTransitionTo(KeyguardState.GLANCEABLE_HUB)
+ transitionToGlanceableHub()
}
} else {
startTransitionTo(KeyguardState.LOCKSCREEN)
@@ -218,7 +221,9 @@
canWakeDirectlyToGone,
primaryBouncerShowing) ->
if (
- !maybeStartTransitionToOccludedOrInsecureCamera() &&
+ !maybeStartTransitionToOccludedOrInsecureCamera { state, reason ->
+ startTransitionTo(state, ownerReason = reason)
+ } &&
// Handled by dismissFromDozing().
!isWakeAndUnlock(biometricUnlockState.mode)
) {
@@ -242,7 +247,7 @@
ownerReason = "waking from dozing"
)
}
- } else if (isIdleOnCommunal) {
+ } else if (isIdleOnCommunal && !communalSceneKtfRefactor()) {
if (SceneContainerFlag.isEnabled) {
// TODO(b/336576536): Check if adaptation for scene framework is
// needed
@@ -264,10 +269,7 @@
// TODO(b/336576536): Check if adaptation for scene framework is
// needed
} else {
- startTransitionTo(
- KeyguardState.GLANCEABLE_HUB,
- ownerReason = "waking from dozing"
- )
+ transitionToGlanceableHub()
}
} else {
startTransitionTo(
@@ -280,6 +282,18 @@
}
}
+ private suspend fun transitionToGlanceableHub() {
+ if (communalSceneKtfRefactor()) {
+ communalSceneInteractor.changeScene(
+ CommunalScenes.Communal,
+ // Immediately show the hub when transitioning from dozing to hub.
+ CommunalTransitionKeys.Immediately,
+ )
+ } else {
+ startTransitionTo(KeyguardState.GLANCEABLE_HUB)
+ }
+ }
+
/** Dismisses keyguard from the DOZING state. */
fun dismissFromDozing() {
scope.launch { startTransitionTo(KeyguardState.GONE) }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 453401d..4c3a75e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -19,6 +19,7 @@
import android.animation.ValueAnimator
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launch
+import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -81,7 +82,9 @@
listenForDreamingToLockscreenOrGone()
listenForDreamingToAodOrDozing()
listenForTransitionToCamera(scope, keyguardInteractor)
- listenForDreamingToGlanceableHub()
+ if (!communalSceneKtfRefactor()) {
+ listenForDreamingToGlanceableHub()
+ }
listenForDreamingToPrimaryBouncer()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index 1a7012a..d811950 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -19,7 +19,11 @@
import android.animation.ValueAnimator
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launch
+import com.android.systemui.Flags.communalSceneKtfRefactor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -30,6 +34,7 @@
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
+import com.android.systemui.util.kotlin.BooleanFlowOperators.noneOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -50,6 +55,7 @@
private val glanceableHubTransitions: GlanceableHubTransitions,
private val communalSettingsInteractor: CommunalSettingsInteractor,
keyguardInteractor: KeyguardInteractor,
+ private val communalSceneInteractor: CommunalSceneInteractor,
override val transitionRepository: KeyguardTransitionRepository,
override val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
transitionInteractor: KeyguardTransitionInteractor,
@@ -72,7 +78,9 @@
if (!communalSettingsInteractor.isCommunalFlagEnabled()) {
return
}
- listenForHubToLockscreenOrDreaming()
+ if (!communalSceneKtfRefactor()) {
+ listenForHubToLockscreenOrDreaming()
+ }
listenForHubToDozing()
listenForHubToPrimaryBouncer()
listenForHubToAlternateBouncer()
@@ -120,7 +128,10 @@
scope.launch("$TAG#listenForHubToPrimaryBouncer") {
keyguardInteractor.primaryBouncerShowing
.filterRelevantKeyguardStateAnd { primaryBouncerShowing -> primaryBouncerShowing }
- .collect { startTransitionTo(KeyguardState.PRIMARY_BOUNCER) }
+ .collect {
+ // Bouncer shows on top of the hub, so do not change scenes here.
+ startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
+ }
}
}
@@ -130,7 +141,10 @@
.filterRelevantKeyguardStateAnd { alternateBouncerShowing ->
alternateBouncerShowing
}
- .collect { pair -> startTransitionTo(KeyguardState.ALTERNATE_BOUNCER) }
+ .collect { pair ->
+ // Bouncer shows on top of the hub, so do not change scenes here.
+ startTransitionTo(KeyguardState.ALTERNATE_BOUNCER)
+ }
}
}
@@ -139,10 +153,18 @@
powerInteractor.isAsleep
.filterRelevantKeyguardStateAnd { isAsleep -> isAsleep }
.collect {
- startTransitionTo(
- toState = KeyguardState.DOZING,
- modeOnCanceled = TransitionModeOnCanceled.LAST_VALUE,
- )
+ if (communalSceneKtfRefactor()) {
+ communalSceneInteractor.changeScene(
+ newScene = CommunalScenes.Blank,
+ transitionKey = CommunalTransitionKeys.Immediately,
+ keyguardState = KeyguardState.DOZING,
+ )
+ } else {
+ startTransitionTo(
+ toState = KeyguardState.DOZING,
+ modeOnCanceled = TransitionModeOnCanceled.LAST_VALUE,
+ )
+ }
}
}
}
@@ -152,7 +174,44 @@
scope.launch {
keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop
.filterRelevantKeyguardStateAnd { onTop -> onTop }
- .collect { maybeStartTransitionToOccludedOrInsecureCamera() }
+ .collect {
+ maybeStartTransitionToOccludedOrInsecureCamera { state, reason ->
+ if (communalSceneKtfRefactor()) {
+ communalSceneInteractor.changeScene(
+ newScene = CommunalScenes.Blank,
+ transitionKey = CommunalTransitionKeys.SimpleFade,
+ keyguardState = state,
+ )
+ null
+ } else {
+ startTransitionTo(state, ownerReason = reason)
+ }
+ }
+ }
+ }
+ } else if (communalSceneKtfRefactor()) {
+ scope.launch {
+ allOf(
+ keyguardInteractor.isKeyguardOccluded,
+ noneOf(
+ // Dream is a special-case of occluded, so filter out the dreaming
+ // case here.
+ keyguardInteractor.isDreaming,
+ // When launching activities from widgets on the hub, we have a
+ // custom occlusion animation.
+ communalSceneInteractor.isLaunchingWidget,
+ ),
+ )
+ .filterRelevantKeyguardStateAnd { isOccludedAndNotDreamingNorLaunchingWidget ->
+ isOccludedAndNotDreamingNorLaunchingWidget
+ }
+ .collect { _ ->
+ communalSceneInteractor.changeScene(
+ newScene = CommunalScenes.Blank,
+ transitionKey = CommunalTransitionKeys.SimpleFade,
+ keyguardState = KeyguardState.OCCLUDED,
+ )
+ }
}
} else {
scope.launch {
@@ -160,9 +219,7 @@
.filterRelevantKeyguardStateAnd { isOccludedAndNotDreaming ->
isOccludedAndNotDreaming
}
- .collect { isOccludedAndNotDreaming ->
- startTransitionTo(KeyguardState.OCCLUDED)
- }
+ .collect { _ -> startTransitionTo(KeyguardState.OCCLUDED) }
}
}
}
@@ -170,10 +227,33 @@
private fun listenForHubToGone() {
// TODO(b/336576536): Check if adaptation for scene framework is needed
if (SceneContainerFlag.isEnabled) return
- scope.launch {
- keyguardInteractor.isKeyguardGoingAway
- .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway }
- .collect { startTransitionTo(KeyguardState.GONE) }
+ if (communalSceneKtfRefactor()) {
+ scope.launch {
+ allOf(
+ keyguardInteractor.isKeyguardGoingAway,
+ // TODO(b/327225415): Handle edit mode opening here to avoid going to GONE
+ // state until after edit mode is ready to be shown.
+ noneOf(
+ // When launching activities from widgets on the hub, we wait to change
+ // scenes until the activity launch is complete.
+ communalSceneInteractor.isLaunchingWidget,
+ ),
+ )
+ .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway }
+ .collect {
+ communalSceneInteractor.changeScene(
+ newScene = CommunalScenes.Blank,
+ transitionKey = CommunalTransitionKeys.SimpleFade,
+ keyguardState = KeyguardState.GONE
+ )
+ }
+ }
+ } else {
+ scope.launch {
+ keyguardInteractor.isKeyguardGoingAway
+ .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway }
+ .collect { startTransitionTo(KeyguardState.GONE) }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 5c7adf0..16c014f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -20,6 +20,7 @@
import android.util.MathUtils
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launch
+import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -90,7 +91,9 @@
listenForLockscreenToPrimaryBouncerDragging()
listenForLockscreenToAlternateBouncer()
listenForLockscreenTransitionToCamera()
- listenForLockscreenToGlanceableHub()
+ if (!communalSceneKtfRefactor()) {
+ listenForLockscreenToGlanceableHub()
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index f3ca9df..2f32040 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -18,8 +18,12 @@
import android.animation.ValueAnimator
import com.android.app.animation.Interpolators
+import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.Flags.restartDreamOnUnocclude
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -49,6 +53,7 @@
keyguardInteractor: KeyguardInteractor,
powerInteractor: PowerInteractor,
private val communalInteractor: CommunalInteractor,
+ private val communalSceneInteractor: CommunalSceneInteractor,
keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
) :
TransitionInteractor(
@@ -140,7 +145,14 @@
} else if (isIdleOnCommunal || showCommunalFromOccluded) {
// TODO(b/336576536): Check if adaptation for scene framework is needed
if (SceneContainerFlag.isEnabled) return
- startTransitionTo(KeyguardState.GLANCEABLE_HUB)
+ if (communalSceneKtfRefactor()) {
+ communalSceneInteractor.changeScene(
+ CommunalScenes.Communal,
+ CommunalTransitionKeys.SimpleFade
+ )
+ } else {
+ startTransitionTo(KeyguardState.GLANCEABLE_HUB)
+ }
} else {
startTransitionTo(KeyguardState.LOCKSCREEN)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 2429088..6c89ce0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -113,10 +113,9 @@
(isBouncerShowing, isAwake, isActiveDreamLockscreenHosted, isIdleOnCommunal)
->
if (
- !maybeStartTransitionToOccludedOrInsecureCamera() &&
- !isBouncerShowing &&
- isAwake &&
- !isActiveDreamLockscreenHosted
+ !maybeStartTransitionToOccludedOrInsecureCamera { state, reason ->
+ startTransitionTo(state, ownerReason = reason)
+ } && !isBouncerShowing && isAwake && !isActiveDreamLockscreenHosted
) {
val toState =
if (isIdleOnCommunal) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index 89c7178..d06ee64 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -122,9 +122,14 @@
* SHOW_WHEN_LOCKED activity, or back to [KeyguardState.GONE], for some power button launch
* gesture cases. If so, start the transition.
*
+ * @param startTransition A callback which is triggered to start the transition to the desired
+ * KeyguardState. Allows caller to hook into the transition start if needed.
+ *
* Returns true if a transition was started, false otherwise.
*/
- suspend fun maybeStartTransitionToOccludedOrInsecureCamera(): Boolean {
+ suspend fun maybeStartTransitionToOccludedOrInsecureCamera(
+ startTransition: suspend (state: KeyguardState, reason: String) -> UUID?
+ ): Boolean {
// The refactor is required for the occlusion interactor to work.
KeyguardWmStateRefactor.isUnexpectedlyInLegacyMode()
@@ -136,10 +141,7 @@
if (!maybeHandleInsecurePowerGesture()) {
// Otherwise, the double tap gesture occurred while not GONE and not dismissable,
// which means we will launch the secure camera, which OCCLUDES the keyguard.
- startTransitionTo(
- KeyguardState.OCCLUDED,
- ownerReason = "Power button gesture on lockscreen"
- )
+ startTransition(KeyguardState.OCCLUDED, "Power button gesture on lockscreen")
}
return true
@@ -147,10 +149,7 @@
// A SHOW_WHEN_LOCKED activity is on top of the task stack. Transition to OCCLUDED so
// it's visible.
// TODO(b/307976454) - Centralize transition to DREAMING here.
- startTransitionTo(
- KeyguardState.OCCLUDED,
- ownerReason = "SHOW_WHEN_LOCKED activity on top"
- )
+ startTransition(KeyguardState.OCCLUDED, "SHOW_WHEN_LOCKED activity on top")
return true
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 6b3dfe1..dbfe818 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -107,7 +107,9 @@
set(value) {
if (field == value) return
field = value
- updateHeight()
+ if (longPressEffect?.state != QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_CANCEL) {
+ updateHeight()
+ }
}
override var squishinessFraction: Float = 1f
@@ -381,14 +383,6 @@
}
private fun updateHeight() {
- // TODO(b/332900989): Find a more robust way of resetting the tile if not reset by the
- // launch animation.
- if (!haveLongPressPropertiesBeenReset && longPressEffect != null) {
- // The launch animation of a long-press effect did not reset the long-press effect so
- // we must do it here
- resetLongPressEffectProperties()
- longPressEffect.resetState()
- }
val actualHeight =
if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
heightOverride
@@ -417,17 +411,17 @@
}
override fun init(tile: QSTile) {
- val expandable = Expandable.fromView(this)
if (longPressEffect != null) {
isHapticFeedbackEnabled = false
longPressEffect.qsTile = tile
- longPressEffect.expandable = expandable
+ longPressEffect.createExpandableFromView(this)
initLongPressEffectCallback()
init(
{ _: View -> longPressEffect.onTileClick() },
null, // Haptics and long-clicks will be handled by the [QSLongPressEffect]
)
} else {
+ val expandable = Expandable.fromView(this)
init(
{ _: View? -> tile.click(expandable) },
{ _: View? ->
@@ -475,10 +469,10 @@
}
}
- override fun onReverseAnimator() {
+ override fun onReverseAnimator(playHaptics: Boolean) {
longPressEffectAnimator?.let {
val pausedProgress = it.animatedFraction
- longPressEffect?.playReverseHaptics(pausedProgress)
+ if (playHaptics) longPressEffect?.playReverseHaptics(pausedProgress)
it.reverse()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 4914409..54ae225 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -49,6 +49,7 @@
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
/**
* An AsyncTask that saves an image to the media store in the background.
@@ -59,12 +60,73 @@
private static final String SCREENSHOT_ID_TEMPLATE = "Screenshot_%s";
private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
+ /**
+ * POD used in the AsyncTask which saves an image in the background.
+ */
+ static class SaveImageInBackgroundData {
+ public Bitmap image;
+ public Consumer<Uri> finisher;
+ public ActionsReadyListener mActionsReadyListener;
+ public QuickShareActionReadyListener mQuickShareActionsReadyListener;
+ public UserHandle owner;
+ public int displayId;
+
+ void clearImage() {
+ image = null;
+ }
+ }
+
+ /**
+ * Structure returned by the SaveImageInBackgroundTask
+ */
+ public static class SavedImageData {
+ public Uri uri;
+ public List<Notification.Action> smartActions;
+ public Notification.Action quickShareAction;
+ public UserHandle owner;
+ public String subject; // Title for sharing
+ public Long imageTime; // Time at which screenshot was saved
+
+ /**
+ * Used to reset the return data on error
+ */
+ public void reset() {
+ uri = null;
+ smartActions = null;
+ quickShareAction = null;
+ subject = null;
+ imageTime = null;
+ }
+ }
+
+ /**
+ * Structure returned by the QueryQuickShareInBackgroundTask
+ */
+ static class QuickShareData {
+ public Notification.Action quickShareAction;
+
+ /**
+ * Used to reset the return data on error
+ */
+ public void reset() {
+ quickShareAction = null;
+ }
+ }
+
+ interface ActionsReadyListener {
+ void onActionsReady(SavedImageData imageData);
+ }
+
+ interface QuickShareActionReadyListener {
+ void onActionsReady(QuickShareData quickShareData);
+ }
+
private final Context mContext;
private FeatureFlags mFlags;
private final ScreenshotSmartActions mScreenshotSmartActions;
- private final ScreenshotController.SaveImageInBackgroundData mParams;
- private final ScreenshotController.SavedImageData mImageData;
- private final ScreenshotController.QuickShareData mQuickShareData;
+ private final SaveImageInBackgroundData mParams;
+ private final SavedImageData mImageData;
+ private final QuickShareData mQuickShareData;
private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
private String mScreenshotId;
@@ -77,15 +139,15 @@
FeatureFlags flags,
ImageExporter exporter,
ScreenshotSmartActions screenshotSmartActions,
- ScreenshotController.SaveImageInBackgroundData data,
+ SaveImageInBackgroundData data,
ScreenshotNotificationSmartActionsProvider
screenshotNotificationSmartActionsProvider
) {
mContext = context;
mFlags = flags;
mScreenshotSmartActions = screenshotSmartActions;
- mImageData = new ScreenshotController.SavedImageData();
- mQuickShareData = new ScreenshotController.QuickShareData();
+ mImageData = new SavedImageData();
+ mQuickShareData = new QuickShareData();
mImageExporter = exporter;
// Prepare all the output metadata
@@ -195,7 +257,7 @@
* Update the listener run when the saving task completes. Used to avoid showing UI for the
* first screenshot when a second one is taken.
*/
- void setActionsReadyListener(ScreenshotController.ActionsReadyListener listener) {
+ void setActionsReadyListener(ActionsReadyListener listener) {
mParams.mActionsReadyListener = listener;
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 7739009..0a4635e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -35,10 +35,7 @@
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityOptions;
-import android.app.ExitTransitionCoordinator;
import android.app.ICompatCameraControlCallback;
-import android.app.Notification;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -55,7 +52,6 @@
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.Pair;
import android.view.Display;
import android.view.ScrollCaptureResponse;
import android.view.View;
@@ -67,7 +63,6 @@
import android.widget.Toast;
import android.window.WindowContext;
-import com.android.internal.app.ChooserActivity;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.policy.PhoneWindow;
import com.android.settingslib.applications.InterestingConfigChanges;
@@ -89,7 +84,6 @@
import kotlin.Unit;
-import java.util.List;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
@@ -104,67 +98,6 @@
public class ScreenshotController implements ScreenshotHandler {
private static final String TAG = logTag(ScreenshotController.class);
- /**
- * POD used in the AsyncTask which saves an image in the background.
- */
- static class SaveImageInBackgroundData {
- public Bitmap image;
- public Consumer<Uri> finisher;
- public ScreenshotController.ActionsReadyListener mActionsReadyListener;
- public ScreenshotController.QuickShareActionReadyListener mQuickShareActionsReadyListener;
- public UserHandle owner;
- public int displayId;
-
- void clearImage() {
- image = null;
- }
- }
-
- /**
- * Structure returned by the SaveImageInBackgroundTask
- */
- public static class SavedImageData {
- public Uri uri;
- public List<Notification.Action> smartActions;
- public Notification.Action quickShareAction;
- public UserHandle owner;
- public String subject; // Title for sharing
- public Long imageTime; // Time at which screenshot was saved
-
- /**
- * Used to reset the return data on error
- */
- public void reset() {
- uri = null;
- smartActions = null;
- quickShareAction = null;
- subject = null;
- imageTime = null;
- }
- }
-
- /**
- * Structure returned by the QueryQuickShareInBackgroundTask
- */
- static class QuickShareData {
- public Notification.Action quickShareAction;
-
- /**
- * Used to reset the return data on error
- */
- public void reset() {
- quickShareAction = null;
- }
- }
-
- interface ActionsReadyListener {
- void onActionsReady(ScreenshotController.SavedImageData imageData);
- }
-
- interface QuickShareActionReadyListener {
- void onActionsReady(ScreenshotController.QuickShareData quickShareData);
- }
-
public interface TransitionDestination {
/**
* Allows the long screenshot activity to call back with a destination location (the bounds
@@ -213,7 +146,6 @@
private final ScreenshotNotificationSmartActionsProvider
mScreenshotNotificationSmartActionsProvider;
private final TimeoutHandler mScreenshotHandler;
- private final ActionIntentExecutor mActionIntentExecutor;
private final UserManager mUserManager;
private final AssistContentRequester mAssistContentRequester;
private final ActionExecutor mActionExecutor;
@@ -259,7 +191,6 @@
BroadcastDispatcher broadcastDispatcher,
ScreenshotNotificationSmartActionsProvider screenshotNotificationSmartActionsProvider,
ScreenshotActionsController.Factory screenshotActionsControllerFactory,
- ActionIntentExecutor actionIntentExecutor,
ActionExecutor.Factory actionExecutorFactory,
UserManager userManager,
AssistContentRequester assistContentRequester,
@@ -289,7 +220,6 @@
final Context displayContext = context.createDisplayContext(display);
mContext = (WindowContext) displayContext.createWindowContext(TYPE_SCREENSHOT, null);
mFlags = flags;
- mActionIntentExecutor = actionIntentExecutor;
mUserManager = userManager;
mMessageContainerController = messageContainerController;
mAssistContentRequester = assistContentRequester;
@@ -765,33 +695,6 @@
mScreenshotAnimation.start();
}
- /**
- * Supplies the necessary bits for the shared element transition to share sheet.
- * Note that once called, the action intent to share must be sent immediately after.
- */
- private Pair<ActivityOptions, ExitTransitionCoordinator> createWindowTransition() {
- ExitTransitionCoordinator.ExitTransitionCallbacks callbacks =
- new ExitTransitionCoordinator.ExitTransitionCallbacks() {
- @Override
- public boolean isReturnTransitionAllowed() {
- return false;
- }
-
- @Override
- public void hideSharedElements() {
- finishDismiss();
- }
-
- @Override
- public void onFinish() {
- }
- };
-
- return ActivityOptions.startSharedElementAnimation(mWindow, callbacks, null,
- Pair.create(mViewProxy.getScreenshotPreview(),
- ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME));
- }
-
/** Reset screenshot view and then call onCompleteRunnable */
private void finishDismiss() {
Log.d(TAG, "finishDismiss");
@@ -838,11 +741,11 @@
private void saveScreenshotInWorkerThread(
UserHandle owner,
@NonNull Consumer<Uri> finisher,
- @Nullable ActionsReadyListener actionsReadyListener,
- @Nullable QuickShareActionReadyListener
+ @Nullable SaveImageInBackgroundTask.ActionsReadyListener actionsReadyListener,
+ @Nullable SaveImageInBackgroundTask.QuickShareActionReadyListener
quickShareActionsReadyListener) {
- ScreenshotController.SaveImageInBackgroundData
- data = new ScreenshotController.SaveImageInBackgroundData();
+ SaveImageInBackgroundTask.SaveImageInBackgroundData
+ data = new SaveImageInBackgroundTask.SaveImageInBackgroundData();
data.image = mScreenBitmap;
data.finisher = finisher;
data.mActionsReadyListener = actionsReadyListener;
@@ -881,7 +784,7 @@
/**
* Logs success/failure of the screenshot saving task, and shows an error if it failed.
*/
- private void logSuccessOnActionsReady(ScreenshotController.SavedImageData imageData) {
+ private void logSuccessOnActionsReady(SaveImageInBackgroundTask.SavedImageData imageData) {
logScreenshotResultStatus(imageData.uri, imageData.owner);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 789a6f4..e08dbb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -308,6 +308,9 @@
HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.getKey());
if (headsUpEntry != null && headsUpEntry.mRemoteInputActive != remoteInputActive) {
headsUpEntry.mRemoteInputActive = remoteInputActive;
+ if (ExpandHeadsUpOnInlineReply.isEnabled() && remoteInputActive) {
+ headsUpEntry.mRemoteInputActivatedAtLeastOnce = true;
+ }
if (remoteInputActive) {
headsUpEntry.cancelAutoRemovalCallbacks("setRemoteInputActive(true)");
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index a0eb989..6517135 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -41,6 +41,7 @@
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
+import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
import com.android.systemui.util.ListenerSet;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.settings.GlobalSettings;
@@ -726,6 +727,7 @@
* of AvalancheController that take it as param.
*/
public class HeadsUpEntry implements Comparable<HeadsUpEntry> {
+ public boolean mRemoteInputActivatedAtLeastOnce;
public boolean mRemoteInputActive;
public boolean mUserActionMayIndirectlyRemove;
@@ -835,6 +837,15 @@
*/
public boolean isSticky() {
if (mEntry == null) return false;
+
+ if (ExpandHeadsUpOnInlineReply.isEnabled()) {
+ // we don't consider pinned and expanded huns as sticky after the remote input
+ // has been activated for them
+ if (!mRemoteInputActive && mRemoteInputActivatedAtLeastOnce) {
+ return false;
+ }
+ }
+
return (mEntry.isRowPinned() && mExpanded)
|| mRemoteInputActive
|| hasFullScreenIntent(mEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt
index 25dd9fe..24e8b18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt
@@ -27,7 +27,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.screenshot.ScreenshotController.SaveImageInBackgroundData
import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
@@ -44,7 +43,7 @@
private val imageExporter = mock<ImageExporter>()
private val smartActions = mock<ScreenshotSmartActions>()
private val smartActionsProvider = mock<ScreenshotNotificationSmartActionsProvider>()
- private val saveImageData = SaveImageInBackgroundData()
+ private val saveImageData = SaveImageInBackgroundTask.SaveImageInBackgroundData()
private val testScreenshotId: String = "testScreenshotId"
private val testBitmap = mock<Bitmap>()
private val testUser = UserHandle.getUserHandleForUid(0)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt
index 079852a..494f08b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
@@ -38,5 +39,6 @@
transitionInteractor = keyguardTransitionInteractor,
powerInteractor = powerInteractor,
keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+ communalSceneInteractor = communalSceneInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt
index c216945..7827655 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -37,5 +38,6 @@
powerInteractor = powerInteractor,
communalInteractor = communalInteractor,
keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+ communalSceneInteractor = communalSceneInteractor,
)
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 3d53deb..4fc9d55 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -103,9 +103,10 @@
String packageName = getNextArgRequired();
String address = getNextArgRequired();
String deviceProfile = getNextArg();
+ boolean selfManaged = getNextBooleanArg();
final MacAddress macAddress = MacAddress.fromString(address);
mAssociationRequestsProcessor.createAssociation(userId, packageName, macAddress,
- deviceProfile, deviceProfile, /* associatedDevice */ null, false,
+ deviceProfile, deviceProfile, /* associatedDevice */ null, selfManaged,
/* callback */ null, /* resultReceiver */ null);
}
break;
@@ -462,6 +463,17 @@
}
}
+ private boolean getNextBooleanArg() {
+ String arg = getNextArg();
+ if (arg == null || "false".equalsIgnoreCase(arg)) {
+ return false;
+ } else if ("true".equalsIgnoreCase(arg)) {
+ return Boolean.parseBoolean(arg);
+ } else {
+ throw new IllegalArgumentException("Expected a boolean argument but was: " + arg);
+ }
+ }
+
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -470,7 +482,7 @@
pw.println(" Print this help text.");
pw.println(" list USER_ID");
pw.println(" List all Associations for a user.");
- pw.println(" associate USER_ID PACKAGE MAC_ADDRESS [DEVICE_PROFILE]");
+ pw.println(" associate USER_ID PACKAGE MAC_ADDRESS [DEVICE_PROFILE] [SELF_MANAGED]");
pw.println(" Create a new Association.");
pw.println(" disassociate USER_ID PACKAGE MAC_ADDRESS");
pw.println(" Remove an existing Association.");
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 8e8a037..8ec835b 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -21,6 +21,7 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricManager.Authenticators;
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_NO_AUTHENTICATION;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_IDLE;
@@ -41,6 +42,7 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.BiometricStateListener;
import android.hardware.biometrics.Flags;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
@@ -54,8 +56,12 @@
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.camera2.CameraManager;
+import android.hardware.face.FaceManager;
+import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.hardware.security.keymint.HardwareAuthenticatorType;
import android.net.Uri;
import android.os.Binder;
@@ -234,6 +240,8 @@
private static final boolean DEFAULT_APP_ENABLED = true;
private static final boolean DEFAULT_ALWAYS_REQUIRE_CONFIRMATION = false;
private static final boolean DEFAULT_MANDATORY_BIOMETRICS_STATUS = false;
+ private static final boolean DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS =
+ true;
// Some devices that shipped before S already have face-specific settings. Instead of
// migrating, which is complicated, let's just keep using the existing settings.
@@ -256,6 +264,8 @@
Settings.Secure.getUriFor(Settings.Secure.BIOMETRIC_APP_ENABLED);
private final Uri MANDATORY_BIOMETRICS_ENABLED =
Settings.Secure.getUriFor(Settings.Secure.MANDATORY_BIOMETRICS);
+ private final Uri MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED = Settings.Secure.getUriFor(
+ Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED);
private final ContentResolver mContentResolver;
private final List<BiometricService.EnabledOnKeyguardCallback> mCallbacks;
@@ -264,6 +274,12 @@
private final Map<Integer, Boolean> mBiometricEnabledForApps = new HashMap<>();
private final Map<Integer, Boolean> mFaceAlwaysRequireConfirmation = new HashMap<>();
private final Map<Integer, Boolean> mMandatoryBiometricsEnabled = new HashMap<>();
+ private final Map<Integer, Boolean> mMandatoryBiometricsRequirementsSatisfied =
+ new HashMap<>();
+ private final Map<Integer, Boolean> mFingerprintEnrolledForUser =
+ new HashMap<>();
+ private final Map<Integer, Boolean> mFaceEnrolledForUser =
+ new HashMap<>();
/**
* Creates a content observer.
@@ -288,7 +304,13 @@
mMandatoryBiometricsEnabled.put(context.getUserId(), Settings.Secure.getIntForUser(
mContentResolver, Settings.Secure.MANDATORY_BIOMETRICS,
DEFAULT_MANDATORY_BIOMETRICS_STATUS ? 1 : 0, context.getUserId()) != 0);
+ mMandatoryBiometricsRequirementsSatisfied.put(context.getUserId(),
+ Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
+ DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS ? 1 : 0,
+ context.getUserId()) != 0);
+ addBiometricListenersForMandatoryBiometrics(context);
updateContentObserver();
}
@@ -322,6 +344,10 @@
false /* notifyForDescendants */,
this /* observer */,
UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
+ false /* notifyForDescendants */,
+ this /* observer */,
+ UserHandle.USER_ALL);
}
@Override
@@ -370,6 +396,13 @@
Settings.Secure.MANDATORY_BIOMETRICS,
DEFAULT_MANDATORY_BIOMETRICS_STATUS ? 1 : 0 /* default */,
userId) != 0);
+ } else if (MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED.equals(uri)) {
+ mMandatoryBiometricsRequirementsSatisfied.put(userId, Settings.Secure.getIntForUser(
+ mContentResolver,
+ Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
+ DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS
+ ? 1 : 0 /* default */,
+ userId) != 0);
}
}
@@ -411,9 +444,15 @@
}
}
- public boolean getMandatoryBiometricsEnabledForUser(int userId) {
+ public boolean getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(int userId) {
return mMandatoryBiometricsEnabled.getOrDefault(userId,
- DEFAULT_MANDATORY_BIOMETRICS_STATUS);
+ DEFAULT_MANDATORY_BIOMETRICS_STATUS)
+ && mMandatoryBiometricsRequirementsSatisfied.getOrDefault(userId,
+ DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS)
+ && mBiometricEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED)
+ && getEnabledForApps(userId)
+ && (mFingerprintEnrolledForUser.getOrDefault(userId, false /* default */)
+ || mFaceEnrolledForUser.getOrDefault(userId, false /* default */));
}
void notifyEnabledOnKeyguardCallbacks(int userId) {
@@ -424,6 +463,79 @@
userId);
}
}
+
+ private void addBiometricListenersForMandatoryBiometrics(Context context) {
+ final FingerprintManager fingerprintManager = context.getSystemService(
+ FingerprintManager.class);
+ final FaceManager faceManager = context.getSystemService(FaceManager.class);
+ if (fingerprintManager != null) {
+ fingerprintManager.addAuthenticatorsRegisteredCallback(
+ new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FingerprintSensorPropertiesInternal> list) {
+ if (list == null || list.isEmpty()) {
+ Slog.d(TAG, "No fingerprint authenticators registered.");
+ return;
+ }
+ final FingerprintSensorPropertiesInternal
+ fingerprintSensorProperties = list.get(0);
+ if (fingerprintSensorProperties.sensorStrength
+ == STRENGTH_STRONG) {
+ fingerprintManager.registerBiometricStateListener(
+ new BiometricStateListener() {
+ @Override
+ public void onEnrollmentsChanged(
+ int userId,
+ int sensorId,
+ boolean hasEnrollments
+ ) {
+ if (sensorId == fingerprintSensorProperties
+ .sensorId) {
+ mFingerprintEnrolledForUser.put(userId,
+ hasEnrollments);
+ }
+ }
+ });
+ }
+ }
+ });
+ }
+ if (faceManager != null) {
+ faceManager.addAuthenticatorsRegisteredCallback(
+ new IFaceAuthenticatorsRegisteredCallback.Stub() {
+ @Override
+ public void onAllAuthenticatorsRegistered(
+ List<FaceSensorPropertiesInternal> list) {
+ if (list == null || list.isEmpty()) {
+ Slog.d(TAG, "No face authenticators registered.");
+ return;
+ }
+ final FaceSensorPropertiesInternal
+ faceSensorPropertiesInternal = list.get(0);
+ if (faceSensorPropertiesInternal.sensorStrength
+ == STRENGTH_STRONG) {
+ faceManager.registerBiometricStateListener(
+ new BiometricStateListener() {
+ @Override
+ public void onEnrollmentsChanged(
+ int userId,
+ int sensorId,
+ boolean hasEnrollments
+ ) {
+ if (sensorId
+ == faceSensorPropertiesInternal
+ .sensorId) {
+ mFaceEnrolledForUser.put(userId,
+ hasEnrollments);
+ }
+ }
+ });
+ }
+ }
+ });
+ }
+ }
}
final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index b9e6563..0bd22f3 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -112,8 +112,8 @@
== BiometricManager.Authenticators.MANDATORY_BIOMETRICS;
if (dropCredentialFallback(promptInfo.getAuthenticators(),
- settingObserver.getMandatoryBiometricsEnabledForUser(userId),
- trustManager)) {
+ settingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(
+ userId), trustManager)) {
promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
promptInfo.setNegativeButtonText(context.getString(R.string.cancel));
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 73aa14b..78f7187 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -684,7 +684,8 @@
if (clipboard == null) {
return null;
}
- showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard);
+ showAccessNotificationLocked(
+ pkg, intendingUid, intendingUserId, clipboard, deviceId);
notifyTextClassifierLocked(clipboard, pkg, intendingUid);
if (clipboard.primaryClip != null) {
scheduleAutoClear(userId, intendingUid, intendingDeviceId);
@@ -1438,7 +1439,7 @@
*/
@GuardedBy("mLock")
private void showAccessNotificationLocked(String callingPackage, int uid, @UserIdInt int userId,
- Clipboard clipboard) {
+ Clipboard clipboard, int accessDeviceId) {
if (clipboard.primaryClip == null) {
return;
}
@@ -1477,7 +1478,7 @@
return;
}
- final ArraySet<Context> toastContexts = getToastContexts(clipboard);
+ final ArraySet<Context> toastContexts = getToastContexts(clipboard, accessDeviceId);
Binder.withCleanCallingIdentity(() -> {
try {
CharSequence callingAppLabel = mPm.getApplicationLabel(
@@ -1516,40 +1517,55 @@
* If the clipboard is for a VirtualDevice, we attempt to return the single DisplayContext for
* the focused VirtualDisplay for that device, but might need to return the contexts for
* multiple displays if the VirtualDevice has several but none of them were focused.
+ *
+ * If the clipboard is NOT for a VirtualDevice, but it's being accessed from a VirtualDevice,
+ * this means that the clipboard is shared between the default and that device. In this case we
+ * need to show a toast in both places.
*/
- private ArraySet<Context> getToastContexts(Clipboard clipboard) throws IllegalStateException {
+ private ArraySet<Context> getToastContexts(Clipboard clipboard, int accessDeviceId)
+ throws IllegalStateException {
ArraySet<Context> contexts = new ArraySet<>();
-
- if (mVdmInternal != null && clipboard.deviceId != DEVICE_ID_DEFAULT) {
- DisplayManager displayManager = getContext().getSystemService(DisplayManager.class);
-
- int topFocusedDisplayId = mWm.getTopFocusedDisplayId();
- ArraySet<Integer> displayIds = mVdmInternal.getDisplayIdsForDevice(clipboard.deviceId);
-
- if (displayIds.contains(topFocusedDisplayId)) {
- Display display = displayManager.getDisplay(topFocusedDisplayId);
- if (display != null) {
- contexts.add(getContext().createDisplayContext(display));
- return contexts;
- }
- }
-
- for (int i = 0; i < displayIds.size(); i++) {
- Display display = displayManager.getDisplay(displayIds.valueAt(i));
- if (display != null) {
- contexts.add(getContext().createDisplayContext(display));
- }
- }
- if (!contexts.isEmpty()) {
- return contexts;
- }
- Slog.e(TAG, "getToastContexts Couldn't find any VirtualDisplays for VirtualDevice "
- + clipboard.deviceId);
- // Since we couldn't find any VirtualDisplays to use at all, just fall through to using
- // the default display below.
+ if (clipboard.deviceId == DEVICE_ID_DEFAULT || accessDeviceId == DEVICE_ID_DEFAULT) {
+ // Always show the toast on the default display when the default clipboard is accessed -
+ // also when the clipboard is shared with a virtual device and accessed from there.
+ // Same when any clipboard is accessed from the default device.
+ contexts.add(getContext());
}
- contexts.add(getContext());
+ if ((accessDeviceId == DEVICE_ID_DEFAULT && clipboard.deviceId == DEVICE_ID_DEFAULT)
+ || mVdmInternal == null) {
+ // No virtual devices involved.
+ return contexts;
+ }
+
+ // At this point the clipboard is either accessed from a virtual device, or it is a virtual
+ // device clipboard, so show a toast on the relevant virtual display(s).
+ DisplayManager displayManager = getContext().getSystemService(DisplayManager.class);
+ ArraySet<Integer> displayIds = mVdmInternal.getDisplayIdsForDevice(accessDeviceId);
+ int topFocusedDisplayId = mWm.getTopFocusedDisplayId();
+
+ if (displayIds.contains(topFocusedDisplayId)) {
+ Display display = displayManager.getDisplay(topFocusedDisplayId);
+ if (display != null) {
+ contexts.add(getContext().createDisplayContext(display));
+ return contexts;
+ }
+ }
+
+ for (int i = 0; i < displayIds.size(); i++) {
+ Display display = displayManager.getDisplay(displayIds.valueAt(i));
+ if (display != null) {
+ contexts.add(getContext().createDisplayContext(display));
+ }
+ }
+ if (contexts.isEmpty()) {
+ Slog.e(TAG, "getToastContexts Couldn't find any VirtualDisplays for VirtualDevice "
+ + accessDeviceId);
+ // Since we couldn't find any VirtualDisplays to use at all, just fall through to using
+ // the default display below.
+ contexts.add(getContext());
+ }
+
return contexts;
}
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index 3d0b079..741513c 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -616,9 +616,10 @@
LocaleConfig resLocaleConfig = null;
try {
resLocaleConfig = LocaleConfig.fromContextIgnoringOverride(
- mContext.createPackageContext(appPackageName, 0));
+ mContext.createPackageContextAsUser(appPackageName, /* flags= */ 0,
+ UserHandle.of(userId)));
} catch (PackageManager.NameNotFoundException e) {
- Slog.e(TAG, "Unknown package name " + appPackageName);
+ Slog.e(TAG, "Unknown package name " + appPackageName + " for user " + userId);
return;
}
final File file = getXmlFileNameForUser(appPackageName, userId);
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 7de1045..3f4a9bb 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -297,7 +297,10 @@
}
public boolean isExpired() {
- return mTimestamp + ContextHubTransactionManager.RELIABLE_MESSAGE_TIMEOUT.toNanos()
+ return mTimestamp
+ + ContextHubTransactionManager
+ .RELIABLE_MESSAGE_DUPLICATE_DETECTION_TIMEOUT
+ .toNanos()
< SystemClock.elapsedRealtimeNanos();
}
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index e6d330f8..cd69eba 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -56,6 +56,9 @@
public static final Duration RELIABLE_MESSAGE_TIMEOUT = Duration.ofSeconds(1);
+ public static final Duration RELIABLE_MESSAGE_DUPLICATE_DETECTION_TIMEOUT =
+ RELIABLE_MESSAGE_TIMEOUT.multipliedBy(3);
+
private static final int MAX_PENDING_REQUESTS = 10000;
private static final int RELIABLE_MESSAGE_MAX_NUM_RETRY = 3;
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 803b125..621c090 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -702,7 +702,7 @@
}
}
- private final class BinderService extends IMediaProjectionManager.Stub {
+ final class BinderService extends IMediaProjectionManager.Stub {
BinderService(Context context) {
super(PermissionEnforcer.fromContext(context));
@@ -891,6 +891,13 @@
@Override
public void requestConsentForInvalidProjection(@NonNull IMediaProjection projection) {
requestConsentForInvalidProjection_enforcePermission();
+
+ if (android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()
+ && mKeyguardManager.isKeyguardLocked()) {
+ Slog.v(TAG, "Reusing token: Won't request consent while the keyguard is locked");
+ return;
+ }
+
synchronized (mLock) {
if (!isCurrentProjection(projection)) {
Slog.v(TAG, "Reusing token: Won't request consent again for a token that "
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index fde23b7..9b64488 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -16,6 +16,7 @@
package com.android.server.policy;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.role.RoleManager;
import android.content.ActivityNotFoundException;
@@ -248,31 +249,7 @@
+ " className=" + className + " shortcutChar=" + shortcutChar);
continue;
}
- ComponentName componentName = new ComponentName(packageName, className);
- try {
- mPackageManager.getActivityInfo(componentName,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_UNINSTALLED_PACKAGES);
- } catch (PackageManager.NameNotFoundException e) {
- String[] packages = mPackageManager.canonicalToCurrentPackageNames(
- new String[] { packageName });
- componentName = new ComponentName(packages[0], className);
- try {
- mPackageManager.getActivityInfo(componentName,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_UNINSTALLED_PACKAGES);
- } catch (PackageManager.NameNotFoundException e1) {
- Log.w(TAG, "Unable to add bookmark: " + packageName
- + "/" + className + " not found.");
- continue;
- }
- }
-
- intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(Intent.CATEGORY_LAUNCHER);
- intent.setComponent(componentName);
+ intent = resolveComponentNameIntent(packageName, className);
} else if (categoryName != null) {
if (roleName != null) {
Log.w(TAG, "Cannot specify role bookmark when category is present for"
@@ -310,6 +287,32 @@
}
}
+ @Nullable
+ private Intent resolveComponentNameIntent(String packageName, String className) {
+ int flags = PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES;
+ ComponentName componentName = new ComponentName(packageName, className);
+ try {
+ mPackageManager.getActivityInfo(componentName, flags);
+ } catch (PackageManager.NameNotFoundException e) {
+ String[] packages = mPackageManager.canonicalToCurrentPackageNames(
+ new String[] { packageName });
+ componentName = new ComponentName(packages[0], className);
+ try {
+ mPackageManager.getActivityInfo(componentName, flags);
+ } catch (PackageManager.NameNotFoundException e1) {
+ Log.w(TAG, "Unable to add bookmark: " + packageName
+ + "/" + className + " not found.");
+ return null;
+ }
+ }
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ intent.setComponent(componentName);
+ return intent;
+ }
+
void registerShortcutKey(long shortcutCode, IShortcutService shortcutService)
throws RemoteException {
IShortcutService service = mShortcutKeyServices.get(shortcutCode);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 8419a60..df97313 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2402,7 +2402,7 @@
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
}
- mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() {
+ final var transitionListener = new AppTransitionListener(DEFAULT_DISPLAY) {
@Override
public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
long statusBarAnimationDuration) {
@@ -2436,7 +2436,8 @@
mLockAfterDreamingTransitionFinished = false;
}
}
- });
+ };
+ mWindowManagerInternal.registerAppTransitionListener(transitionListener);
mKeyguardDrawnTimeout = mContext.getResources().getInteger(
com.android.internal.R.integer.config_keyguardDrawnTimeout);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 400919a..516fc65 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -53,7 +53,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
-import static android.app.WindowConfiguration.isFloating;
import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION;
import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE;
import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
@@ -336,7 +335,6 @@
import android.service.dreams.DreamActivity;
import android.service.voice.IVoiceInteractionSession;
import android.util.ArraySet;
-import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
import android.util.MergedConfiguration;
@@ -8648,7 +8646,14 @@
resolvedConfig.windowConfiguration.setMaxBounds(mTmpBounds);
}
- applySizeOverrideIfNeeded(newParentConfiguration, parentWindowingMode, resolvedConfig);
+ applySizeOverrideIfNeeded(
+ mDisplayContent,
+ info.applicationInfo,
+ newParentConfiguration,
+ resolvedConfig,
+ mOptOutEdgeToEdge,
+ hasFixedRotationTransform(),
+ getCompatDisplayInsets() != null);
mResolveConfigHint.resetTmpOverrides();
logAppCompatState();
@@ -8658,100 +8663,6 @@
return Rect.copyOrNull(mResolveConfigHint.mParentAppBoundsOverride);
}
- /**
- * If necessary, override configuration fields related to app bounds.
- * This will happen when the app is targeting SDK earlier than 35.
- * The insets and configuration has decoupled since SDK level 35, to make the system
- * compatible to existing apps, override the configuration with legacy metrics. In legacy
- * metrics, fields such as appBounds will exclude some of the system bar areas.
- * The override contains all potentially affected fields in Configuration, including
- * screenWidthDp, screenHeightDp, smallestScreenWidthDp, and orientation.
- * All overrides to those fields should be in this method.
- *
- * TODO: Consider integrate this with computeConfigByResolveHint()
- */
- private void applySizeOverrideIfNeeded(Configuration newParentConfiguration,
- int parentWindowingMode, Configuration inOutConfig) {
- if (mDisplayContent == null) {
- return;
- }
- final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
- int rotation = newParentConfiguration.windowConfiguration.getRotation();
- if (rotation == ROTATION_UNDEFINED && !isFixedRotationTransforming()) {
- rotation = mDisplayContent.getRotation();
- }
- if (!mOptOutEdgeToEdge && (!mResolveConfigHint.mUseOverrideInsetsForConfig
- || getCompatDisplayInsets() != null
- || (isFloating(parentWindowingMode)
- // Check the requested windowing mode of activity as well in case it is
- // switching between PiP and fullscreen.
- && (inOutConfig.windowConfiguration.getWindowingMode()
- == WINDOWING_MODE_UNDEFINED
- || isFloating(inOutConfig.windowConfiguration.getWindowingMode())))
- || rotation == ROTATION_UNDEFINED)) {
- // If the insets configuration decoupled logic is not enabled for the app, or the app
- // already has a compat override, or the context doesn't contain enough info to
- // calculate the override, skip the override.
- return;
- }
- // Make sure the orientation related fields will be updated by the override insets, because
- // fixed rotation has assigned the fields from display's configuration.
- if (hasFixedRotationTransform()) {
- inOutConfig.windowConfiguration.setAppBounds(null);
- inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
- inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
- inOutConfig.smallestScreenWidthDp = Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
- inOutConfig.orientation = ORIENTATION_UNDEFINED;
- }
-
- // Override starts here.
- final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- final int dw = rotated ? mDisplayContent.mBaseDisplayHeight
- : mDisplayContent.mBaseDisplayWidth;
- final int dh = rotated ? mDisplayContent.mBaseDisplayWidth
- : mDisplayContent.mBaseDisplayHeight;
- final Rect nonDecorInsets = mDisplayContent.getDisplayPolicy()
- .getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets;
- // This should be the only place override the configuration for ActivityRecord. Override
- // the value if not calculated yet.
- Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
- if (outAppBounds == null || outAppBounds.isEmpty()) {
- inOutConfig.windowConfiguration.setAppBounds(parentBounds);
- outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
- outAppBounds.inset(nonDecorInsets);
- }
- float density = inOutConfig.densityDpi;
- if (density == Configuration.DENSITY_DPI_UNDEFINED) {
- density = newParentConfiguration.densityDpi;
- }
- density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
- if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
- inOutConfig.screenWidthDp = (int) (outAppBounds.width() / density + 0.5f);
- }
- if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- inOutConfig.screenHeightDp = (int) (outAppBounds.height() / density + 0.5f);
- }
- if (inOutConfig.smallestScreenWidthDp
- == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
- && parentWindowingMode == WINDOWING_MODE_FULLSCREEN) {
- // For the case of PIP transition and multi-window environment, the
- // smallestScreenWidthDp is handled already. Override only if the app is in
- // fullscreen.
- final DisplayInfo info = new DisplayInfo(mDisplayContent.getDisplayInfo());
- mDisplayContent.computeSizeRanges(info, rotated, dw, dh,
- mDisplayContent.getDisplayMetrics().density,
- inOutConfig, true /* overrideConfig */);
- }
-
- // It's possible that screen size will be considered in different orientation with or
- // without considering the system bar insets. Override orientation as well.
- if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
- inOutConfig.orientation =
- (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
- ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
- }
- }
-
private void computeConfigByResolveHint(@NonNull Configuration resolvedConfig,
@NonNull Configuration parentConfig) {
task.computeConfigResourceOverrides(resolvedConfig, parentConfig, mResolveConfigHint);
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 8421765..0f8d68b 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -166,15 +166,14 @@
return null;
}
- // Move focus to the top embedded window if possible
- if (mWindowManagerService.moveFocusToAdjacentEmbeddedWindow(window)) {
- window = wmService.getFocusedWindowLocked();
- if (window == null) {
- Slog.e(TAG, "New focused window is null, returning null.");
- return null;
- }
+ // Updating the window to the most recently used one among the embedded windows
+ // that are displayed adjacently, unless the IME is visible.
+ // When the IME is visible, the IME is displayed on top of embedded activities.
+ // In that case, the back event should still be delivered to focused activity in
+ // order to dismiss the IME.
+ if (!window.getDisplayContent().getImeContainer().isVisible()) {
+ window = mWindowManagerService.getMostRecentUsedEmbeddedWindowForBack(window);
}
-
if (!window.isDrawn()) {
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"Focused window didn't have a valid surface drawn.");
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index efd5202..3ebaf03 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -22,14 +22,23 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.isFloating;
import static android.app.WindowConfiguration.windowingModeToString;
import static android.app.WindowConfigurationProto.WINDOWING_MODE;
import static android.content.ConfigurationProto.WINDOW_CONFIGURATION;
+import static android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED;
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
import static com.android.server.wm.ConfigurationContainerProto.FULL_CONFIGURATION;
import static com.android.server.wm.ConfigurationContainerProto.MERGED_OVERRIDE_CONFIGURATION;
@@ -38,11 +47,14 @@
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.app.WindowConfiguration;
+import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.LocaleList;
+import android.util.DisplayMetrics;
import android.util.proto.ProtoOutputStream;
+import android.view.DisplayInfo;
import com.android.internal.annotations.VisibleForTesting;
@@ -173,6 +185,110 @@
mResolvedOverrideConfiguration.setTo(mRequestedOverrideConfiguration);
}
+ /**
+ * If necessary, override configuration fields related to app bounds.
+ * This will happen when the app is targeting SDK earlier than 35.
+ * The insets and configuration has decoupled since SDK level 35, to make the system
+ * compatible to existing apps, override the configuration with legacy metrics. In legacy
+ * metrics, fields such as appBounds will exclude some of the system bar areas.
+ * The override contains all potentially affected fields in Configuration, including
+ * screenWidthDp, screenHeightDp, smallestScreenWidthDp, and orientation.
+ * All overrides to those fields should be in this method.
+ *
+ * TODO: Consider integrate this with computeConfigByResolveHint()
+ */
+ static void applySizeOverrideIfNeeded(DisplayContent displayContent, ApplicationInfo appInfo,
+ Configuration newParentConfiguration, Configuration inOutConfig,
+ boolean optsOutEdgeToEdge, boolean hasFixedRotationTransform,
+ boolean hasCompatDisplayInsets) {
+ if (displayContent == null) {
+ return;
+ }
+ final boolean useOverrideInsetsForConfig =
+ displayContent.mWmService.mFlags.mInsetsDecoupledConfiguration
+ ? !appInfo.isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED)
+ && !appInfo.isChangeEnabled(
+ OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION)
+ : appInfo.isChangeEnabled(OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION);
+ final int parentWindowingMode =
+ newParentConfiguration.windowConfiguration.getWindowingMode();
+ final boolean isFloating = isFloating(parentWindowingMode)
+ // Check the requested windowing mode of activity as well in case it is
+ // switching between PiP and fullscreen.
+ && (inOutConfig.windowConfiguration.getWindowingMode() == WINDOWING_MODE_UNDEFINED
+ || isFloating(inOutConfig.windowConfiguration.getWindowingMode()));
+ int rotation = newParentConfiguration.windowConfiguration.getRotation();
+ if (rotation == ROTATION_UNDEFINED && !hasFixedRotationTransform) {
+ rotation = displayContent.getRotation();
+ }
+ if (!optsOutEdgeToEdge && (!useOverrideInsetsForConfig
+ || hasCompatDisplayInsets
+ || isFloating
+ || rotation == ROTATION_UNDEFINED)) {
+ // If the insets configuration decoupled logic is not enabled for the app, or the app
+ // already has a compat override, or the context doesn't contain enough info to
+ // calculate the override, skip the override.
+ return;
+ }
+ // Make sure the orientation related fields will be updated by the override insets, because
+ // fixed rotation has assigned the fields from display's configuration.
+ if (hasFixedRotationTransform) {
+ inOutConfig.windowConfiguration.setAppBounds(null);
+ inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
+ inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+ inOutConfig.smallestScreenWidthDp = Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
+ inOutConfig.orientation = ORIENTATION_UNDEFINED;
+ }
+
+ // Override starts here.
+ final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
+ final int dw = rotated
+ ? displayContent.mBaseDisplayHeight
+ : displayContent.mBaseDisplayWidth;
+ final int dh = rotated
+ ? displayContent.mBaseDisplayWidth
+ : displayContent.mBaseDisplayHeight;
+ // This should be the only place override the configuration for ActivityRecord. Override
+ // the value if not calculated yet.
+ Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+ if (outAppBounds == null || outAppBounds.isEmpty()) {
+ inOutConfig.windowConfiguration.setAppBounds(
+ newParentConfiguration.windowConfiguration.getBounds());
+ outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+ outAppBounds.inset(displayContent.getDisplayPolicy()
+ .getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets);
+ }
+ float density = inOutConfig.densityDpi;
+ if (density == Configuration.DENSITY_DPI_UNDEFINED) {
+ density = newParentConfiguration.densityDpi;
+ }
+ density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
+ if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
+ inOutConfig.screenWidthDp = (int) (outAppBounds.width() / density + 0.5f);
+ }
+ if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+ inOutConfig.screenHeightDp = (int) (outAppBounds.height() / density + 0.5f);
+ }
+ if (inOutConfig.smallestScreenWidthDp == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
+ && parentWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+ // For the case of PIP transition and multi-window environment, the
+ // smallestScreenWidthDp is handled already. Override only if the app is in
+ // fullscreen.
+ final DisplayInfo info = new DisplayInfo(displayContent.getDisplayInfo());
+ displayContent.computeSizeRanges(info, rotated, dw, dh,
+ displayContent.getDisplayMetrics().density,
+ inOutConfig, true /* overrideConfig */);
+ }
+
+ // It's possible that screen size will be considered in different orientation with or
+ // without considering the system bar insets. Override orientation as well.
+ if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
+ inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
+ ? ORIENTATION_PORTRAIT
+ : ORIENTATION_LANDSCAPE;
+ }
+ }
+
/** Returns {@code true} if requested override override configuration is not empty. */
boolean hasRequestedOverrideConfiguration() {
return mHasOverrideConfiguration;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9371149..fa60368 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3895,6 +3895,22 @@
}
/**
+ * Returns the focused window of the given Activity if the Activity is focused.
+ */
+ WindowState findFocusedWindow(ActivityRecord activityRecord) {
+ final ActivityRecord tmpApp = mFocusedApp;
+ mTmpWindow = null;
+ try {
+ mFocusedApp = activityRecord;
+ // mFindFocusedWindow will populate mTmpWindow with the new focused window when found.
+ activityRecord.forAllWindows(mFindFocusedWindow, true /* traverseTopToBottom */);
+ } finally {
+ mFocusedApp = tmpApp;
+ }
+ return mTmpWindow;
+ }
+
+ /**
* Update the focused window and make some adjustments if the focus has changed.
*
* @param mode Indicates the situation we are in. Possible modes are:
@@ -6911,6 +6927,10 @@
/** Whether {@link #mAnimatingRecents} is going to be the top activity. */
private boolean mRecentsWillBeTop;
+ FixedRotationTransitionListener() {
+ super(DisplayContent.this.mDisplayId);
+ }
+
/**
* If the recents activity has a fixed orientation which is different from the current top
* activity, it will be rotated before being shown so we avoid a screen rotation animation
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 80362a4..c3339cd 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -588,7 +588,7 @@
gesturesPointerEventCallbacks);
displayContent.registerPointerEventListener(mSystemGestures);
}
- mAppTransitionListener = new WindowManagerInternal.AppTransitionListener() {
+ mAppTransitionListener = new WindowManagerInternal.AppTransitionListener(displayId) {
private Runnable mAppTransitionPending = () -> {
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index 63fe94c..e50a089 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -44,6 +44,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
import com.android.server.UiThread;
+import com.android.window.flags.Flags;
/**
* Controls camera compatibility treatment that handles orientation mismatch between camera
@@ -69,6 +70,9 @@
@NonNull
private final ActivityRefresher mActivityRefresher;
+ @Nullable
+ private Task mCameraTask;
+
@ScreenOrientation
private int mLastReportedOrientation = SCREEN_ORIENTATION_UNSET;
@@ -104,7 +108,7 @@
* guaranteed to match, the rotation can cause letterboxing.
*
* <p>If treatment isn't applicable returns {@link SCREEN_ORIENTATION_UNSPECIFIED}. See {@link
- * #shouldComputeCameraCompatOrientation} for conditions enabling the treatment.
+ * #isTreatmentEnabledForDisplay} for conditions enabling the treatment.
*/
@ScreenOrientation
int getOrientation() {
@@ -136,9 +140,9 @@
// are aligned when they compute orientation of the preview.
// This means that even for a landscape-only activity and a device with landscape natural
// orientation this would return SCREEN_ORIENTATION_PORTRAIT because an assumption that
- // natural orientation = portrait window = portait camera is the main wrong assumption
+ // natural orientation = portrait window = portrait camera is the main wrong assumption
// that apps make when they implement camera previews so landscape windows need be
- // rotated in the orientation oposite to the natural one even if it's portrait.
+ // rotated in the orientation opposite to the natural one even if it's portrait.
// TODO(b/261475895): Consider allowing more rotations for "sensor" and "user" versions
// of the portrait and landscape orientation requests.
final int orientation = (isPortraitActivity && isNaturalDisplayOrientationPortrait)
@@ -296,6 +300,7 @@
@Override
public boolean onCameraOpened(@NonNull ActivityRecord cameraActivity,
@NonNull String cameraId) {
+ mCameraTask = cameraActivity.getTask();
// Checking whether an activity in fullscreen rather than the task as this camera
// compat treatment doesn't cover activity embedding.
if (cameraActivity.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
@@ -305,7 +310,7 @@
}
// Checking that the whole app is in multi-window mode as we shouldn't show toast
// for the activity embedding case.
- if (cameraActivity.getTask().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW
+ if (mCameraTask != null && mCameraTask.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW
&& isTreatmentEnabledForActivity(
cameraActivity, /* mustBeFullscreen */ false)) {
final PackageManager packageManager = mWmService.mContext.getPackageManager();
@@ -343,10 +348,15 @@
@Override
public boolean onCameraClosed(@NonNull String cameraId) {
- // Top activity in the same task as the camera activity, or `null` if the task is
- // closed.
- final ActivityRecord topActivity = mDisplayContent.topRunningActivity(
- /* considerKeyguardState= */ true);
+ final ActivityRecord topActivity;
+ if (Flags.cameraCompatFullscreenPickSameTaskActivity()) {
+ topActivity = mCameraTask != null ? mCameraTask.getTopActivity(
+ /* includeFinishing= */ true, /* includeOverlays= */ false) : null;
+ } else {
+ topActivity = mDisplayContent.topRunningActivity(/* considerKeyguardState= */ true);
+ }
+
+ mCameraTask = null;
if (topActivity == null) {
return true;
}
@@ -368,8 +378,6 @@
mDisplayContent.mDisplayId);
// Checking whether an activity in fullscreen rather than the task as this camera compat
// treatment doesn't cover activity embedding.
- // TODO(b/350495350): Consider checking whether this activity is the camera activity, or
- // whether the top activity has the same task as the one which opened camera.
if (topActivity.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
return true;
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index c26684f..cc95518 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -39,7 +39,6 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
@@ -537,27 +536,11 @@
@Override
public boolean startMovingTask(IWindow window, float startX, float startY) {
- if (DEBUG_TASK_POSITIONING) Slog.d(
- TAG_WM, "startMovingTask: {" + startX + "," + startY + "}");
-
- final long ident = Binder.clearCallingIdentity();
- try {
- return mService.mTaskPositioningController.startMovingTask(window, startX, startY);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ return false;
}
@Override
public void finishMovingTask(IWindow window) {
- if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishMovingTask");
-
- final long ident = Binder.clearCallingIdentity();
- try {
- mService.mTaskPositioningController.finishTaskPositioning(window);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
deleted file mode 100644
index 972dd2e..0000000
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * Copyright (C) 2015 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.server.wm;
-
-import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
-import static android.app.ActivityTaskManager.RESIZE_MODE_USER_FORCED;
-import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
-import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
-import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.dipToPixel;
-import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
-import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
-
-import static java.util.concurrent.CompletableFuture.completedFuture;
-
-import android.annotation.NonNull;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.InputConfig;
-import android.os.RemoteException;
-import android.os.Trace;
-import android.util.DisplayMetrics;
-import android.util.Slog;
-import android.view.BatchedInputEventReceiver;
-import android.view.InputApplicationHandle;
-import android.view.InputChannel;
-import android.view.InputDevice;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
-import android.view.InputWindowHandle;
-import android.view.MotionEvent;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.policy.TaskResizingAlgorithm;
-import com.android.internal.policy.TaskResizingAlgorithm.CtrlType;
-import com.android.internal.protolog.ProtoLog;
-
-import java.util.concurrent.CompletableFuture;
-
-class TaskPositioner implements IBinder.DeathRecipient {
- private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
- private static final String TAG_LOCAL = "TaskPositioner";
- private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
-
- private static Factory sFactory;
-
- public static final float RESIZING_HINT_ALPHA = 0.5f;
-
- public static final int RESIZING_HINT_DURATION_MS = 0;
-
- private final WindowManagerService mService;
- private InputEventReceiver mInputEventReceiver;
- private DisplayContent mDisplayContent;
- private Rect mTmpRect = new Rect();
- private int mMinVisibleWidth;
- private int mMinVisibleHeight;
-
- @VisibleForTesting
- Task mTask;
- WindowState mWindow;
- private boolean mResizing;
- private boolean mPreserveOrientation;
- private boolean mStartOrientationWasLandscape;
- private final Rect mWindowOriginalBounds = new Rect();
- private final Rect mWindowDragBounds = new Rect();
- private final Point mMaxVisibleSize = new Point();
- private float mStartDragX;
- private float mStartDragY;
- @CtrlType
- private int mCtrlType = CTRL_NONE;
- @VisibleForTesting
- boolean mDragEnded;
- IBinder mClientCallback;
-
- InputChannel mClientChannel;
- InputApplicationHandle mDragApplicationHandle;
- InputWindowHandle mDragWindowHandle;
-
- /** Use {@link #create(WindowManagerService)} instead. */
- @VisibleForTesting
- TaskPositioner(WindowManagerService service) {
- mService = service;
- }
-
- private boolean onInputEvent(InputEvent event) {
- // All returns need to be in the try block to make sure the finishInputEvent is
- // called correctly.
- if (!(event instanceof MotionEvent)
- || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
- return false;
- }
- final MotionEvent motionEvent = (MotionEvent) event;
- if (mDragEnded) {
- // The drag has ended but the clean-up message has not been processed by
- // window manager. Drop events that occur after this until window manager
- // has a chance to clean-up the input handle.
- return true;
- }
-
- final float newX = motionEvent.getRawX();
- final float newY = motionEvent.getRawY();
-
- switch (motionEvent.getAction()) {
- case MotionEvent.ACTION_DOWN: {
- if (DEBUG_TASK_POSITIONING) {
- Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
- }
- }
- break;
-
- case MotionEvent.ACTION_MOVE: {
- if (DEBUG_TASK_POSITIONING) {
- Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
- }
- synchronized (mService.mGlobalLock) {
- mDragEnded = notifyMoveLocked(newX, newY);
- mTask.getDimBounds(mTmpRect);
- }
- if (!mTmpRect.equals(mWindowDragBounds)) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
- "wm.TaskPositioner.resizeTask");
- mService.mAtmService.resizeTask(
- mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
- }
- }
- break;
-
- case MotionEvent.ACTION_UP: {
- if (DEBUG_TASK_POSITIONING) {
- Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
- }
- mDragEnded = true;
- }
- break;
-
- case MotionEvent.ACTION_CANCEL: {
- if (DEBUG_TASK_POSITIONING) {
- Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
- }
- mDragEnded = true;
- }
- break;
- }
-
- if (mDragEnded) {
- final boolean wasResizing = mResizing;
- synchronized (mService.mGlobalLock) {
- endDragLocked();
- mTask.getDimBounds(mTmpRect);
- }
- if (wasResizing && !mTmpRect.equals(mWindowDragBounds)) {
- // We were using fullscreen surface during resizing. Request
- // resizeTask() one last time to restore surface to window size.
- mService.mAtmService.resizeTask(
- mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
- }
-
- // Post back to WM to handle clean-ups. We still need the input
- // event handler for the last finishInputEvent()!
- mService.mTaskPositioningController.finishTaskPositioning();
- }
- return true;
- }
-
- @VisibleForTesting
- Rect getWindowDragBounds() {
- return mWindowDragBounds;
- }
-
- /**
- * @param displayContent The Display that the window being dragged is on.
- * @param win The window which will be dragged.
- */
- CompletableFuture<Void> register(DisplayContent displayContent, @NonNull WindowState win) {
- if (DEBUG_TASK_POSITIONING) {
- Slog.d(TAG, "Registering task positioner");
- }
-
- if (mClientChannel != null) {
- Slog.e(TAG, "Task positioner already registered");
- return completedFuture(null);
- }
-
- mDisplayContent = displayContent;
- mClientChannel = mService.mInputManager.createInputChannel(TAG);
-
- mInputEventReceiver = new BatchedInputEventReceiver.SimpleBatchedInputEventReceiver(
- mClientChannel, mService.mAnimationHandler.getLooper(),
- mService.mAnimator.getChoreographer(), this::onInputEvent);
-
- mDragApplicationHandle = new InputApplicationHandle(new Binder(), TAG,
- DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
-
- mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
- displayContent.getDisplayId());
- mDragWindowHandle.name = TAG;
- mDragWindowHandle.token = mClientChannel.getToken();
- mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
- mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
- mDragWindowHandle.ownerPid = WindowManagerService.MY_PID;
- mDragWindowHandle.ownerUid = WindowManagerService.MY_UID;
- mDragWindowHandle.scaleFactor = 1.0f;
- // When dragging the window around, we do not want to steal focus for the window.
- mDragWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE;
-
- // The drag window cannot receive new touches.
- mDragWindowHandle.touchableRegion.setEmpty();
-
- // Pause rotations before a drag.
- ProtoLog.d(WM_DEBUG_ORIENTATION, "Pausing rotation during re-position");
- mDisplayContent.getDisplayRotation().pause();
-
- // Notify InputMonitor to take mDragWindowHandle.
- return mService.mTaskPositioningController.showInputSurface(win.getDisplayId())
- .thenRun(() -> {
- // The global lock is held by the callers of register but released before the async
- // results are waited on. We must acquire the lock in this callback to ensure thread
- // safety.
- synchronized (mService.mGlobalLock) {
- final Rect displayBounds = mTmpRect;
- displayContent.getBounds(displayBounds);
- final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics();
- mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics);
- mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics);
- mMaxVisibleSize.set(displayBounds.width(), displayBounds.height());
-
- mDragEnded = false;
-
- try {
- mClientCallback = win.mClient.asBinder();
- mClientCallback.linkToDeath(this, 0 /* flags */);
- } catch (RemoteException e) {
- // The caller has died, so clean up TaskPositioningController.
- mService.mTaskPositioningController.finishTaskPositioning();
- return;
- }
- mWindow = win;
- mTask = win.getTask();
- }
- });
- }
-
- void unregister() {
- if (DEBUG_TASK_POSITIONING) {
- Slog.d(TAG, "Unregistering task positioner");
- }
-
- if (mClientChannel == null) {
- Slog.e(TAG, "Task positioner not registered");
- return;
- }
-
- mService.mTaskPositioningController.hideInputSurface(mDisplayContent.getDisplayId());
- mService.mInputManager.removeInputChannel(mClientChannel.getToken());
-
- mInputEventReceiver.dispose();
- mInputEventReceiver = null;
- mClientChannel.dispose();
- mClientChannel = null;
-
- mDragWindowHandle = null;
- mDragApplicationHandle = null;
- mDragEnded = true;
-
- // Notify InputMonitor to remove mDragWindowHandle.
- mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
-
- // Resume rotations after a drag.
- ProtoLog.d(WM_DEBUG_ORIENTATION, "Resuming rotation after re-position");
- mDisplayContent.getDisplayRotation().resume();
- mDisplayContent = null;
- if (mClientCallback != null) {
- mClientCallback.unlinkToDeath(this, 0 /* flags */);
- }
- mWindow = null;
- }
-
- /**
- * Starts moving or resizing the task. This method should be only called from
- * {@link TaskPositioningController#startPositioningLocked} or unit tests.
- */
- void startDrag(boolean resize, boolean preserveOrientation, float startX, float startY) {
- if (DEBUG_TASK_POSITIONING) {
- Slog.d(TAG, "startDrag: win=" + mWindow + ", resize=" + resize
- + ", preserveOrientation=" + preserveOrientation + ", {" + startX + ", "
- + startY + "}");
- }
- // Use the bounds of the task which accounts for
- // multiple app windows. Don't use any bounds from win itself as it
- // may not be the same size as the task.
- final Rect startBounds = mTmpRect;
- mTask.getBounds(startBounds);
-
- mCtrlType = CTRL_NONE;
- mStartDragX = startX;
- mStartDragY = startY;
- mPreserveOrientation = preserveOrientation;
-
- if (resize) {
- if (startX < startBounds.left) {
- mCtrlType |= CTRL_LEFT;
- }
- if (startX > startBounds.right) {
- mCtrlType |= CTRL_RIGHT;
- }
- if (startY < startBounds.top) {
- mCtrlType |= CTRL_TOP;
- }
- if (startY > startBounds.bottom) {
- mCtrlType |= CTRL_BOTTOM;
- }
- mResizing = mCtrlType != CTRL_NONE;
- }
-
- // In case of !isDockedInEffect we are using the union of all task bounds. These might be
- // made up out of multiple windows which are only partially overlapping. When that happens,
- // the orientation from the window of interest to the entire stack might diverge. However
- // for now we treat them as the same.
- mStartOrientationWasLandscape = startBounds.width() >= startBounds.height();
- mWindowOriginalBounds.set(startBounds);
-
- // Notify the app that resizing has started, even though we haven't received any new
- // bounds yet. This will guarantee that the app starts the backdrop renderer before
- // configuration changes which could cause an activity restart.
- if (mResizing) {
- notifyMoveLocked(startX, startY);
-
- // The WindowPositionerEventReceiver callbacks are delivered on the same handler so this
- // initial resize is always guaranteed to happen before subsequent drag resizes.
- mService.mH.post(() -> {
- mService.mAtmService.resizeTask(
- mTask.mTaskId, startBounds, RESIZE_MODE_USER_FORCED);
- });
- }
-
- // Make sure we always have valid drag bounds even if the drag ends before any move events
- // have been handled.
- mWindowDragBounds.set(startBounds);
- }
-
- private void endDragLocked() {
- mResizing = false;
- mTask.setDragResizing(false);
- }
-
- /** Returns true if the move operation should be ended. */
- @VisibleForTesting
- boolean notifyMoveLocked(float x, float y) {
- if (DEBUG_TASK_POSITIONING) {
- Slog.d(TAG, "notifyMoveLocked: {" + x + "," + y + "}");
- }
-
- if (mCtrlType != CTRL_NONE) {
- resizeDrag(x, y);
- mTask.setDragResizing(true);
- return false;
- }
-
- // This is a moving or scrolling operation.
- // Only allow to move in stable area so the target window won't be covered by system bar.
- // Though {@link Task#resolveOverrideConfiguration} should also avoid the case.
- mDisplayContent.getStableRect(mTmpRect);
- // The task may be put in a limited display area.
- mTmpRect.intersect(mTask.getRootTask().getParent().getBounds());
-
- int nX = (int) x;
- int nY = (int) y;
- if (!mTmpRect.contains(nX, nY)) {
- // For a moving operation we allow the pointer to go out of the stack bounds, but
- // use the clamped pointer position for the drag bounds computation.
- nX = Math.min(Math.max(nX, mTmpRect.left), mTmpRect.right);
- nY = Math.min(Math.max(nY, mTmpRect.top), mTmpRect.bottom);
- }
-
- updateWindowDragBounds(nX, nY, mTmpRect);
- return false;
- }
-
- /**
- * The user is drag - resizing the window.
- *
- * @param x The x coordinate of the current drag coordinate.
- * @param y the y coordinate of the current drag coordinate.
- */
- @VisibleForTesting
- void resizeDrag(float x, float y) {
- updateDraggedBounds(TaskResizingAlgorithm.resizeDrag(x, y, mStartDragX, mStartDragY,
- mWindowOriginalBounds, mCtrlType, mMinVisibleWidth, mMinVisibleHeight,
- mMaxVisibleSize, mPreserveOrientation, mStartOrientationWasLandscape));
- }
-
- private void updateDraggedBounds(Rect newBounds) {
- mWindowDragBounds.set(newBounds);
-
- checkBoundsForOrientationViolations(mWindowDragBounds);
- }
-
- /**
- * Validate bounds against orientation violations (if DEBUG_ORIENTATION_VIOLATIONS is set).
- *
- * @param bounds The bounds to be checked.
- */
- private void checkBoundsForOrientationViolations(Rect bounds) {
- // When using debug check that we are not violating the given constraints.
- if (DEBUG_ORIENTATION_VIOLATIONS) {
- if (mStartOrientationWasLandscape != (bounds.width() >= bounds.height())) {
- Slog.e(TAG, "Orientation violation detected! should be "
- + (mStartOrientationWasLandscape ? "landscape" : "portrait")
- + " but is the other");
- } else {
- Slog.v(TAG, "new bounds size: " + bounds.width() + " x " + bounds.height());
- }
- if (mMinVisibleWidth > bounds.width() || mMinVisibleHeight > bounds.height()) {
- Slog.v(TAG, "Minimum requirement violated: Width(min, is)=(" + mMinVisibleWidth
- + ", " + bounds.width() + ") Height(min,is)=("
- + mMinVisibleHeight + ", " + bounds.height() + ")");
- }
- if (mMaxVisibleSize.x < bounds.width() || mMaxVisibleSize.y < bounds.height()) {
- Slog.v(TAG, "Maximum requirement violated: Width(min, is)=(" + mMaxVisibleSize.x
- + ", " + bounds.width() + ") Height(min,is)=("
- + mMaxVisibleSize.y + ", " + bounds.height() + ")");
- }
- }
- }
-
- private void updateWindowDragBounds(int x, int y, Rect rootTaskBounds) {
- final int offsetX = Math.round(x - mStartDragX);
- final int offsetY = Math.round(y - mStartDragY);
- mWindowDragBounds.set(mWindowOriginalBounds);
- // Horizontally, at least mMinVisibleWidth pixels of the window should remain visible.
- final int maxLeft = rootTaskBounds.right - mMinVisibleWidth;
- final int minLeft = rootTaskBounds.left + mMinVisibleWidth - mWindowOriginalBounds.width();
-
- // Vertically, the top mMinVisibleHeight of the window should remain visible.
- // (This assumes that the window caption bar is at the top of the window).
- final int minTop = rootTaskBounds.top;
- final int maxTop = rootTaskBounds.bottom - mMinVisibleHeight;
-
- mWindowDragBounds.offsetTo(
- Math.min(Math.max(mWindowOriginalBounds.left + offsetX, minLeft), maxLeft),
- Math.min(Math.max(mWindowOriginalBounds.top + offsetY, minTop), maxTop));
-
- if (DEBUG_TASK_POSITIONING) Slog.d(TAG,
- "updateWindowDragBounds: " + mWindowDragBounds);
- }
-
- public String toShortString() {
- return TAG;
- }
-
- static void setFactory(Factory factory) {
- sFactory = factory;
- }
-
- static TaskPositioner create(WindowManagerService service) {
- if (sFactory == null) {
- sFactory = new Factory() {};
- }
-
- return sFactory.create(service);
- }
-
- @Override
- public void binderDied() {
- mService.mTaskPositioningController.finishTaskPositioning();
- }
-
- interface Factory {
- default TaskPositioner create(WindowManagerService service) {
- return new TaskPositioner(service);
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
deleted file mode 100644
index 6f548ab..0000000
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.wm;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import static java.util.concurrent.CompletableFuture.completedFuture;
-
-import android.annotation.Nullable;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.util.Slog;
-import android.view.Display;
-import android.view.IWindow;
-import android.view.InputWindowHandle;
-import android.view.SurfaceControl;
-
-import java.util.concurrent.CompletableFuture;
-
-/**
- * Controller for task positioning by drag.
- */
-class TaskPositioningController {
- private final WindowManagerService mService;
- private SurfaceControl mInputSurface;
- private DisplayContent mPositioningDisplay;
-
- private @Nullable TaskPositioner mTaskPositioner;
-
- private final Rect mTmpClipRect = new Rect();
-
- boolean isPositioningLocked() {
- return mTaskPositioner != null;
- }
-
- final SurfaceControl.Transaction mTransaction;
-
- InputWindowHandle getDragWindowHandleLocked() {
- return mTaskPositioner != null ? mTaskPositioner.mDragWindowHandle : null;
- }
-
- TaskPositioningController(WindowManagerService service) {
- mService = service;
- mTransaction = service.mTransactionFactory.get();
- }
-
- void hideInputSurface(int displayId) {
- if (mPositioningDisplay != null && mPositioningDisplay.getDisplayId() == displayId
- && mInputSurface != null) {
- mTransaction.hide(mInputSurface).apply();
- }
- }
-
- /**
- * @return a future that completes after window info is sent.
- */
- CompletableFuture<Void> showInputSurface(int displayId) {
- if (mPositioningDisplay == null || mPositioningDisplay.getDisplayId() != displayId) {
- return completedFuture(null);
- }
- final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
- if (mInputSurface == null) {
- mInputSurface = mService.makeSurfaceBuilder(dc.getSession())
- .setContainerLayer()
- .setName("Drag and Drop Input Consumer")
- .setCallsite("TaskPositioningController.showInputSurface")
- .setParent(dc.getOverlayLayer())
- .build();
- }
-
- final InputWindowHandle h = getDragWindowHandleLocked();
- if (h == null) {
- Slog.w(TAG_WM, "Drag is in progress but there is no "
- + "drag window handle.");
- return completedFuture(null);
- }
-
- final Display display = dc.getDisplay();
- final Point p = new Point();
- display.getRealSize(p);
- mTmpClipRect.set(0, 0, p.x, p.y);
-
- CompletableFuture<Void> result = new CompletableFuture<>();
- mTransaction.show(mInputSurface)
- .setInputWindowInfo(mInputSurface, h)
- .setLayer(mInputSurface, Integer.MAX_VALUE)
- .setPosition(mInputSurface, 0, 0)
- .setCrop(mInputSurface, mTmpClipRect)
- .addWindowInfosReportedListener(() -> result.complete(null))
- .apply();
- return result;
- }
-
- boolean startMovingTask(IWindow window, float startX, float startY) {
- WindowState win = null;
- CompletableFuture<Boolean> startPositioningLockedFuture;
- synchronized (mService.mGlobalLock) {
- win = mService.windowForClientLocked(null, window, false);
- startPositioningLockedFuture =
- startPositioningLocked(
- win, false /*resize*/, false /*preserveOrientation*/, startX, startY);
- }
-
- try {
- if (!startPositioningLockedFuture.get()) {
- return false;
- }
- } catch (Exception exception) {
- Slog.e(TAG_WM, "Exception thrown while waiting for startPositionLocked future",
- exception);
- return false;
- }
-
- synchronized (mService.mGlobalLock) {
- mService.mAtmService.setFocusedTask(win.getTask().mTaskId);
- }
- return true;
- }
-
- void handleTapOutsideTask(DisplayContent displayContent, int x, int y) {
- mService.mH.post(() -> {
- Task task;
- CompletableFuture<Boolean> startPositioningLockedFuture;
- synchronized (mService.mGlobalLock) {
- task = displayContent.findTaskForResizePoint(x, y);
- if (task == null || !task.isResizeable()) {
- // The task is not resizable, so don't do anything when the user drags the
- // the resize handles.
- return;
- }
- startPositioningLockedFuture =
- startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
- task.preserveOrientationOnResize(), x, y);
- }
-
- try {
- if (!startPositioningLockedFuture.get()) {
- return;
- }
- } catch (Exception exception) {
- Slog.e(TAG_WM, "Exception thrown while waiting for startPositionLocked future",
- exception);
- return;
- }
-
- synchronized (mService.mGlobalLock) {
- mService.mAtmService.setFocusedTask(task.mTaskId);
- }
- });
- }
-
- private CompletableFuture<Boolean> startPositioningLocked(WindowState win, boolean resize,
- boolean preserveOrientation, float startX, float startY) {
- if (DEBUG_TASK_POSITIONING)
- Slog.d(TAG_WM, "startPositioningLocked: "
- + "win=" + win + ", resize=" + resize + ", preserveOrientation="
- + preserveOrientation + ", {" + startX + ", " + startY + "}");
-
- if (win == null || win.mActivityRecord == null) {
- Slog.w(TAG_WM, "startPositioningLocked: Bad window " + win);
- return completedFuture(false);
- }
- if (win.mInputChannel == null) {
- Slog.wtf(TAG_WM, "startPositioningLocked: " + win + " has no input channel, "
- + " probably being removed");
- return completedFuture(false);
- }
-
- final DisplayContent displayContent = win.getDisplayContent();
- if (displayContent == null) {
- Slog.w(TAG_WM, "startPositioningLocked: Invalid display content " + win);
- return completedFuture(false);
- }
- mPositioningDisplay = displayContent;
-
- mTaskPositioner = TaskPositioner.create(mService);
- return mTaskPositioner.register(displayContent, win).thenApply(unused -> {
- // The global lock is held by the callers of startPositioningLocked but released before
- // the async results are waited on. We must acquire the lock in this callback to ensure
- // thread safety.
- synchronized (mService.mGlobalLock) {
- // We need to grab the touch focus so that the touch events during the
- // resizing/scrolling are not sent to the app. 'win' is the main window
- // of the app, it may not have focus since there might be other windows
- // on top (eg. a dialog window).
- WindowState transferTouchFromWin = win;
- if (displayContent.mCurrentFocus != null && displayContent.mCurrentFocus != win
- && displayContent.mCurrentFocus.mActivityRecord == win.mActivityRecord) {
- transferTouchFromWin = displayContent.mCurrentFocus;
- }
- if (!mService.mInputManager.transferTouchGesture(
- transferTouchFromWin.mInputChannel.getToken(),
- mTaskPositioner.mClientChannel.getToken())) {
- Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
- cleanUpTaskPositioner();
- return false;
- }
-
- mTaskPositioner.startDrag(resize, preserveOrientation, startX, startY);
- return true;
- }
- });
- }
-
- public void finishTaskPositioning(IWindow window) {
- if (mTaskPositioner != null && mTaskPositioner.mClientCallback == window.asBinder()) {
- finishTaskPositioning();
- }
- }
-
- void finishTaskPositioning() {
- // TaskPositioner attaches the InputEventReceiver to the animation thread. We need to
- // dispose the receiver on the same thread to avoid race conditions.
- mService.mAnimationHandler.post(() -> {
- if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning");
-
- synchronized (mService.mGlobalLock) {
- cleanUpTaskPositioner();
- mPositioningDisplay = null;
- }
- });
- }
-
- private void cleanUpTaskPositioner() {
- final TaskPositioner positioner = mTaskPositioner;
- if (positioner == null) {
- return;
- }
-
- // We need to assign task positioner to null first to indicate that we're finishing task
- // positioning.
- mTaskPositioner = null;
- positioner.unregister();
- }
-}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index f6a68d5..65bc9a2 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -745,6 +745,7 @@
if (mController.isAnimating()) {
dc.enableHighPerfTransition(true);
}
+ mController.dispatchLegacyAppTransitionPending(dc.mDisplayId);
}
/**
@@ -1618,7 +1619,7 @@
mController.mTransitionTracer.logAbortedTransition(this);
// Syncengine abort will call through to onTransactionReady()
mSyncEngine.abort(mSyncId);
- mController.dispatchLegacyAppTransitionCancelled();
+ mController.dispatchLegacyAppTransitionCancelled(mTargetDisplays);
invokeTransitionEndedListeners();
}
@@ -1766,7 +1767,19 @@
}
for (int i = 0; i < mTargets.size(); ++i) {
- final DisplayArea da = mTargets.get(i).mContainer.asDisplayArea();
+ final WindowContainer<?> wc = mTargets.get(i).mContainer;
+ final WallpaperWindowToken wp = wc.asWallpaperToken();
+ if (wp != null) {
+ // If on a rotation leash, the wallpaper token surface needs to be shown explicitly
+ // because shell only gets the leash and the wallpaper token surface is not allowed
+ // to be changed by non-transition logic until the transition is finished.
+ if (Flags.ensureWallpaperInTransitions() && wp.isVisibleRequested()
+ && wp.getFixedRotationLeash() != null) {
+ transaction.show(wp.mSurfaceControl);
+ }
+ continue;
+ }
+ final DisplayArea<?> da = wc.asDisplayArea();
if (da == null) continue;
if (da.isVisibleRequested()) {
mController.mValidateDisplayVis.remove(da);
@@ -2168,14 +2181,6 @@
&& !wallpaperIsOwnTarget(wallpaper)) {
wallpaper.setVisibleRequested(false);
}
- if (showWallpaper && Flags.ensureWallpaperInTransitions()
- && wallpaper.isVisibleRequested()
- && getLeashSurface(wallpaper, t) != wallpaper.getSurfaceControl()) {
- // If on a rotation leash, we need to explicitly show the wallpaper surface
- // because shell only gets the leash and we don't allow non-transition logic
- // to touch the surfaces until the transition is over.
- t.show(wallpaper.getSurfaceControl());
- }
}
}
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index f4ff404..1df251c 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -42,6 +42,7 @@
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import android.view.Display;
import android.view.WindowManager;
import android.window.ITransitionMetricsReporter;
import android.window.ITransitionPlayer;
@@ -326,7 +327,6 @@
mCollectingTransition.startCollecting(timeoutMs);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Start collecting in Transition: %s",
mCollectingTransition);
- dispatchLegacyAppTransitionPending();
}
void registerTransitionPlayer(@Nullable ITransitionPlayer player,
@@ -1347,31 +1347,54 @@
mLegacyListeners.remove(listener);
}
- void dispatchLegacyAppTransitionPending() {
+ private static boolean shouldDispatchLegacyListener(
+ WindowManagerInternal.AppTransitionListener listener, int displayId) {
+ // INVALID_DISPLAY means that it is a global listener.
+ return listener.mDisplayId == Display.INVALID_DISPLAY || listener.mDisplayId == displayId;
+ }
+
+ void dispatchLegacyAppTransitionPending(int displayId) {
for (int i = 0; i < mLegacyListeners.size(); ++i) {
- mLegacyListeners.get(i).onAppTransitionPendingLocked();
+ final WindowManagerInternal.AppTransitionListener listener = mLegacyListeners.get(i);
+ if (shouldDispatchLegacyListener(listener, displayId)) {
+ listener.onAppTransitionPendingLocked();
+ }
}
}
void dispatchLegacyAppTransitionStarting(TransitionInfo info, long statusBarTransitionDelay) {
+ final long now = SystemClock.uptimeMillis();
for (int i = 0; i < mLegacyListeners.size(); ++i) {
- // TODO(shell-transitions): handle (un)occlude transition.
- mLegacyListeners.get(i).onAppTransitionStartingLocked(
- SystemClock.uptimeMillis() + statusBarTransitionDelay,
- AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
+ final WindowManagerInternal.AppTransitionListener listener = mLegacyListeners.get(i);
+ for (int j = 0; j < info.getRootCount(); ++j) {
+ final int displayId = info.getRoot(j).getDisplayId();
+ if (shouldDispatchLegacyListener(listener, displayId)) {
+ listener.onAppTransitionStartingLocked(
+ now + statusBarTransitionDelay,
+ AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
+ }
+ }
}
}
void dispatchLegacyAppTransitionFinished(ActivityRecord ar) {
for (int i = 0; i < mLegacyListeners.size(); ++i) {
- mLegacyListeners.get(i).onAppTransitionFinishedLocked(ar.token);
+ final WindowManagerInternal.AppTransitionListener listener = mLegacyListeners.get(i);
+ if (shouldDispatchLegacyListener(listener, ar.getDisplayId())) {
+ listener.onAppTransitionFinishedLocked(ar.token);
+ }
}
}
- void dispatchLegacyAppTransitionCancelled() {
- for (int i = 0; i < mLegacyListeners.size(); ++i) {
- mLegacyListeners.get(i).onAppTransitionCancelledLocked(
- false /* keyguardGoingAwayCancelled */);
+ void dispatchLegacyAppTransitionCancelled(ArrayList<DisplayContent> targetDisplays) {
+ for (int i = 0; i < targetDisplays.size(); ++i) {
+ final int displayId = targetDisplays.get(i).mDisplayId;
+ for (int j = 0; j < mLegacyListeners.size(); ++j) {
+ final var listener = mLegacyListeners.get(j);
+ if (shouldDispatchLegacyListener(listener, displayId)) {
+ listener.onAppTransitionCancelledLocked(false /* keyguardGoingAwayCancelled */);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index 42b556f..6125360 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -47,7 +47,6 @@
static final boolean DEBUG_LAYOUT_REPEATS = false;
static final boolean DEBUG_WINDOW_TRACE = false;
static final boolean DEBUG_TASK_MOVEMENT = false;
- static final boolean DEBUG_TASK_POSITIONING = false;
static final boolean DEBUG_ROOT_TASK = false;
static final boolean DEBUG_DISPLAY = false;
static final boolean DEBUG_POWER = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 2ea1cf8..132e1ee 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -244,6 +244,22 @@
public static abstract class AppTransitionListener {
/**
+ * The display this listener is interested in. If it is INVALID_DISPLAY, then which display
+ * should be notified depends on the dispatcher.
+ */
+ public final int mDisplayId;
+
+ /** Let transition controller decide which display should receive the callbacks. */
+ public AppTransitionListener() {
+ this(Display.INVALID_DISPLAY);
+ }
+
+ /** It will listen the transition on the given display. */
+ public AppTransitionListener(int displayId) {
+ mDisplayId = displayId;
+ }
+
+ /**
* Called when an app transition is being setup and about to be executed.
*/
public void onAppTransitionPendingLocked() {}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index acd8b3f..f65eea0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -54,7 +54,6 @@
import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
-import static android.view.flags.Flags.sensitiveContentAppProtection;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
@@ -97,6 +96,7 @@
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
+import static android.view.flags.Flags.sensitiveContentAppProtection;
import static android.window.WindowProviderService.isWindowProviderService;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
@@ -1070,7 +1070,6 @@
/** Whether or not a layout can cause a wake up when theater mode is enabled. */
boolean mAllowTheaterModeWakeFromLayout;
- final TaskPositioningController mTaskPositioningController;
final DragDropController mDragDropController;
/** For frozen screen animations. */
@@ -1428,7 +1427,6 @@
mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
- mTaskPositioningController = new TaskPositioningController(this);
mDragDropController = new DragDropController(this, mH.getLooper());
mHighRefreshRateDenylist = HighRefreshRateDenylist.create(context.getResources());
@@ -9379,40 +9377,82 @@
}
/**
- * Move focus to the adjacent embedded activity if the adjacent activity is more recently
- * created or has a window more recently added.
+ * Returns the Activity that has the most recently created window in the adjacent activities
+ * if any.
*/
- boolean moveFocusToAdjacentEmbeddedWindow(@NonNull WindowState focusedWindow) {
- final TaskFragment taskFragment = focusedWindow.getTaskFragment();
+ @NonNull
+ ActivityRecord getMostRecentActivityInAdjacent(@NonNull ActivityRecord focusedActivity) {
+ final TaskFragment taskFragment = focusedActivity.getTaskFragment();
if (taskFragment == null) {
- // Skip if not an Activity window.
- return false;
+ // Return if activity no attached.
+ return focusedActivity;
}
if (!Flags.embeddedActivityBackNavFlag()) {
- // Skip if flag is not enabled.
- return false;
+ // Return if flag is not enabled.
+ return focusedActivity;
}
- if (!focusedWindow.mActivityRecord.isEmbedded()) {
- // Skip if the focused activity is not embedded
- return false;
+ if (!focusedActivity.isEmbedded()) {
+ // Return if the focused activity is not embedded.
+ return focusedActivity;
}
final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
final ActivityRecord adjacentTopActivity =
adjacentTaskFragment != null ? adjacentTaskFragment.topRunningActivity() : null;
if (adjacentTopActivity == null) {
- return false;
+ // Return if no adjacent activity.
+ return focusedActivity;
}
if (adjacentTopActivity.getLastWindowCreateTime()
- < focusedWindow.mActivityRecord.getLastWindowCreateTime()) {
- // Skip if the current focus activity has more recently active window.
+ < focusedActivity.getLastWindowCreateTime()) {
+ // Return if the current focus activity has more recently active window.
+ return focusedActivity;
+ }
+
+ return adjacentTopActivity;
+ }
+
+ @NonNull
+ WindowState getMostRecentUsedEmbeddedWindowForBack(@NonNull WindowState focusedWindow) {
+ final ActivityRecord focusedActivity = focusedWindow.getActivityRecord();
+ if (focusedActivity == null) {
+ // Not an Activity.
+ return focusedWindow;
+ }
+
+ final ActivityRecord mostRecentActivityInAdjacent = getMostRecentActivityInAdjacent(
+ focusedActivity);
+ if (mostRecentActivityInAdjacent == focusedActivity) {
+ // Already be the most recent window.
+ return focusedWindow;
+ }
+
+ // Looks for a candidate focused window on the adjacent Activity for the back event.
+ final WindowState candidate =
+ mostRecentActivityInAdjacent.getDisplayContent().findFocusedWindow(
+ mostRecentActivityInAdjacent);
+ return candidate != null ? candidate : focusedWindow;
+ }
+
+ /**
+ * Move focus to the adjacent embedded activity if the adjacent activity is more recently
+ * created or has a window more recently added.
+ * <p>
+ * Returns {@code true} if the focused window is changed. Otherwise, returns {@code false}.
+ */
+ boolean moveFocusToAdjacentEmbeddedWindow(@NonNull WindowState focusedWindow) {
+ final ActivityRecord activity = focusedWindow.getActivityRecord();
+ if (activity == null) {
return false;
}
- moveFocusToActivity(adjacentTopActivity);
+ final ActivityRecord mostRecentActivityInAdjacent = getMostRecentActivityInAdjacent(
+ activity);
+
+ moveFocusToActivity(mostRecentActivityInAdjacent);
return !focusedWindow.isFocused();
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 12c5073..984caf1 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1674,6 +1674,22 @@
// Otherwise if other places send wpc.getConfiguration() to client, the configuration may
// be ignored due to the seq is older.
resolvedConfig.seq = newParentConfig.seq;
+
+ if (mConfigActivityRecord != null) {
+ // Let the activity decide whether to apply the size override.
+ return;
+ }
+ final DisplayContent displayContent = mAtm.mWindowManager != null
+ ? mAtm.mWindowManager.getDefaultDisplayContentLocked()
+ : null;
+ applySizeOverrideIfNeeded(
+ displayContent,
+ mInfo,
+ newParentConfig,
+ resolvedConfig,
+ false /* optsOutEdgeToEdge */,
+ false /* hasFixedRotationTransform */,
+ false /* hasCompatDisplayInsets */);
}
void dispatchConfiguration(@NonNull Configuration config) {
diff --git a/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java b/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java
index 4211764..3559e62 100644
--- a/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java
+++ b/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java
@@ -47,16 +47,13 @@
Flags::enableDesktopWindowingWallpaperActivity, /* shouldOverrideByDevOption= */ true);
private static final String TAG = "DesktopModeFlagsUtil";
- private static final String SYSTEM_PROPERTY_OVERRIDE_KEY =
- "sys.wmshell.desktopmode.dev_toggle_override";
-
// Function called to obtain aconfig flag value.
private final Supplier<Boolean> mFlagFunction;
// Whether the flag state should be affected by developer option.
private final boolean mShouldOverrideByDevOption;
// Local cache for toggle override, which is initialized once on its first access. It needs to
- // be refreshed only on reboots as overridden state takes effect on reboots.
+ // be refreshed only on reboots as overridden state is expected to take effect on reboots.
private static ToggleOverride sCachedToggleOverride;
DesktopModeFlagsUtil(Supplier<Boolean> flagFunction, boolean shouldOverrideByDevOption) {
@@ -67,9 +64,6 @@
/**
* Determines state of flag based on the actual flag and desktop mode developer option
* overrides.
- *
- * Note: this method makes sure that a constant developer toggle overrides is read until
- * reboot.
*/
public boolean isEnabled(Context context) {
if (!Flags.showDesktopWindowingDevOption()
@@ -102,49 +96,15 @@
}
/**
- * Returns {@link ToggleOverride} from a non-persistent system property if present. Otherwise
- * initializes the system property by reading Settings.Global.
+ * Returns {@link ToggleOverride} from Settings.Global set by toggle.
*/
private ToggleOverride getToggleOverrideFromSystem(Context context) {
- // A non-persistent System Property is used to store override to ensure it remains
- // constant till reboot.
- String overrideProperty = System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, null);
- ToggleOverride overrideFromSystemProperties = convertToToggleOverride(overrideProperty);
-
- // If valid system property, return it
- if (overrideFromSystemProperties != null) {
- return overrideFromSystemProperties;
- }
-
- // Fallback when System Property is not present (just after reboot) or not valid (user
- // manually changed the value): Read from Settings.Global
int settingValue = Settings.Global.getInt(
context.getContentResolver(),
Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
OVERRIDE_UNSET.getSetting()
);
- ToggleOverride overrideFromSettingsGlobal =
- ToggleOverride.fromSetting(settingValue, OVERRIDE_UNSET);
- // Initialize System Property
- System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, String.valueOf(settingValue));
- return overrideFromSettingsGlobal;
- }
-
- /**
- * Converts {@code intString} into {@link ToggleOverride}. Return {@code null} if
- * {@code intString} does not correspond to a {@link ToggleOverride}.
- */
- private static @Nullable ToggleOverride convertToToggleOverride(
- @Nullable String intString
- ) {
- if (intString == null) return null;
- try {
- int intValue = Integer.parseInt(intString);
- return ToggleOverride.fromSetting(intValue, null);
- } catch (NumberFormatException e) {
- Log.w(TAG, "Unknown toggleOverride int " + intString);
- return null;
- }
+ return ToggleOverride.fromSetting(settingValue, OVERRIDE_UNSET);
}
/** Override state of desktop mode developer option toggle. */
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index db4b171..9e8811f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -153,6 +153,7 @@
import com.android.server.contextualsearch.ContextualSearchManagerService;
import com.android.server.coverage.CoverageService;
import com.android.server.cpu.CpuMonitorService;
+import com.android.server.crashrecovery.CrashRecoveryModule;
import com.android.server.credentials.CredentialManagerService;
import com.android.server.criticalevents.CriticalEventLog;
import com.android.server.devicepolicy.DevicePolicyManagerService;
@@ -381,8 +382,6 @@
+ "OnDevicePersonalizationSystemService$Lifecycle";
private static final String UPDATABLE_DEVICE_CONFIG_SERVICE_CLASS =
"com.android.server.deviceconfig.DeviceConfigInit$Lifecycle";
- private static final String CRASHRECOVERY_MODULE_LIFECYCLE_CLASS =
- "com.android.server.crashrecovery.CrashRecoveryModule$Lifecycle";
/*
@@ -2939,7 +2938,7 @@
if (Flags.refactorCrashrecovery()) {
t.traceBegin("StartCrashRecoveryModule");
- mSystemServiceManager.startService(CRASHRECOVERY_MODULE_LIFECYCLE_CLASS);
+ mSystemServiceManager.startService(CrashRecoveryModule.Lifecycle.class);
t.traceEnd();
} else {
if (Flags.recoverabilityDetection()) {
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 3ed6ad7..acdbbde 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -398,13 +398,13 @@
if (randomNum >= traceFrequency) {
return;
}
- // For a small percentage a traces, we collect the initialization behavior.
- boolean traceInitialization = ThreadLocalRandom.current().nextInt(10) < 1;
- int traceDelay = traceInitialization ? 0 : 1000;
- String traceTag = traceInitialization ? "camera_init" : "camera";
+ final int traceDelay = 1000;
+ final int traceDuration = 5000;
+ final String traceTag = "camera";
BackgroundThread.get().getThreadHandler().postDelayed(() -> {
try {
- mIProfcollect.trace_process(traceTag, "android.hardware.camera.provider");
+ mIProfcollect.trace_process(traceTag, "android.hardware.camera.provider",
+ traceDuration);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
}
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamAccessibilityTest.java b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamAccessibilityTest.java
index 99968d5..9da695a 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamAccessibilityTest.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamAccessibilityTest.java
@@ -19,10 +19,12 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
import android.content.Context;
import android.content.res.Resources;
@@ -44,9 +46,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.ArrayList;
-import java.util.Collections;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DreamAccessibilityTest {
@@ -73,7 +72,8 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mDreamAccessibility = new DreamAccessibility(mContext, mView);
+ Runnable mDismissCallback = () -> {};
+ mDreamAccessibility = new DreamAccessibility(mContext, mView, mDismissCallback);
when(mContext.getResources()).thenReturn(mResources);
when(mResources.getString(R.string.dream_accessibility_action_click))
@@ -84,80 +84,55 @@
*/
@Test
public void testConfigureAccessibilityActions() {
- when(mAccessibilityNodeInfo.getActionList()).thenReturn(new ArrayList<>());
+ when(mView.getAccessibilityDelegate()).thenReturn(null);
- mDreamAccessibility.updateAccessibilityConfiguration(false);
+ mDreamAccessibility.updateAccessibilityConfiguration();
verify(mView).setAccessibilityDelegate(mAccessibilityDelegateArgumentCaptor.capture());
- View.AccessibilityDelegate capturedDelegate =
- mAccessibilityDelegateArgumentCaptor.getValue();
+ View.AccessibilityDelegate capturedDelegate = mAccessibilityDelegateArgumentCaptor
+ .getValue();
capturedDelegate.onInitializeAccessibilityNodeInfo(mView, mAccessibilityNodeInfo);
verify(mAccessibilityNodeInfo).addAction(argThat(action ->
- action.getId() == AccessibilityNodeInfo.ACTION_CLICK
+ action.getId() == AccessibilityNodeInfo.ACTION_DISMISS
&& TextUtils.equals(action.getLabel(), CUSTOM_ACTION)));
}
/**
- * Test to verify the configuration of accessibility actions within a view delegate,
- * specifically checking the removal of an existing click action and addition
- * of a new custom action.
+ * Test to verify no accessibility configuration is added if one exist.
*/
@Test
- public void testConfigureAccessibilityActions_RemovesExistingClickAction() {
- AccessibilityNodeInfo.AccessibilityAction existingAction =
- new AccessibilityNodeInfo.AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK,
- EXISTING_ACTION);
- when(mAccessibilityNodeInfo.getActionList())
- .thenReturn(Collections.singletonList(existingAction));
+ public void testNotAddingDuplicateAccessibilityConfiguration() {
+ View.AccessibilityDelegate existingDelegate = mock(View.AccessibilityDelegate.class);
+ when(mView.getAccessibilityDelegate()).thenReturn(existingDelegate);
- mDreamAccessibility.updateAccessibilityConfiguration(false);
+ mDreamAccessibility.updateAccessibilityConfiguration();
- verify(mView).setAccessibilityDelegate(mAccessibilityDelegateArgumentCaptor.capture());
- View.AccessibilityDelegate capturedDelegate =
- mAccessibilityDelegateArgumentCaptor.getValue();
-
- capturedDelegate.onInitializeAccessibilityNodeInfo(mView, mAccessibilityNodeInfo);
-
- verify(mAccessibilityNodeInfo).removeAction(existingAction);
- verify(mAccessibilityNodeInfo).addAction(argThat(action ->
- action.getId() == AccessibilityNodeInfo.ACTION_CLICK
- && TextUtils.equals(action.getLabel(), CUSTOM_ACTION)));
-
- }
-
- /**
- * Test to verify the removal of a custom accessibility action within a view delegate.
- */
- @Test
- public void testRemoveCustomAccessibilityAction() {
-
- AccessibilityNodeInfo.AccessibilityAction existingAction =
- new AccessibilityNodeInfo.AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK,
- EXISTING_ACTION);
- when(mAccessibilityNodeInfo.getActionList())
- .thenReturn(Collections.singletonList(existingAction));
-
- mDreamAccessibility.updateAccessibilityConfiguration(false);
- verify(mView).setAccessibilityDelegate(mAccessibilityDelegateArgumentCaptor.capture());
- View.AccessibilityDelegate capturedDelegate =
- mAccessibilityDelegateArgumentCaptor.getValue();
- when(mView.getAccessibilityDelegate()).thenReturn(capturedDelegate);
- clearInvocations(mView);
-
- mDreamAccessibility.updateAccessibilityConfiguration(true);
- verify(mView).setAccessibilityDelegate(null);
- }
-
- /**
- * Test to verify the removal of custom accessibility action is not called if delegate is not
- * set by the dreamService.
- */
- @Test
- public void testRemoveCustomAccessibility_DoesNotRemoveDelegateNotSetByDreamAccessibility() {
- mDreamAccessibility.updateAccessibilityConfiguration(true);
verify(mView, never()).setAccessibilityDelegate(any());
}
+
+ /**
+ * Test to verify dismiss callback is called
+ */
+ @Test
+ public void testPerformAccessibilityAction() {
+ Runnable mockDismissCallback = mock(Runnable.class);
+ DreamAccessibility dreamAccessibility = new DreamAccessibility(mContext,
+ mView, mockDismissCallback);
+
+ dreamAccessibility.updateAccessibilityConfiguration();
+
+ verify(mView).setAccessibilityDelegate(mAccessibilityDelegateArgumentCaptor.capture());
+ View.AccessibilityDelegate capturedDelegate = mAccessibilityDelegateArgumentCaptor
+ .getValue();
+
+ boolean result = capturedDelegate.performAccessibilityAction(mView,
+ AccessibilityNodeInfo.ACTION_DISMISS, null);
+
+ assertTrue(result);
+ verify(mockDismissCallback).run();
+ }
+
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 0f38532..a4222ff 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -1518,7 +1518,8 @@
mBiometricService.onStart();
when(mTrustManager.isInSignificantPlace()).thenReturn(false);
- when(mBiometricService.mSettingObserver.getMandatoryBiometricsEnabledForUser(anyInt()))
+ when(mBiometricService.mSettingObserver
+ .getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(anyInt()))
.thenReturn(true);
setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
@@ -1540,7 +1541,8 @@
mBiometricService.onStart();
when(mTrustManager.isInSignificantPlace()).thenReturn(false);
- when(mBiometricService.mSettingObserver.getMandatoryBiometricsEnabledForUser(anyInt()))
+ when(mBiometricService.mSettingObserver
+ .getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(anyInt()))
.thenReturn(true);
setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
@@ -1564,7 +1566,8 @@
mBiometricService.onStart();
when(mTrustManager.isInSignificantPlace()).thenReturn(false);
- when(mBiometricService.mSettingObserver.getMandatoryBiometricsEnabledForUser(anyInt()))
+ when(mBiometricService.mSettingObserver
+ .getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(anyInt()))
.thenReturn(true);
setupAuthForOnly(TYPE_CREDENTIAL, Authenticators.DEVICE_CREDENTIAL);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
index b831ef5..240da9f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
@@ -90,7 +90,8 @@
when(mDevicePolicyManager.getKeyguardDisabledFeatures(any(), anyInt()))
.thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
when(mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true);
- when(mSettingObserver.getMandatoryBiometricsEnabledForUser(anyInt())).thenReturn(true);
+ when(mSettingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(
+ anyInt())).thenReturn(true);
when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true);
when(mFaceAuthenticator.getLockoutModeForUser(anyInt()))
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index 316b5fa..689b241 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -39,6 +39,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -364,6 +365,39 @@
@EnableFlags(android.companion.virtualdevice.flags
.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
+ public void testReuseProjection_keyguardNotLocked_startConsentDialog()
+ throws NameNotFoundException {
+ MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+ projection.start(mIMediaProjectionCallback);
+
+ doNothing().when(mContext).startActivityAsUser(any(), any());
+ doReturn(false).when(mKeyguardManager).isKeyguardLocked();
+
+ MediaProjectionManagerService.BinderService mediaProjectionBinderService =
+ mService.new BinderService(mContext);
+ mediaProjectionBinderService.requestConsentForInvalidProjection(projection);
+
+ verify(mContext).startActivityAsUser(any(), any());
+ }
+
+ @EnableFlags(android.companion.virtualdevice.flags
+ .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
+ @Test
+ public void testReuseProjection_keyguardLocked_noConsentDialog() throws NameNotFoundException {
+ MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+ projection.start(mIMediaProjectionCallback);
+
+ doReturn(true).when(mKeyguardManager).isKeyguardLocked();
+ MediaProjectionManagerService.BinderService mediaProjectionBinderService =
+ mService.new BinderService(mContext);
+ mediaProjectionBinderService.requestConsentForInvalidProjection(projection);
+
+ verify(mContext, never()).startActivityAsUser(any(), any());
+ }
+
+ @EnableFlags(android.companion.virtualdevice.flags
+ .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
+ @Test
public void testKeyguardLocked_stopsActiveProjection() throws Exception {
MediaProjectionManagerService service =
new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 9dac23f..d7004e7 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -1746,10 +1746,6 @@
assertTrue("Tested duration=" + duration4, duration4 < 2000);
// Effect5: played normally after effect4, which may or may not have played.
-
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId5));
- verifyCallbacksTriggered(vibrationId5, Vibration.Status.FINISHED);
-
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
fakeVibrator.getEffectSegments(vibrationId5));
}
diff --git a/services/tests/wmtests/res/xml/bookmarks.xml b/services/tests/wmtests/res/xml/bookmarks.xml
index 88419e9..1549b2d 100644
--- a/services/tests/wmtests/res/xml/bookmarks.xml
+++ b/services/tests/wmtests/res/xml/bookmarks.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2024 The Android Open Source Project
+<!-- Copyright 2024 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.
@@ -14,6 +14,8 @@
limitations under the License.
-->
<bookmarks>
+ <!-- the key combinations for the following shortcuts must be in sync
+ with the key combinations sent by the test in ModifierShortcutTests.java -->
<bookmark
role="android.app.role.BROWSER"
shortcut="b" />
@@ -38,4 +40,37 @@
<bookmark
category="android.intent.category.APP_CALCULATOR"
shortcut="u" />
+
+ <!-- The following shortcuts will not be invoked by tests but are here to
+ provide test coverage of parsing the different types of shortcut. -->
+ <bookmark
+ package="com.test"
+ class="com.test.BookmarkTest"
+ shortcut="a" />
+ <bookmark
+ package="com.test2"
+ class="com.test.BookmarkTest"
+ shortcut="d" />
+
+ <bookmark
+ role="android.app.role.BROWSER"
+ shortcut="b"
+ shift="true" />
+ <bookmark
+ category="android.intent.category.APP_CONTACTS"
+ shortcut="c"
+ shift="true" />
+ <bookmark
+ package="com.test"
+ class="com.test.BookmarkTest"
+ shortcut="a"
+ shift="true" />
+
+ <!-- It's intended that this package/class will NOT resolve so we test the resolution
+ failure case. -->
+ <bookmark
+ package="com.test3"
+ class="com.test.BookmarkTest"
+ shortcut="f" />
+
</bookmarks>
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
index 8c375d4..5533ff9 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright 2024 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.
@@ -19,15 +19,22 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
+import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.os.Handler;
@@ -58,27 +65,56 @@
private Handler mHandler;
private Context mContext;
private Resources mResources;
+ private PackageManager mPackageManager;
@Before
public void setUp() {
mHandler = new Handler(Looper.getMainLooper());
mContext = spy(getInstrumentation().getTargetContext());
mResources = spy(mContext.getResources());
+ mPackageManager = spy(mContext.getPackageManager());
XmlResourceParser testBookmarks = mResources.getXml(
com.android.frameworks.wmtests.R.xml.bookmarks);
when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mResources.getXml(R.xml.bookmarks)).thenReturn(testBookmarks);
+ try {
+ // Keep packageName / className in sync with
+ // services/tests/wmtests/res/xml/bookmarks.xml
+ ActivityInfo testActivityInfo = new ActivityInfo();
+ testActivityInfo.applicationInfo = new ApplicationInfo();
+ testActivityInfo.packageName =
+ testActivityInfo.applicationInfo.packageName = "com.test";
+
+ doReturn(testActivityInfo).when(mPackageManager).getActivityInfo(
+ eq(new ComponentName("com.test", "com.test.BookmarkTest")), anyInt());
+ doThrow(new PackageManager.NameNotFoundException("com.test3")).when(mPackageManager)
+ .getActivityInfo(eq(new ComponentName("com.test3", "com.test.BookmarkTest")),
+ anyInt());
+ } catch (PackageManager.NameNotFoundException ignored) { }
+ doReturn(new String[] { "com.test" }).when(mPackageManager)
+ .canonicalToCurrentPackageNames(aryEq(new String[] { "com.test2" }));
+
mModifierShortcutManager = new ModifierShortcutManager(mContext, mHandler);
}
@Test
public void test_getApplicationLaunchKeyboardShortcuts() {
+ // Expected values here determined by the number of shortcuts defined in
+ // services/tests/wmtests/res/xml/bookmarks.xml
+
+ // Total valid shortcuts.
KeyboardShortcutGroup group =
mModifierShortcutManager.getApplicationLaunchKeyboardShortcuts(-1);
- assertEquals(8, group.getItems().size());
+ assertEquals(13, group.getItems().size());
+
+ // Total valid shift shortcuts.
+ assertEquals(3, group.getItems().stream()
+ .filter(s -> s.getModifiers() == (KeyEvent.META_SHIFT_ON | KeyEvent.META_META_ON))
+ .count());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java
new file mode 100644
index 0000000..ddd6d56
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatAspectRatioOverridesTest.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2024 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.server.wm;
+
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.assertEquals;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+/**
+ * Test class for {@link AppCompatAspectRatioOverrides}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:AppCompatAspectRatioOverridesTest
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class AppCompatAspectRatioOverridesTest extends WindowTestsBase {
+
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+ @Test
+ public void testShouldApplyUserFullscreenOverride_trueProperty_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.prop().enable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE);
+ robot.conf().enableUserAppAspectRatioFullscreen(/* enabled */ false);
+
+ robot.activity().createActivityWithComponent();
+
+ robot.checkShouldApplyUserFullscreenOverride(/* expected */ false);
+ });
+ }
+
+ @Test
+ public void testShouldApplyUserFullscreenOverride_falseFullscreenProperty_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioFullscreen(/* enabled */ true);
+ robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE);
+ robot.activity().createActivityWithComponent();
+ robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_FULLSCREEN);
+
+ robot.checkShouldApplyUserFullscreenOverride(/* expected */ false);
+ });
+ }
+
+ @Test
+ public void testShouldApplyUserFullscreenOverride_falseSettingsProperty_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+ robot.activity().createActivityWithComponent();
+ robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_FULLSCREEN);
+ robot.checkShouldApplyUserFullscreenOverride(/* expected */ false);
+ });
+ }
+
+
+ @Test
+ public void testShouldApplyUserFullscreenOverride_returnsTrue() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioFullscreen(/* enabled */ true);
+ robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+ robot.activity().createActivityWithComponent();
+ robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_FULLSCREEN);
+
+ robot.checkShouldApplyUserFullscreenOverride(/* expected */ true);
+ });
+ }
+
+ @Test
+ public void testShouldEnableUserAspectRatioSettings_falseProperty_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+ robot.activity().createActivityWithComponent();
+ robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+ robot.checkShouldEnableUserAspectRatioSettings(/* expected */ false);
+ });
+ }
+
+ @Test
+ public void testShouldEnableUserAspectRatioSettings_trueProperty_returnsTrue() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+ robot.prop().enable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+ robot.activity().createActivityWithComponent();
+ robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+ robot.checkShouldEnableUserAspectRatioSettings(/* expected */ true);
+ });
+ }
+
+ @Test
+ public void testShouldEnableUserAspectRatioSettings_ignoreOrientation_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ false);
+ robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+ robot.prop().enable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+ robot.activity().createActivityWithComponent();
+ robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+ robot.checkShouldEnableUserAspectRatioSettings(/* expected */ false);
+ });
+ }
+
+ @Test
+ public void testShouldApplyUserMinAspectRatioOverride_falseProperty_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+ robot.activity().createActivityWithComponent();
+ robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+ robot.checkShouldEnableUserAspectRatioSettings(/* expected */ false);
+ });
+ }
+
+ @Test
+ public void testShouldApplyUserMinAspectRatioOverride_trueProperty_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ false);
+ robot.prop().enable(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
+ robot.activity().createActivityWithComponent();
+
+ robot.checkShouldEnableUserAspectRatioSettings(/* enabled */ false);
+ });
+ }
+
+ @Test
+ public void testShouldApplyUserMinAspectRatioOverride_disabledIgnoreOrientationRequest() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.activity().setIgnoreOrientationRequest(/* enabled */ false);
+ robot.activity().createActivityWithComponent();
+ robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+ robot.checkShouldApplyUserMinAspectRatioOverride(/* expected */ false);
+ });
+ }
+
+ @Test
+ public void testShouldApplyUserMinAspectRatioOverride_returnsTrue() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+ robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+ robot.activity().createActivityWithComponent();
+ robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+ robot.checkShouldApplyUserMinAspectRatioOverride(/* expected */ true);
+ });
+ }
+
+ @Test
+ public void testShouldApplyUserMinAspectRatioOverride_ignoreOrientation_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.conf().enableUserAppAspectRatioSettings(/* enabled */ false);
+ robot.activity().setIgnoreOrientationRequest(/* enabled */ true);
+ robot.activity().createActivityWithComponent();
+ robot.activity().setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+
+ robot.checkShouldApplyUserMinAspectRatioOverride(/* expected */ false);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+ public void testShouldOverrideMinAspectRatio_overrideEnabled_returnsTrue() {
+ runTestScenario((robot)-> {
+ robot.activity().createActivityWithComponent();
+
+ robot.checkShouldOverrideMinAspectRatio(/* expected */ true);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+ public void testShouldOverrideMinAspectRatio_propertyTrue_overrideEnabled_returnsTrue() {
+ runTestScenario((robot)-> {
+ robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+ robot.activity().createActivityWithComponent();
+
+ robot.checkShouldOverrideMinAspectRatio(/* expected */ true);
+ });
+ }
+
+ @Test
+ @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+ public void testShouldOverrideMinAspectRatio_propertyTrue_overrideDisabled_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+ robot.activity().createActivityWithComponent();
+
+ robot.checkShouldOverrideMinAspectRatio(/* expected */ false);
+ });
+ }
+
+ @Test
+ @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+ public void testShouldOverrideMinAspectRatio_overrideDisabled_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.activity().createActivityWithComponent();
+
+ robot.checkShouldOverrideMinAspectRatio(/* expected */ false);
+ });
+ }
+
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+ public void testshouldOverrideMinAspectRatio_propertyFalse_overrideEnabled_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+ robot.activity().createActivityWithComponent();
+
+ robot.checkShouldOverrideMinAspectRatio(/* expected */ false);
+ });
+ }
+
+ @Test
+ @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
+ public void testshouldOverrideMinAspectRatio_propertyFalse_noOverride_returnsFalse() {
+ runTestScenario((robot)-> {
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+ robot.activity().createActivityWithComponent();
+
+ robot.checkShouldOverrideMinAspectRatio(/* expected */ false);
+ });
+ }
+
+ /**
+ * Runs a test scenario providing a Robot.
+ */
+ void runTestScenario(@NonNull Consumer<AspectRatioOverridesRobotTest> consumer) {
+ spyOn(mWm.mAppCompatConfiguration);
+ final AspectRatioOverridesRobotTest robot =
+ new AspectRatioOverridesRobotTest(mWm, mAtm, mSupervisor);
+ consumer.accept(robot);
+ }
+
+ private static class AspectRatioOverridesRobotTest extends AppCompatRobotBase {
+
+ AspectRatioOverridesRobotTest(@NonNull WindowManagerService wm,
+ @NonNull ActivityTaskManagerService atm,
+ @NonNull ActivityTaskSupervisor supervisor) {
+ super(wm, atm, supervisor);
+ }
+
+ void checkShouldApplyUserFullscreenOverride(boolean expected) {
+ assertEquals(expected, getTopActivityAppCompatAspectRatioOverrides()
+ .shouldApplyUserFullscreenOverride());
+ }
+
+ void checkShouldEnableUserAspectRatioSettings(boolean expected) {
+ assertEquals(expected, getTopActivityAppCompatAspectRatioOverrides()
+ .shouldEnableUserAspectRatioSettings());
+ }
+
+ void checkShouldApplyUserMinAspectRatioOverride(boolean expected) {
+ assertEquals(expected, getTopActivityAppCompatAspectRatioOverrides()
+ .shouldApplyUserMinAspectRatioOverride());
+ }
+
+ void checkShouldOverrideMinAspectRatio(boolean expected) {
+ assertEquals(expected, getTopActivityAppCompatAspectRatioOverrides()
+ .shouldOverrideMinAspectRatio());
+ }
+
+ @NonNull
+ private AppCompatAspectRatioOverrides getTopActivityAppCompatAspectRatioOverrides() {
+ return activity().top().mAppCompatController.getAppCompatAspectRatioOverrides();
+ }
+ }
+
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
index d8c7fb3..de99f54 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
@@ -25,6 +25,7 @@
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;
@@ -36,6 +37,7 @@
import androidx.annotation.NonNull;
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
import org.junit.Rule;
@@ -286,6 +288,88 @@
});
}
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+ public void shouldOverrideMinAspectRatioForCamera_overrideEnabled_returnsTrue() {
+ runTestScenario((robot) -> {
+ robot.activity().createActivityWithComponent();
+ robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
+
+ robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ true);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+ public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideEnabled_returnsTrue() {
+ runTestScenario((robot) -> {
+ robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+ robot.activity().createActivityWithComponent();
+ robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
+
+ robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ true);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+ public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideEnabled_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+ robot.activity().createActivityWithComponent();
+ robot.activity().activateCameraInPolicy(/* isCameraActive */ false);
+
+ robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+ });
+ }
+
+ @Test
+ @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+ public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideDisabled_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+ robot.activity().createActivityWithComponent();
+ robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
+
+ robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+ });
+ }
+
+ @Test
+ @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+ public void shouldOverrideMinAspectRatioForCamera_overrideDisabled_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.activity().createActivityWithComponent();
+ robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
+
+ robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+ public void shouldOverrideMinAspectRatioForCamera_propertyFalse_overrideEnabled_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+ robot.activity().createActivityWithComponent();
+
+ robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+ });
+ }
+
+ @Test
+ @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+ public void shouldOverrideMinAspectRatioForCamera_propertyFalse_noOverride_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.prop().disable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+ robot.activity().createActivityWithComponent();
+ robot.activity().activateCameraInPolicy(/* isCameraActive */ true);
+
+ robot.checkShouldOverrideMinAspectRatioForCamera(/* expected */ false);
+ });
+ }
+
/**
* Runs a test scenario providing a Robot.
*/
@@ -323,6 +407,11 @@
.shouldApplyFreeformTreatmentForCameraCompat(), expected);
}
+ void checkShouldOverrideMinAspectRatioForCamera(boolean expected) {
+ Assert.assertEquals(getAppCompatCameraOverrides()
+ .shouldOverrideMinAspectRatioForCamera(), expected);
+ }
+
void checkIsCameraActive(boolean active) {
Assert.assertEquals(getAppCompatCameraOverrides().isCameraActive(), active);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java
index d568eec..361177f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatComponentPropRobot.java
@@ -32,27 +32,27 @@
*/
class AppCompatComponentPropRobot {
@NonNull
- private final WindowManagerService mWm;
+ private final PackageManager mPackageManager;
AppCompatComponentPropRobot(@NonNull WindowManagerService wm) {
- mWm = wm;
+ mPackageManager = wm.mContext.getPackageManager();
+ spyOn(mPackageManager);
}
void enable(@NonNull String propertyName) {
- setPropertyValue(propertyName, /* enabled */ true);
+ setPropertyValue(propertyName, "", "", /* enabled */ true);
}
void disable(@NonNull String propertyName) {
- setPropertyValue(propertyName, /* enabled */ false);
+ setPropertyValue(propertyName, "", "", /* enabled */ false);
}
- private void setPropertyValue(@NonNull String propertyName, boolean enabled) {
+ private void setPropertyValue(@NonNull String propertyName, @NonNull String packageName,
+ @NonNull String className, boolean enabled) {
final PackageManager.Property property = new PackageManager.Property(propertyName,
- /* value */ enabled, /* packageName */ "", /* className */ "");
- final PackageManager pm = mWm.mContext.getPackageManager();
- spyOn(pm);
+ /* value */ enabled, packageName, className);
try {
- doReturn(property).when(pm).getProperty(eq(propertyName), anyString());
+ doReturn(property).when(mPackageManager).getProperty(eq(propertyName), anyString());
} catch (PackageManager.NameNotFoundException e) {
fail(e.getLocalizedMessage());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
index cb3cf6b..0a1b16b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
@@ -66,6 +66,4 @@
doReturn(enabled).when(mAppCompatConfiguration)
.isCameraCompatSplitScreenAspectRatioEnabled();
}
-
-
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 63c14b9..afa22bc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -67,6 +67,7 @@
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedCallbackInfo;
import android.window.OnBackInvokedDispatcher;
+import android.window.TaskFragmentOrganizer;
import android.window.TaskSnapshot;
import android.window.WindowOnBackInvokedDispatcher;
@@ -670,25 +671,29 @@
}
@Test
- public void testAdjacentFocusInActivityEmbedding() {
+ public void testBackOnMostRecentWindowInActivityEmbedding() {
mSetFlagsRule.enableFlags(Flags.FLAG_EMBEDDED_ACTIVITY_BACK_NAV_FLAG);
final Task task = createTask(mDefaultDisplay);
- final TaskFragment primaryTf = createTaskFragmentWithActivity(task);
- final TaskFragment secondaryTf = createTaskFragmentWithActivity(task);
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final TaskFragment primaryTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final TaskFragment secondaryTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
final ActivityRecord primaryActivity = primaryTf.getTopMostActivity();
final ActivityRecord secondaryActivity = secondaryTf.getTopMostActivity();
primaryTf.setAdjacentTaskFragment(secondaryTf);
secondaryTf.setAdjacentTaskFragment(primaryTf);
- final WindowState windowState = mock(WindowState.class);
- windowState.mActivityRecord = primaryActivity;
- doReturn(windowState).when(mWm).getFocusedWindowLocked();
- doReturn(primaryTf).when(windowState).getTaskFragment();
+ final WindowState primaryWindow = mock(WindowState.class);
+ final WindowState secondaryWindow = mock(WindowState.class);
+ doReturn(primaryActivity).when(primaryWindow).getActivityRecord();
+ doReturn(secondaryActivity).when(secondaryWindow).getActivityRecord();
doReturn(1L).when(primaryActivity).getLastWindowCreateTime();
doReturn(2L).when(secondaryActivity).getLastWindowCreateTime();
+ doReturn(mDisplayContent).when(primaryActivity).getDisplayContent();
+ doReturn(secondaryWindow).when(mDisplayContent).findFocusedWindow(eq(secondaryActivity));
- startBackNavigation();
- verify(mWm).moveFocusToActivity(eq(secondaryActivity));
+ final WindowState mostRecentUsedWindow =
+ mWm.getMostRecentUsedEmbeddedWindowForBack(primaryWindow);
+ assertThat(mostRecentUsedWindow).isEqualTo(secondaryWindow);
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index d318f00..44c7057b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -19,19 +19,12 @@
import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
-import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO;
-import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
-import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
-import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -119,8 +112,6 @@
mController = new LetterboxUiController(mWm, mActivity);
}
-
-
@Test
public void testGetCropBoundsIfNeeded_handleCropForTransparentActivityBasedOnOpaqueBounds() {
final InsetsSource taskbar = new InsetsSource(/*id=*/ 0,
@@ -320,164 +311,6 @@
return mainWindow;
}
- // shouldApplyUser...Override
- @Test
- public void testShouldApplyUserFullscreenOverride_trueProperty_returnsFalse() throws Exception {
- mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE,
- /* value */ true);
-
- doReturn(false).when(mAppCompatConfiguration).isUserAppAspectRatioFullscreenEnabled();
- mActivity = setUpActivityWithComponent();
-
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldApplyUserFullscreenOverride());
- }
-
- @Test
- public void testShouldApplyUserFullscreenOverride_falseFullscreenProperty_returnsFalse()
- throws Exception {
- prepareActivityThatShouldApplyUserFullscreenOverride();
- mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE,
- /* value */ false);
-
- mActivity = setUpActivityWithComponent();
-
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldApplyUserFullscreenOverride());
- }
-
- @Test
- public void testShouldApplyUserFullscreenOverride_falseSettingsProperty_returnsFalse()
- throws Exception {
- prepareActivityThatShouldApplyUserFullscreenOverride();
- mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ false);
-
- mActivity = setUpActivityWithComponent();
-
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldApplyUserFullscreenOverride());
- }
-
- @Test
- public void testShouldApplyUserFullscreenOverride_returnsTrue() {
- prepareActivityThatShouldApplyUserFullscreenOverride();
-
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldApplyUserFullscreenOverride());
- }
-
- @Test
- public void testShouldEnableUserAspectRatioSettings_falseProperty_returnsFalse()
- throws Exception {
- prepareActivityThatShouldApplyUserMinAspectRatioOverride();
- mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ false);
-
- mActivity = setUpActivityWithComponent();
- mController = new LetterboxUiController(mWm, mActivity);
-
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldEnableUserAspectRatioSettings());
- }
-
- @Test
- public void testShouldEnableUserAspectRatioSettings_trueProperty_returnsTrue()
- throws Exception {
-
- mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
- mActivity = setUpActivityWithComponent();
- prepareActivityThatShouldApplyUserMinAspectRatioOverride();
-
- mController = new LetterboxUiController(mWm, mActivity);
-
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldEnableUserAspectRatioSettings());
- }
-
- @Test
- public void testShouldEnableUserAspectRatioSettings_noIgnoreOrientation_returnsFalse()
- throws Exception {
- prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ false);
- mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
-
- mController = new LetterboxUiController(mWm, mActivity);
-
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldEnableUserAspectRatioSettings());
- }
-
- @Test
- public void testShouldApplyUserMinAspectRatioOverride_falseProperty_returnsFalse()
- throws Exception {
- prepareActivityThatShouldApplyUserMinAspectRatioOverride();
- mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ false);
-
- mActivity = setUpActivityWithComponent();
-
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldEnableUserAspectRatioSettings());
- }
-
- @Test
- public void testShouldApplyUserMinAspectRatioOverride_trueProperty_returnsFalse()
- throws Exception {
- doReturn(false).when(mAppCompatConfiguration).isUserAppAspectRatioSettingsEnabled();
- mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
-
- mActivity = setUpActivityWithComponent();
-
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldApplyUserMinAspectRatioOverride());
- }
-
- @Test
- public void testShouldApplyUserMinAspectRatioOverride_disabledIgnoreOrientationRequest() {
- prepareActivityThatShouldApplyUserMinAspectRatioOverride();
- mDisplayContent.setIgnoreOrientationRequest(false);
-
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldApplyUserMinAspectRatioOverride());
- }
-
- @Test
- public void testShouldApplyUserMinAspectRatioOverride_returnsTrue() {
- prepareActivityThatShouldApplyUserMinAspectRatioOverride();
-
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldApplyUserMinAspectRatioOverride());
- }
-
- @Test
- public void testShouldApplyUserMinAspectRatioOverride_noIgnoreOrientation_returnsFalse() {
- prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ false);
-
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldApplyUserMinAspectRatioOverride());
- }
-
- private void prepareActivityForShouldApplyUserMinAspectRatioOverride(
- boolean orientationRequest) {
- spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
- doReturn(orientationRequest).when(
- mAppCompatConfiguration).isUserAppAspectRatioSettingsEnabled();
- mDisplayContent.setIgnoreOrientationRequest(true);
- doReturn(USER_MIN_ASPECT_RATIO_3_2)
- .when(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides())
- .getUserMinAspectRatioOverrideCode();
- }
-
- private void prepareActivityThatShouldApplyUserMinAspectRatioOverride() {
- prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ true);
- }
-
- private void prepareActivityThatShouldApplyUserFullscreenOverride() {
- spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
- doReturn(true).when(mAppCompatConfiguration).isUserAppAspectRatioFullscreenEnabled();
- mDisplayContent.setIgnoreOrientationRequest(true);
- doReturn(USER_MIN_ASPECT_RATIO_FULLSCREEN)
- .when(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides())
- .getUserMinAspectRatioOverrideCode();
- }
-
// shouldUseDisplayLandscapeNaturalOrientation
@Test
@@ -595,156 +428,6 @@
}
@Test
- @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
- public void testshouldOverrideMinAspectRatio_overrideEnabled_returnsTrue() {
- mActivity = setUpActivityWithComponent();
-
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldOverrideMinAspectRatio());
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
- public void testshouldOverrideMinAspectRatio_propertyTrue_overrideEnabled_returnsTrue()
- throws Exception {
- mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
- mActivity = setUpActivityWithComponent();
-
- assertTrue(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldOverrideMinAspectRatio());
- }
-
- @Test
- @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
- public void testshouldOverrideMinAspectRatio_propertyTrue_overrideDisabled_returnsFalse()
- throws Exception {
- mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
- mActivity = setUpActivityWithComponent();
-
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldOverrideMinAspectRatio());
- }
-
- @Test
- @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
- public void testshouldOverrideMinAspectRatio_overrideDisabled_returnsFalse() {
- mActivity = setUpActivityWithComponent();
-
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldOverrideMinAspectRatio());
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
- public void testshouldOverrideMinAspectRatio_propertyFalse_overrideEnabled_returnsFalse()
- throws Exception {
- mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
-
- mActivity = setUpActivityWithComponent();
-
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldOverrideMinAspectRatio());
- }
-
- @Test
- @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO})
- public void testshouldOverrideMinAspectRatio_propertyFalse_noOverride_returnsFalse()
- throws Exception {
- mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
- mActivity = setUpActivityWithComponent();
-
- assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides()
- .shouldOverrideMinAspectRatio());
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
- public void shouldOverrideMinAspectRatioForCamera_overrideEnabled_returnsTrue() {
- mActivity = setUpActivityWithComponent();
- doReturn(true).when(mActivity.mAppCompatController
- .getAppCompatCameraOverrides()).isCameraActive();
-
- assertTrue(mActivity.mAppCompatController.getAppCompatCameraOverrides()
- .shouldOverrideMinAspectRatioForCamera());
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
- public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideEnabled_returnsTrue()
- throws Exception {
- mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
- mActivity = setUpActivityWithComponent();
- doReturn(true).when(mActivity.mAppCompatController
- .getAppCompatCameraOverrides()).isCameraActive();
-
- assertTrue(mActivity.mAppCompatController.getAppCompatCameraOverrides()
- .shouldOverrideMinAspectRatioForCamera());
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
- public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideEnabled_returnsFalse()
- throws Exception {
- mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
- mActivity = setUpActivityWithComponent();
- doReturn(false).when(mActivity.mAppCompatController
- .getAppCompatCameraOverrides()).isCameraActive();
-
- assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
- .shouldOverrideMinAspectRatioForCamera());
- }
-
- @Test
- @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
- public void shouldOverrideMinAspectRatioForCamera_propertyTrue_overrideDisabled_returnsFalse()
- throws Exception {
- mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ true);
- mActivity = setUpActivityWithComponent();
- doReturn(true).when(mActivity.mAppCompatController
- .getAppCompatCameraOverrides()).isCameraActive();
-
- assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
- .shouldOverrideMinAspectRatioForCamera());
- }
-
- @Test
- @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
- public void shouldOverrideMinAspectRatioForCamera_overrideDisabled_returnsFalse() {
- mActivity = setUpActivityWithComponent();
- doReturn(true).when(mActivity.mAppCompatController
- .getAppCompatCameraOverrides()).isCameraActive();
-
- assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
- .shouldOverrideMinAspectRatioForCamera());
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
- public void shouldOverrideMinAspectRatioForCamera_propertyFalse_overrideEnabled_returnsFalse()
- throws Exception {
- mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
- mActivity = setUpActivityWithComponent();
-
- assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
- .shouldOverrideMinAspectRatioForCamera());
- }
-
- @Test
- @DisableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
- public void shouldOverrideMinAspectRatioForCamera_propertyFalse_noOverride_returnsFalse()
- throws Exception {
- mockThatProperty(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE, /* value */ false);
-
- mActivity = setUpActivityWithComponent();
-
- doReturn(true).when(mActivity.mAppCompatController
- .getAppCompatCameraOverrides()).isCameraActive();
-
- assertFalse(mActivity.mAppCompatController.getAppCompatCameraOverrides()
- .shouldOverrideMinAspectRatioForCamera());
- }
-
- @Test
@EnableCompatChanges({FORCE_RESIZE_APP})
public void testshouldOverrideForceResizeApp_overrideEnabled_returnsTrue() {
mController = new LetterboxUiController(mWm, mActivity);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
deleted file mode 100644
index d535677..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
+++ /dev/null
@@ -1,526 +0,0 @@
-/*
- * Copyright (C) 2016 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.server.wm;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.internal.policy.TaskResizingAlgorithm.MIN_ASPECT;
-import static com.android.server.wm.WindowManagerService.dipToPixel;
-import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
-import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.util.DisplayMetrics;
-import android.util.Log;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link TaskPositioner} class.
- *
- * Build/Install/Run:
- * atest WmTests:TaskPositionerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class TaskPositionerTests extends WindowTestsBase {
-
- private static final boolean DEBUGGING = false;
- private static final String TAG = "TaskPositionerTest";
-
- private static final int MOUSE_DELTA_X = 5;
- private static final int MOUSE_DELTA_Y = 5;
-
- private int mMinVisibleWidth;
- private int mMinVisibleHeight;
- private TaskPositioner mPositioner;
-
- @Before
- public void setUp() {
- TaskPositioner.setFactory(null);
-
- final DisplayMetrics dm = mDisplayContent.getDisplayMetrics();
-
- // This should be the same calculation as the TaskPositioner uses.
- mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, dm);
- mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
- removeGlobalMinSizeRestriction();
-
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setCreateTask(true)
- .build();
- final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "window");
- mPositioner = new TaskPositioner(mWm);
- mPositioner.register(mDisplayContent, win);
-
- win.getRootTask().setWindowingMode(WINDOWING_MODE_FREEFORM);
- }
-
- @After
- public void tearDown() {
- TaskPositioner.setFactory(null);
- }
-
- @Test
- public void testOverrideFactory() {
- final boolean[] created = new boolean[1];
- created[0] = false;
- TaskPositioner.setFactory(new TaskPositioner.Factory() {
- @Override
- public TaskPositioner create(WindowManagerService service) {
- created[0] = true;
- return null;
- }
- });
-
- assertNull(TaskPositioner.create(mWm));
- assertTrue(created[0]);
- }
-
- /** This tests that the window can move in all directions. */
- @Test
- public void testMoveWindow() {
- final Rect displayBounds = mDisplayContent.getBounds();
- final int windowSize = Math.min(displayBounds.width(), displayBounds.height()) / 2;
- final int left = displayBounds.centerX() - windowSize / 2;
- final int top = displayBounds.centerY() - windowSize / 2;
- final Rect r = new Rect(left, top, left + windowSize, top + windowSize);
- mPositioner.mTask.setBounds(r);
- mPositioner.startDrag(false /* resizing */, false /* preserveOrientation */, left, top);
-
- // Move upper left.
- mPositioner.notifyMoveLocked(left - MOUSE_DELTA_X, top - MOUSE_DELTA_Y);
- r.offset(-MOUSE_DELTA_X, -MOUSE_DELTA_Y);
- assertBoundsEquals(r, mPositioner.getWindowDragBounds());
-
- // Move bottom right.
- mPositioner.notifyMoveLocked(left, top);
- r.offset(MOUSE_DELTA_X, MOUSE_DELTA_Y);
- assertBoundsEquals(r, mPositioner.getWindowDragBounds());
- }
-
- /**
- * This tests that free resizing will allow to change the orientation as well
- * as does some basic tests (e.g. dragging in Y only will keep X stable).
- */
- @Test
- public void testBasicFreeWindowResizing() {
- final Rect r = new Rect(100, 220, 700, 520);
- final int midY = (r.top + r.bottom) / 2;
- mPositioner.mTask.setBounds(r, true);
-
- // Start a drag resize starting upper left.
- mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
- r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y);
- assertBoundsEquals(r, mPositioner.getWindowDragBounds());
-
- // Drag to a good landscape size.
- mPositioner.resizeDrag(0.0f, 0.0f);
- assertBoundsEquals(new Rect(MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to a good portrait size.
- mPositioner.resizeDrag(400.0f, 0.0f);
- assertBoundsEquals(new Rect(400 + MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to a too small size for the width.
- mPositioner.resizeDrag(2000.0f, r.top);
- assertBoundsEquals(
- new Rect(r.right - mMinVisibleWidth, r.top + MOUSE_DELTA_Y, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to a too small size for the height.
- mPositioner.resizeDrag(r.left, 2000.0f);
- assertBoundsEquals(
- new Rect(r.left + MOUSE_DELTA_X, r.bottom - mMinVisibleHeight, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Start a drag resize left and see that only the left coord changes..
- mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
- r.left - MOUSE_DELTA_X, midY);
-
- // Drag to the left.
- mPositioner.resizeDrag(0.0f, midY);
- assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to the right.
- mPositioner.resizeDrag(200.0f, midY);
- assertBoundsEquals(new Rect(200 + MOUSE_DELTA_X, r.top, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to the top
- mPositioner.resizeDrag(r.left, 0.0f);
- assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to the bottom
- mPositioner.resizeDrag(r.left, 1000.0f);
- assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
- }
-
- /**
- * This tests that by dragging any edge, the fixed / opposite edge(s) remains anchored.
- */
- @Test
- public void testFreeWindowResizingTestAllEdges() {
- final Rect r = new Rect(100, 220, 700, 520);
- final int midX = (r.left + r.right) / 2;
- final int midY = (r.top + r.bottom) / 2;
- mPositioner.mTask.setBounds(r, true);
-
- // Drag upper left.
- mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
- r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y);
- mPositioner.resizeDrag(0.0f, 0.0f);
- assertNotEquals(r.left, mPositioner.getWindowDragBounds().left);
- assertEquals(r.right, mPositioner.getWindowDragBounds().right);
- assertNotEquals(r.top, mPositioner.getWindowDragBounds().top);
- assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
- // Drag upper.
- mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */, midX,
- r.top - MOUSE_DELTA_Y);
- mPositioner.resizeDrag(0.0f, 0.0f);
- assertEquals(r.left, mPositioner.getWindowDragBounds().left);
- assertEquals(r.right, mPositioner.getWindowDragBounds().right);
- assertNotEquals(r.top, mPositioner.getWindowDragBounds().top);
- assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
- // Drag upper right.
- mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
- r.right + MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y);
- mPositioner.resizeDrag(r.right + 100, 0.0f);
- assertEquals(r.left, mPositioner.getWindowDragBounds().left);
- assertNotEquals(r.right, mPositioner.getWindowDragBounds().right);
- assertNotEquals(r.top, mPositioner.getWindowDragBounds().top);
- assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
- // Drag right.
- mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
- r.right + MOUSE_DELTA_X, midY);
- mPositioner.resizeDrag(r.right + 100, 0.0f);
- assertEquals(r.left, mPositioner.getWindowDragBounds().left);
- assertNotEquals(r.right, mPositioner.getWindowDragBounds().right);
- assertEquals(r.top, mPositioner.getWindowDragBounds().top);
- assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
- // Drag bottom right.
- mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
- r.right + MOUSE_DELTA_X, r.bottom + MOUSE_DELTA_Y);
- mPositioner.resizeDrag(r.right + 100, r.bottom + 100);
- assertEquals(r.left, mPositioner.getWindowDragBounds().left);
- assertNotEquals(r.right, mPositioner.getWindowDragBounds().right);
- assertEquals(r.top, mPositioner.getWindowDragBounds().top);
- assertNotEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
- // Drag bottom.
- mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */, midX,
- r.bottom + MOUSE_DELTA_Y);
- mPositioner.resizeDrag(r.right + 100, r.bottom + 100);
- assertEquals(r.left, mPositioner.getWindowDragBounds().left);
- assertEquals(r.right, mPositioner.getWindowDragBounds().right);
- assertEquals(r.top, mPositioner.getWindowDragBounds().top);
- assertNotEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
- // Drag bottom left.
- mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
- r.left - MOUSE_DELTA_X, r.bottom + MOUSE_DELTA_Y);
- mPositioner.resizeDrag(0.0f, r.bottom + 100);
- assertNotEquals(r.left, mPositioner.getWindowDragBounds().left);
- assertEquals(r.right, mPositioner.getWindowDragBounds().right);
- assertEquals(r.top, mPositioner.getWindowDragBounds().top);
- assertNotEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
-
- // Drag left.
- mPositioner.startDrag(true /* resizing */, false /* preserveOrientation */,
- r.left - MOUSE_DELTA_X, midY);
- mPositioner.resizeDrag(0.0f, r.bottom + 100);
- assertNotEquals(r.left, mPositioner.getWindowDragBounds().left);
- assertEquals(r.right, mPositioner.getWindowDragBounds().right);
- assertEquals(r.top, mPositioner.getWindowDragBounds().top);
- assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom);
- }
-
- /**
- * This tests that a constrained landscape window will keep the aspect and do the
- * right things upon resizing when dragged from the top left corner.
- */
- @Test
- public void testLandscapePreservedWindowResizingDragTopLeft() {
- final Rect r = new Rect(100, 220, 700, 520);
- mPositioner.mTask.setBounds(r, true);
-
- mPositioner.startDrag(true /* resizing */, true /* preserveOrientation */,
- r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y);
- assertBoundsEquals(r, mPositioner.getWindowDragBounds());
-
- // Drag to a good landscape size.
- mPositioner.resizeDrag(0.0f, 0.0f);
- assertBoundsEquals(new Rect(MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to a good portrait size.
- mPositioner.resizeDrag(400.0f, 0.0f);
- int width = Math.round((float) (r.bottom - MOUSE_DELTA_Y) * MIN_ASPECT);
- assertBoundsEquals(new Rect(r.right - width, MOUSE_DELTA_Y, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to a too small size for the width.
- mPositioner.resizeDrag(2000.0f, r.top);
- final int w = mMinVisibleWidth;
- final int h = Math.round(w / MIN_ASPECT);
- assertBoundsEquals(new Rect(r.right - w, r.bottom - h, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to a too small size for the height.
- mPositioner.resizeDrag(r.left, 2000.0f);
- assertBoundsEquals(
- new Rect(r.left + MOUSE_DELTA_X, r.bottom - mMinVisibleHeight, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
- }
-
- /**
- * This tests that a constrained landscape window will keep the aspect and do the
- * right things upon resizing when dragged from the left corner.
- */
- @Test
- public void testLandscapePreservedWindowResizingDragLeft() {
- final Rect r = new Rect(100, 220, 700, 520);
- final int midY = (r.top + r.bottom) / 2;
- mPositioner.mTask.setBounds(r, true);
-
- mPositioner.startDrag(true /* resizing */, true /* preserveOrientation */,
- r.left - MOUSE_DELTA_X, midY);
-
- // Drag to the left.
- mPositioner.resizeDrag(0.0f, midY);
- assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to the right.
- mPositioner.resizeDrag(200.0f, midY);
- assertBoundsEquals(new Rect(200 + MOUSE_DELTA_X, r.top, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag all the way to the right and see the height also shrinking.
- mPositioner.resizeDrag(2000.0f, midY);
- final int w = mMinVisibleWidth;
- final int h = Math.round((float) w / MIN_ASPECT);
- assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h),
- mPositioner.getWindowDragBounds());
-
- // Drag to the top.
- mPositioner.resizeDrag(r.left, 0.0f);
- assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to the bottom.
- mPositioner.resizeDrag(r.left, 1000.0f);
- assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
- }
-
- /**
- * This tests that a constrained landscape window will keep the aspect and do the
- * right things upon resizing when dragged from the top corner.
- */
- @Test
- public void testLandscapePreservedWindowResizingDragTop() {
- final Rect r = new Rect(100, 220, 700, 520);
- final int midX = (r.left + r.right) / 2;
- mPositioner.mTask.setBounds(r, true);
-
- mPositioner.startDrag(true /*resizing*/, true /*preserveOrientation*/, midX,
- r.top - MOUSE_DELTA_Y);
-
- // Drag to the left (no change).
- mPositioner.resizeDrag(0.0f, r.top);
- assertBoundsEquals(new Rect(r.left, r.top + MOUSE_DELTA_Y, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to the right (no change).
- mPositioner.resizeDrag(2000.0f, r.top);
- assertBoundsEquals(new Rect(r.left , r.top + MOUSE_DELTA_Y, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to the top.
- mPositioner.resizeDrag(300.0f, 0.0f);
- int h = r.bottom - MOUSE_DELTA_Y;
- int w = Math.max(r.right - r.left, Math.round(h * MIN_ASPECT));
- assertBoundsEquals(new Rect(r.left, MOUSE_DELTA_Y, r.left + w, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to the bottom.
- mPositioner.resizeDrag(r.left, 1000.0f);
- h = mMinVisibleHeight;
- assertBoundsEquals(new Rect(r.left, r.bottom - h, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
- }
-
- /**
- * This tests that a constrained portrait window will keep the aspect and do the
- * right things upon resizing when dragged from the top left corner.
- */
- @Test
- public void testPortraitPreservedWindowResizingDragTopLeft() {
- final Rect r = new Rect(330, 100, 630, 600);
- mPositioner.mTask.setBounds(r, true);
-
- mPositioner.startDrag(true /*resizing*/, true /*preserveOrientation*/,
- r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y);
- assertBoundsEquals(r, mPositioner.getWindowDragBounds());
-
- // Drag to a good landscape size.
- mPositioner.resizeDrag(0.0f, 0.0f);
- int height = Math.round((float) (r.right - MOUSE_DELTA_X) * MIN_ASPECT);
- assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.bottom - height, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to a good portrait size.
- mPositioner.resizeDrag(400.0f, 0.0f);
- assertBoundsEquals(new Rect(400 + MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to a too small size for the height and the the width shrinking.
- mPositioner.resizeDrag(r.left + MOUSE_DELTA_X, 2000.0f);
- final int w = Math.max(mMinVisibleWidth, Math.round(mMinVisibleHeight / MIN_ASPECT));
- final int h = Math.max(mMinVisibleHeight, Math.round(w * MIN_ASPECT));
- assertBoundsEquals(
- new Rect(r.right - w, r.bottom - h, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
- }
-
- /**
- * This tests that a constrained portrait window will keep the aspect and do the
- * right things upon resizing when dragged from the left corner.
- */
- @Test
- public void testPortraitPreservedWindowResizingDragLeft() {
- final Rect r = new Rect(330, 100, 630, 600);
- final int midY = (r.top + r.bottom) / 2;
- mPositioner.mTask.setBounds(r, true);
-
- mPositioner.startDrag(true /* resizing */, true /* preserveOrientation */,
- r.left - MOUSE_DELTA_X, midY);
-
- // Drag to the left.
- mPositioner.resizeDrag(0.0f, midY);
- int w = r.right - MOUSE_DELTA_X;
- int h = Math.round(w * MIN_ASPECT);
- assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.top + h),
- mPositioner.getWindowDragBounds());
-
- // Drag to the right.
- mPositioner.resizeDrag(450.0f, midY);
- assertBoundsEquals(new Rect(450 + MOUSE_DELTA_X, r.top, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag all the way to the right.
- mPositioner.resizeDrag(2000.0f, midY);
- w = mMinVisibleWidth;
- h = Math.max(Math.round((float) w * MIN_ASPECT), r.height());
- assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h),
- mPositioner.getWindowDragBounds());
-
- // Drag to the top.
- mPositioner.resizeDrag(r.left, 0.0f);
- assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to the bottom.
- mPositioner.resizeDrag(r.left, 1000.0f);
- assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
- }
-
- /**
- * This tests that a constrained portrait window will keep the aspect and do the
- * right things upon resizing when dragged from the top corner.
- */
- @Test
- public void testPortraitPreservedWindowResizingDragTop() {
- final Rect r = new Rect(330, 100, 630, 600);
- final int midX = (r.left + r.right) / 2;
- mPositioner.mTask.setBounds(r, true);
-
- mPositioner.startDrag(true /* resizing */, true /* preserveOrientation */, midX,
- r.top - MOUSE_DELTA_Y);
-
- // Drag to the left (no change).
- mPositioner.resizeDrag(0.0f, r.top);
- assertBoundsEquals(new Rect(r.left, r.top + MOUSE_DELTA_Y, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to the right (no change).
- mPositioner.resizeDrag(2000.0f, r.top);
- assertBoundsEquals(new Rect(r.left , r.top + MOUSE_DELTA_Y, r.right, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to the top.
- mPositioner.resizeDrag(300.0f, 0.0f);
- int h = r.bottom - MOUSE_DELTA_Y;
- int w = Math.min(r.width(), Math.round(h / MIN_ASPECT));
- assertBoundsEquals(new Rect(r.left, MOUSE_DELTA_Y, r.left + w, r.bottom),
- mPositioner.getWindowDragBounds());
-
- // Drag to the bottom.
- mPositioner.resizeDrag(r.left, 1000.0f);
- h = Math.max(mMinVisibleHeight, Math.round(mMinVisibleWidth * MIN_ASPECT));
- w = Math.round(h / MIN_ASPECT);
- assertBoundsEquals(new Rect(r.left, r.bottom - h, r.left + w, r.bottom),
- mPositioner.getWindowDragBounds());
- }
-
- private static void assertBoundsEquals(Rect expected, Rect actual) {
- if (DEBUGGING) {
- if (!expected.equals(actual)) {
- Log.e(TAG, "rect(" + actual.toString() + ") != isRect(" + actual.toString()
- + ") " + Log.getStackTraceString(new Throwable()));
- }
- }
- assertEquals(expected, actual);
- }
-
- @Test
- public void testFinishingMovingWhenBinderDied() {
- spyOn(mWm.mTaskPositioningController);
-
- mPositioner.startDrag(false, false, 0 /* startX */, 0 /* startY */);
- verify(mWm.mTaskPositioningController, never()).finishTaskPositioning();
- mPositioner.binderDied();
- verify(mWm.mTaskPositioningController).finishTaskPositioning();
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
deleted file mode 100644
index bfc13d3..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.wm;
-
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import android.platform.test.annotations.Presubmit;
-import android.view.InputChannel;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link TaskPositioningController} class.
- *
- * Build/Install/Run:
- * atest WmTests:TaskPositioningControllerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class TaskPositioningControllerTests extends WindowTestsBase {
- private static final int TIMEOUT_MS = 1000;
-
- private TaskPositioningController mTarget;
- private WindowState mWindow;
-
- @Before
- public void setUp() throws Exception {
- assertNotNull(mWm.mTaskPositioningController);
- mTarget = mWm.mTaskPositioningController;
-
- when(mWm.mInputManager.transferTouchGesture(any(), any())).thenReturn(true);
-
- mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
- mWindow.getTask().setResizeMode(RESIZE_MODE_RESIZEABLE);
- mWindow.mInputChannel = new InputChannel();
- mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
- doReturn(mock(InputMonitor.class)).when(mDisplayContent).getInputMonitor();
- }
-
- @FlakyTest(bugId = 291067614)
- @Test
- public void testStartAndFinishPositioning() {
- assertFalse(mTarget.isPositioningLocked());
- assertNull(mTarget.getDragWindowHandleLocked());
-
- assertTrue(mTarget.startMovingTask(mWindow.mClient, 0, 0));
-
- assertTrue(mTarget.isPositioningLocked());
- assertNotNull(mTarget.getDragWindowHandleLocked());
-
- mTarget.finishTaskPositioning();
- // Wait until the looper processes finishTaskPositioning.
- assertTrue(waitHandlerIdle(mWm.mAnimationHandler, TIMEOUT_MS));
-
- assertFalse(mTarget.isPositioningLocked());
- assertNull(mTarget.getDragWindowHandleLocked());
- }
-
- @Test
- public void testFinishPositioningWhenAppRequested() {
- assertFalse(mTarget.isPositioningLocked());
- assertNull(mTarget.getDragWindowHandleLocked());
-
- assertTrue(mTarget.startMovingTask(mWindow.mClient, 0, 0));
-
- assertTrue(mTarget.isPositioningLocked());
- assertNotNull(mTarget.getDragWindowHandleLocked());
-
- mTarget.finishTaskPositioning(mWindow.mClient);
- // Wait until the looper processes finishTaskPositioning.
- assertTrue(waitHandlerIdle(mWm.mAnimationHandler, TIMEOUT_MS));
-
- assertFalse(mTarget.isPositioningLocked());
- assertNull(mTarget.getDragWindowHandleLocked());
- }
-
- @Test
- public void testHandleTapOutsideTask() {
- assertFalse(mTarget.isPositioningLocked());
- assertNull(mTarget.getDragWindowHandleLocked());
-
- final DisplayContent content = mock(DisplayContent.class);
- doReturn(mWindow.getTask()).when(content).findTaskForResizePoint(anyInt(), anyInt());
- assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow());
-
- mTarget.handleTapOutsideTask(content, 0, 0);
- // Wait until the looper processes handleTapOutsideTask.
- assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
-
- assertTrue(mTarget.isPositioningLocked());
- assertNotNull(mTarget.getDragWindowHandleLocked());
-
- mTarget.finishTaskPositioning();
- // Wait until the looper processes finishTaskPositioning.
- assertTrue(waitHandlerIdle(mWm.mAnimationHandler, TIMEOUT_MS));
-
- assertFalse(mTarget.isPositioningLocked());
- assertNull(mTarget.getDragWindowHandleLocked());
- }
-
- @Test
- public void testHandleTapOutsideNonResizableTask() {
- assertFalse(mTarget.isPositioningLocked());
- assertNull(mTarget.getDragWindowHandleLocked());
-
- final DisplayContent content = mock(DisplayContent.class);
- doReturn(mWindow.getTask()).when(content).findTaskForResizePoint(anyInt(), anyInt());
- assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow());
-
- mWindow.getTask().setResizeMode(RESIZE_MODE_UNRESIZEABLE);
-
- mTarget.handleTapOutsideTask(content, 0, 0);
- // Wait until the looper processes handleTapOutsideTask.
- assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
-
- assertFalse(mTarget.isPositioningLocked());
- }
-
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 7d01b79..720457e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1573,7 +1573,8 @@
enteringAnimReports.clear();
doCallRealMethod().when(mWm.mRoot).ensureActivitiesVisible(any(), anyBoolean());
final boolean[] wasInFinishingTransition = { false };
- controller.registerLegacyListener(new WindowManagerInternal.AppTransitionListener() {
+ controller.registerLegacyListener(new WindowManagerInternal.AppTransitionListener(
+ mDisplayContent.mDisplayId) {
@Override
public void onAppTransitionFinishedLocked(IBinder token) {
final ActivityRecord r = ActivityRecord.forToken(token);
@@ -1582,6 +1583,14 @@
}
}
});
+ final boolean[] calledListenerOnOtherDisplay = { false };
+ controller.registerLegacyListener(new WindowManagerInternal.AppTransitionListener(
+ mDisplayContent.mDisplayId + 1234) {
+ @Override
+ public void onAppTransitionFinishedLocked(IBinder token) {
+ calledListenerOnOtherDisplay[0] = true;
+ }
+ });
assertTrue(activity1.isVisible());
doReturn(false).when(task1).isTranslucent(null);
doReturn(false).when(task1).isTranslucentForTransition();
@@ -1592,6 +1601,7 @@
controller.finishTransition(closeTransition);
assertTrue(wasInFinishingTransition[0]);
+ assertFalse(calledListenerOnOtherDisplay[0]);
assertNull(controller.mFinishingTransition);
assertTrue(activity2.isVisible());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index e6648da..0cb22ad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED;
import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -86,6 +87,7 @@
ApplicationInfo info = mock(ApplicationInfo.class);
info.packageName = "test.package.name";
+ doReturn(true).when(info).isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED);
mWpc = new WindowProcessController(
mAtm, info, null, 0, -1, null, mMockListener);
mWpc.setThread(mock(IApplicationThread.class));
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java
index e5f2f89..eda78cb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java
@@ -63,9 +63,6 @@
resetCache();
}
- private static final String SYSTEM_PROPERTY_OVERRIDE_KEY =
- "sys.wmshell.desktopmode.dev_toggle_override";
-
@Test
@DisableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
@@ -190,110 +187,6 @@
}
@Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
- @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isEnabled_noProperty_overrideOn_featureFlagOff_returnsTrueAndPropertyOn() {
- System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY);
- setOverride(OVERRIDE_ON.getSetting());
-
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
- // Store System Property if not present
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(String.valueOf(OVERRIDE_ON.getSetting()));
- }
-
- @Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- public void isEnabled_noProperty_overrideUnset_featureFlagOn_returnsTrueAndPropertyUnset() {
- System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY);
- setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
-
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
- // Store System Property if not present
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(String.valueOf(
- DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()));
- }
-
- @Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
- @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isEnabled_noProperty_overrideUnset_featureFlagOff_returnsFalseAndPropertyUnset() {
- System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY);
- setOverride(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting());
-
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
- // Store System Property if not present
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(String.valueOf(
- DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()));
- }
-
- @Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- public void isEnabled_propertyNotInt_overrideOff_featureFlagOn_returnsFalseAndPropertyOff() {
- System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "abc");
- setOverride(OVERRIDE_OFF.getSetting());
-
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
- // Store System Property if currently invalid
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(String.valueOf(OVERRIDE_OFF.getSetting()));
- }
-
- @Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- public void isEnabled_propertyInvalid_overrideOff_featureFlagOn_returnsFalseAndPropertyOff() {
- System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "-2");
- setOverride(OVERRIDE_OFF.getSetting());
-
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
- // Store System Property if currently invalid
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(String.valueOf(OVERRIDE_OFF.getSetting()));
- }
-
- @Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- public void isEnabled_propertyOff_overrideOn_featureFlagOn_returnsFalseAndnoPropertyUpdate() {
- System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, String.valueOf(
- OVERRIDE_OFF.getSetting()));
- setOverride(OVERRIDE_ON.getSetting());
-
- // Have a consistent override until reboot
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse();
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(String.valueOf(OVERRIDE_OFF.getSetting()));
- }
-
- @Test
- @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
- @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- public void isEnabled_propertyOn_overrideOff_featureFlagOff_returnsTrueAndnoPropertyUpdate() {
- System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, String.valueOf(OVERRIDE_ON.getSetting()));
- setOverride(OVERRIDE_OFF.getSetting());
-
- // Have a consistent override until reboot
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(String.valueOf(OVERRIDE_ON.getSetting()));
- }
-
- @Test
- @EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE})
- public void isEnabled_propertyUnset_overrideOff_featureFlagOn_returnsTrueAndnoPropertyUpdate() {
- System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY,
- String.valueOf(DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()));
- setOverride(OVERRIDE_OFF.getSetting());
-
- // Have a consistent override until reboot
- assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue();
- assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
- .isEqualTo(String.valueOf(
- DesktopModeFlagsUtil.ToggleOverride.OVERRIDE_UNSET.getSetting()));
- }
-
- @Test
@EnableFlags({FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY})
public void isEnabled_dwFlagOn_overrideUnset_featureFlagOn_returnsTrue() {
@@ -452,8 +345,5 @@
"sCachedToggleOverride");
cachedToggleOverride.setAccessible(true);
cachedToggleOverride.set(null, null);
-
- // Clear override cache stored in System property
- System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY);
}
}
diff --git a/tests/Input/AndroidTest.xml b/tests/Input/AndroidTest.xml
index 8c24879..bc9322f 100644
--- a/tests/Input/AndroidTest.xml
+++ b/tests/Input/AndroidTest.xml
@@ -25,6 +25,7 @@
<option name="device-listeners" value="android.tools.collectors.DefaultUITraceListener"/>
<!-- DefaultUITraceListener args -->
<option name="instrumentation-arg" key="skip_test_success_metrics" value="true"/>
+ <option name="instrumentation-arg" key="per_class" value="true"/>
</test>
<object class="com.android.tradefed.testtype.suite.module.TestFailureModuleController"
type="module_controller">
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
index b6672a0..fad94d4 100644
--- a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
@@ -698,7 +698,7 @@
traceMonitor.start();
mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP,
- "My test null string: %s", null);
+ "My test null string: %s", (Object) null);
} finally {
traceMonitor.stop(mWriter);
}