Merge "Fixes activity not resumed after applying WCT" into main
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 8ecb1fb..2948129 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -19,6 +19,7 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.service.dreams.Flags.dreamHandlesConfirmKeys;
import static android.service.dreams.Flags.dreamHandlesBeingObscured;
+import static android.service.dreams.Flags.startAndStopDozingInBackground;
import android.annotation.FlaggedApi;
import android.annotation.IdRes;
@@ -923,9 +924,16 @@
if (mDozing) {
try {
- mDreamManager.startDozing(
+ if (startAndStopDozingInBackground()) {
+ mDreamManager.startDozingOneway(
mDreamToken, mDozeScreenState, mDozeScreenStateReason,
mDozeScreenBrightness);
+ } else {
+ mDreamManager.startDozing(
+ mDreamToken, mDozeScreenState, mDozeScreenStateReason,
+ mDozeScreenBrightness);
+ }
+
} catch (RemoteException ex) {
// system server died
}
@@ -1250,7 +1258,11 @@
try {
// finishSelf will unbind the dream controller from the dream service. This will
// trigger DreamService.this.onDestroy and DreamService.this will die.
- mDreamManager.finishSelf(mDreamToken, true /*immediate*/);
+ if (startAndStopDozingInBackground()) {
+ mDreamManager.finishSelfOneway(mDreamToken, true /*immediate*/);
+ } else {
+ mDreamManager.finishSelf(mDreamToken, true /*immediate*/);
+ }
} catch (RemoteException ex) {
// system server died
}
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index cf98bfe0..620eef6 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -50,4 +50,6 @@
void startDreamActivity(in Intent intent);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)")
oneway void setDreamIsObscured(in boolean isObscured);
+ oneway void startDozingOneway(in IBinder token, int screenState, int reason, int screenBrightness);
+ oneway void finishSelfOneway(in IBinder token, boolean immediate);
}
diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig
index 54d950c..83e0adf 100644
--- a/core/java/android/service/dreams/flags.aconfig
+++ b/core/java/android/service/dreams/flags.aconfig
@@ -47,3 +47,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "start_and_stop_dozing_in_background"
+ namespace: "systemui"
+ description: "Move the start-dozing and stop-dozing operation to the background"
+ bug: "330287187"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/view/ImeBackAnimationController.java b/core/java/android/view/ImeBackAnimationController.java
index fc1852d..c7e93c1 100644
--- a/core/java/android/view/ImeBackAnimationController.java
+++ b/core/java/android/view/ImeBackAnimationController.java
@@ -27,6 +27,7 @@
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.WindowConfiguration;
import android.graphics.Insets;
import android.util.Log;
import android.view.animation.BackGestureInterpolator;
@@ -137,9 +138,10 @@
@Override
public void onBackInvoked() {
if (!isBackAnimationAllowed() || !mIsPreCommitAnimationInProgress) {
- // play regular hide animation if back-animation is not allowed or if insets control has
- // been cancelled by the system (this can happen in split screen for example)
- mInsetsController.hide(ime());
+ // play regular hide animation if predictive back-animation is not allowed or if insets
+ // control has been cancelled by the system. This can happen in multi-window mode for
+ // example (i.e. split-screen or activity-embedding)
+ notifyHideIme();
return;
}
startPostCommitAnim(/*hideIme*/ true);
@@ -209,6 +211,11 @@
if (triggerBack) {
mInsetsController.setPredictiveBackImeHideAnimInProgress(true);
notifyHideIme();
+ // requesting IME as invisible during post-commit
+ mInsetsController.setRequestedVisibleTypes(0, ime());
+ // Changes the animation state. This also notifies RootView of changed insets, which
+ // causes it to reset its scrollY to 0f (animated) if it was panned
+ mInsetsController.onAnimationStateChanged(ime(), /*running*/ true);
}
if (mStartRootScrollY != 0 && !triggerBack) {
// This causes RootView to update its scroll back to the panned position
@@ -228,12 +235,6 @@
// the IME away
mInsetsController.getHost().getInputMethodManager()
.notifyImeHidden(mInsetsController.getHost().getWindowToken(), statsToken);
-
- // requesting IME as invisible during post-commit
- mInsetsController.setRequestedVisibleTypes(0, ime());
- // Changes the animation state. This also notifies RootView of changed insets, which causes
- // it to reset its scrollY to 0f (animated) if it was panned
- mInsetsController.onAnimationStateChanged(ime(), /*running*/ true);
}
private void reset() {
@@ -254,8 +255,18 @@
}
private boolean isBackAnimationAllowed() {
- // back animation is allowed in all cases except when softInputMode is adjust_resize AND
- // there is no app-registered WindowInsetsAnimationCallback AND edge-to-edge is not enabled.
+
+ if (mViewRoot.mContext.getResources().getConfiguration().windowConfiguration
+ .getWindowingMode() == WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW) {
+ // TODO(b/346726115) enable predictive back animation in multi-window mode in
+ // DisplayImeController
+ return false;
+ }
+
+ // otherwise, the predictive back animation is allowed in all cases except when
+ // 1. softInputMode is adjust_resize AND
+ // 2. there is no app-registered WindowInsetsAnimationCallback AND
+ // 3. edge-to-edge is not enabled.
return (mViewRoot.mWindowAttributes.softInputMode & SOFT_INPUT_MASK_ADJUST)
!= SOFT_INPUT_ADJUST_RESIZE
|| (mViewRoot.mView != null && mViewRoot.mView.hasWindowInsetsAnimationCallback())
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c0bd535..cda58e38 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -7451,6 +7451,10 @@
@Override
protected int onProcess(QueuedInputEvent q) {
+ if (q.forPreImeOnly()) {
+ // this event is intended for the ViewPreImeInputStage only, let's forward
+ return FORWARD;
+ }
if (q.mEvent instanceof KeyEvent) {
final KeyEvent keyEvent = (KeyEvent) q.mEvent;
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 4fb6e69..9b87e23 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -489,7 +489,8 @@
return;
}
OnBackAnimationCallback animationCallback = getBackAnimationCallback();
- if (animationCallback != null) {
+ if (animationCallback != null
+ && !(callback instanceof ImeBackAnimationController)) {
mProgressAnimator.onBackInvoked(callback::onBackInvoked);
} else {
mProgressAnimator.reset();
diff --git a/core/res/res/anim/overlay_task_fragment_change.xml b/core/res/res/anim/overlay_task_fragment_change.xml
new file mode 100644
index 0000000..eb02ba8
--- /dev/null
+++ b/core/res/res/anim/overlay_task_fragment_change.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:showBackdrop="false">
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/overlay_task_fragment_close_to_bottom.xml b/core/res/res/anim/overlay_task_fragment_close_to_bottom.xml
new file mode 100644
index 0000000..d9487cb
--- /dev/null
+++ b/core/res/res/anim/overlay_task_fragment_close_to_bottom.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate
+ android:fromYDelta="0"
+ android:toYDelta="100%"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:duration="517" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/overlay_task_fragment_close_to_left.xml b/core/res/res/anim/overlay_task_fragment_close_to_left.xml
new file mode 100644
index 0000000..3cdb77a
--- /dev/null
+++ b/core/res/res/anim/overlay_task_fragment_close_to_left.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate
+ android:fromXDelta="0"
+ android:toXDelta="-100%"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:duration="517" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/overlay_task_fragment_close_to_right.xml b/core/res/res/anim/overlay_task_fragment_close_to_right.xml
new file mode 100644
index 0000000..3764561
--- /dev/null
+++ b/core/res/res/anim/overlay_task_fragment_close_to_right.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate
+ android:fromXDelta="0"
+ android:toXDelta="100%"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:duration="517" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/overlay_task_fragment_close_to_top.xml b/core/res/res/anim/overlay_task_fragment_close_to_top.xml
new file mode 100644
index 0000000..a8bfbbd
--- /dev/null
+++ b/core/res/res/anim/overlay_task_fragment_close_to_top.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate
+ android:fromYDelta="0"
+ android:toYDelta="-100%"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:duration="517" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/overlay_task_fragment_open_from_bottom.xml b/core/res/res/anim/overlay_task_fragment_open_from_bottom.xml
new file mode 100644
index 0000000..1d1223f
--- /dev/null
+++ b/core/res/res/anim/overlay_task_fragment_open_from_bottom.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate
+ android:fromYDelta="100%"
+ android:toYDelta="0"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:duration="517" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/overlay_task_fragment_open_from_left.xml b/core/res/res/anim/overlay_task_fragment_open_from_left.xml
new file mode 100644
index 0000000..5e5e080
--- /dev/null
+++ b/core/res/res/anim/overlay_task_fragment_open_from_left.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate
+ android:fromXDelta="-100%"
+ android:toXDelta="0"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:duration="517" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/overlay_task_fragment_open_from_right.xml b/core/res/res/anim/overlay_task_fragment_open_from_right.xml
new file mode 100644
index 0000000..5674ff3
--- /dev/null
+++ b/core/res/res/anim/overlay_task_fragment_open_from_right.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate
+ android:fromXDelta="100%"
+ android:toXDelta="0"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:duration="517" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/overlay_task_fragment_open_from_top.xml b/core/res/res/anim/overlay_task_fragment_open_from_top.xml
new file mode 100644
index 0000000..2e3dd0a
--- /dev/null
+++ b/core/res/res/anim/overlay_task_fragment_open_from_top.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate
+ android:fromYDelta="-100%"
+ android:toYDelta="0"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:duration="517" />
+</set>
\ No newline at end of file
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e3f9187..f696e872 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6511,17 +6511,17 @@
<!-- Fingerprint dangling notification title -->
<string name="fingerprint_dangling_notification_title">Set up Fingerprint Unlock again</string>
<!-- Fingerprint dangling notification content for only 1 fingerprint deleted -->
- <string name="fingerprint_dangling_notification_msg_1"><xliff:g id="fingerprint">%s</xliff:g> wasn\'t working well and was deleted</string>
+ <string name="fingerprint_dangling_notification_msg_1"><xliff:g id="fingerprint">%s</xliff:g> can no longer be recognized.</string>
<!-- Fingerprint dangling notification content for more than 1 fingerprints deleted -->
- <string name="fingerprint_dangling_notification_msg_2"><xliff:g id="fingerprint">%1$s</xliff:g> and <xliff:g id="fingerprint">%2$s</xliff:g> weren\'t working well and were deleted</string>
+ <string name="fingerprint_dangling_notification_msg_2"><xliff:g id="fingerprint">%1$s</xliff:g> and <xliff:g id="fingerprint">%2$s</xliff:g> can no longer be recognized.</string>
<!-- Fingerprint dangling notification content for only 1 fingerprint deleted and no fingerprint left-->
- <string name="fingerprint_dangling_notification_msg_all_deleted_1"><xliff:g id="fingerprint">%s</xliff:g> wasn\'t working well and was deleted. Set it up again to unlock your phone with fingerprint.</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1"><xliff:g id="fingerprint">%s</xliff:g> can no longer be recognized. Set up Fingerprint Unlock again.</string>
<!-- Fingerprint dangling notification content for more than 1 fingerprints deleted and no fingerprint left -->
- <string name="fingerprint_dangling_notification_msg_all_deleted_2"><xliff:g id="fingerprint">%1$s</xliff:g> and <xliff:g id="fingerprint">%2$s</xliff:g> weren\'t working well and were deleted. Set them up again to unlock your phone with your fingerprint.</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2"><xliff:g id="fingerprint">%1$s</xliff:g> and <xliff:g id="fingerprint">%2$s</xliff:g> can no longer be recognized. Set up Fingerprint Unlock again.</string>
<!-- Face dangling notification title -->
<string name="face_dangling_notification_title">Set up Face Unlock again</string>
<!-- Face dangling notification content -->
- <string name="face_dangling_notification_msg">Your face model wasn\'t working well and was deleted. Set it up again to unlock your phone with face.</string>
+ <string name="face_dangling_notification_msg">Your face model can no longer be recognized. Set up Face Unlock again.</string>
<!-- Biometric dangling notification "set up" action button -->
<string name="biometric_dangling_notification_action_set_up">Set up</string>
<!-- Biometric dangling notification "Not now" action button -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6e5e106..7a51abc 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1753,6 +1753,15 @@
<java-symbol type="anim" name="task_fragment_clear_top_close_exit" />
<java-symbol type="anim" name="task_fragment_clear_top_open_enter" />
<java-symbol type="anim" name="task_fragment_clear_top_open_exit" />
+ <java-symbol type="anim" name="overlay_task_fragment_open_from_left" />
+ <java-symbol type="anim" name="overlay_task_fragment_open_from_top" />
+ <java-symbol type="anim" name="overlay_task_fragment_open_from_right" />
+ <java-symbol type="anim" name="overlay_task_fragment_open_from_bottom" />
+ <java-symbol type="anim" name="overlay_task_fragment_change" />
+ <java-symbol type="anim" name="overlay_task_fragment_close_to_left" />
+ <java-symbol type="anim" name="overlay_task_fragment_close_to_top" />
+ <java-symbol type="anim" name="overlay_task_fragment_close_to_right" />
+ <java-symbol type="anim" name="overlay_task_fragment_close_to_bottom" />
<java-symbol type="array" name="config_autoRotationTiltTolerance" />
<java-symbol type="array" name="config_longPressVibePattern" />
diff --git a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
index 58e5be2..4d9b591c 100644
--- a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
+++ b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
@@ -34,6 +34,7 @@
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.graphics.Insets;
import android.platform.test.annotations.Presubmit;
@@ -102,6 +103,8 @@
mViewRoot.setOnContentApplyWindowInsetsListener(
mock(Window.OnContentApplyWindowInsetsListener.class));
mBackAnimationController = new ImeBackAnimationController(mViewRoot, mInsetsController);
+ mViewRoot.mContext.getResources().getConfiguration().windowConfiguration
+ .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
when(mWindowInsetsAnimationController.getHiddenStateInsets()).thenReturn(Insets.NONE);
when(mWindowInsetsAnimationController.getShownStateInsets()).thenReturn(IME_INSETS);
@@ -156,8 +159,28 @@
mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, 0.5f, EDGE_LEFT));
// commit back gesture
mBackAnimationController.onBackInvoked();
- // verify that InsetsController#hide is called
- verify(mInsetsController, times(1)).hide(ime());
+ // verify that InputMethodManager#notifyImeHidden is called (which is the case whenever
+ // getInputMethodManager is called from ImeBackAnimationController)
+ verify(mViewRootInsetsControllerHost, times(1)).getInputMethodManager();
+ // verify that ImeBackAnimationController does not take control over IME insets
+ verify(mInsetsController, never()).controlWindowInsetsAnimation(anyInt(), any(), any(),
+ anyBoolean(), anyLong(), any(), anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void testMultiWindowModeNotPlayingAnim() {
+ // setup ViewRoot with WINDOWING_MODE_MULTI_WINDOW
+ mViewRoot.mContext.getResources().getConfiguration().windowConfiguration.setWindowingMode(
+ WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
+ // start back gesture
+ mBackAnimationController.onBackStarted(new BackEvent(0f, 0f, 0f, EDGE_LEFT));
+ // progress back gesture
+ mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, 0.5f, EDGE_LEFT));
+ // commit back gesture
+ mBackAnimationController.onBackInvoked();
+ // verify that InputMethodManager#notifyImeHidden is called (which is the case whenever
+ // getInputMethodManager is called from ImeBackAnimationController)
+ verify(mViewRootInsetsControllerHost, times(1)).getInputMethodManager();
// verify that ImeBackAnimationController does not take control over IME insets
verify(mInsetsController, never()).controlWindowInsetsAnimation(anyInt(), any(), any(),
anyBoolean(), anyLong(), any(), anyInt(), anyBoolean());
@@ -277,9 +300,9 @@
// commit back gesture
mBackAnimationController.onBackInvoked();
-
- // verify that InsetsController#hide is called
- verify(mInsetsController, times(1)).hide(ime());
+ // verify that InputMethodManager#notifyImeHidden is called (which is the case whenever
+ // getInputMethodManager is called from ImeBackAnimationController)
+ verify(mViewRootInsetsControllerHost, times(1)).getInputMethodManager();
});
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index d0e49d8..eb1fc23 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -20,8 +20,10 @@
import static androidx.window.extensions.embedding.DividerPresenter.getBoundsOffsetForDivider;
import static androidx.window.extensions.embedding.SplitAttributesHelper.isReversedLayout;
+import static androidx.window.extensions.embedding.SplitController.TAG;
import static androidx.window.extensions.embedding.WindowAttributes.DIM_AREA_ON_TASK;
+import android.annotation.AnimRes;
import android.app.Activity;
import android.app.ActivityThread;
import android.app.WindowConfiguration;
@@ -31,9 +33,11 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
+import android.util.Log;
import android.util.Pair;
import android.util.Size;
import android.view.View;
@@ -56,6 +60,7 @@
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import androidx.window.extensions.layout.WindowLayoutInfo;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.window.flags.Flags;
@@ -125,6 +130,16 @@
static final int RESULT_EXPAND_FAILED_NO_TF_INFO = 2;
/**
+ * The key of {@link ActivityStack} alignment relative to its parent container.
+ * <p>
+ * See {@link ContainerPosition} for possible values.
+ * <p>
+ * Note that this constants must align with the definition in WM Jetpack library.
+ */
+ private static final String KEY_ACTIVITY_STACK_ALIGNMENT =
+ "androidx.window.embedding.ActivityStackAlignment";
+
+ /**
* Result of {@link #expandSplitContainerIfNeeded(WindowContainerTransaction, SplitContainer,
* Activity, Activity, Intent)}
*/
@@ -649,14 +664,114 @@
// TODO(b/243518738): Update to resizeTaskFragment after we migrate WCT#setRelativeBounds
// and WCT#setWindowingMode to take fragmentToken.
resizeTaskFragmentIfRegistered(wct, container, relativeBounds);
- int windowingMode = container.getTaskContainer().getWindowingModeForTaskFragment(
- relativeBounds);
+ final TaskContainer taskContainer = container.getTaskContainer();
+ final int windowingMode = taskContainer.getWindowingModeForTaskFragment(relativeBounds);
updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode);
- // Always use default animation for standalone ActivityStack.
- updateAnimationParams(wct, fragmentToken, TaskFragmentAnimationParams.DEFAULT);
+ if (container.isOverlay() && isOverlayTransitionSupported()) {
+ // Use the overlay transition for the overlay container if it's supported.
+ final TaskFragmentAnimationParams params = createOverlayAnimationParams(relativeBounds,
+ taskContainer.getBounds(), container);
+ updateAnimationParams(wct, fragmentToken, params);
+ } else {
+ // Otherwise, fallabck to use the default animation params.
+ updateAnimationParams(wct, fragmentToken, TaskFragmentAnimationParams.DEFAULT);
+ }
setTaskFragmentDimOnTask(wct, fragmentToken, dimOnTask);
}
+ private static boolean isOverlayTransitionSupported() {
+ return Flags.moveAnimationOptionsToChange()
+ && Flags.activityEmbeddingOverlayPresentationFlag();
+ }
+
+ @NonNull
+ private static TaskFragmentAnimationParams createOverlayAnimationParams(
+ @NonNull Rect relativeBounds, @NonNull Rect parentContainerBounds,
+ @NonNull TaskFragmentContainer container) {
+ if (relativeBounds.isEmpty()) {
+ return TaskFragmentAnimationParams.DEFAULT;
+ }
+
+ final int positionFromOptions = container.getLaunchOptions()
+ .getInt(KEY_ACTIVITY_STACK_ALIGNMENT , -1);
+ final int position = positionFromOptions != -1 ? positionFromOptions
+ // Fallback to calculate from bounds if the info can't be retrieved from options.
+ : getOverlayPosition(relativeBounds, parentContainerBounds);
+
+ return new TaskFragmentAnimationParams.Builder()
+ .setOpenAnimationResId(getOpenAnimationResourcesId(position))
+ .setChangeAnimationResId(R.anim.overlay_task_fragment_change)
+ .setCloseAnimationResId(getCloseAnimationResourcesId(position))
+ .build();
+ }
+
+ @VisibleForTesting
+ @ContainerPosition
+ static int getOverlayPosition(
+ @NonNull Rect relativeBounds, @NonNull Rect parentContainerBounds) {
+ final Rect relativeParentBounds = new Rect(parentContainerBounds);
+ relativeParentBounds.offsetTo(0, 0);
+ final int leftMatch = (relativeParentBounds.left == relativeBounds.left) ? 1 : 0;
+ final int topMatch = (relativeParentBounds.top == relativeBounds.top) ? 1 : 0;
+ final int rightMatch = (relativeParentBounds.right == relativeBounds.right) ? 1 : 0;
+ final int bottomMatch = (relativeParentBounds.bottom == relativeBounds.bottom) ? 1 : 0;
+
+ // Flag format: {left|top|right|bottom}. Note that overlay container could be shrunk and
+ // centered, which makes only one of overlay container edge matches the parent container.
+ final int directionFlag = (leftMatch << 3) + (topMatch << 2) + (rightMatch << 1)
+ + bottomMatch;
+
+ final int position = switch (directionFlag) {
+ // Only the left edge match or only the right edge not match: should be on the left of
+ // the parent container.
+ case 0b1000, 0b1101 -> CONTAINER_POSITION_LEFT;
+ // Only the top edge match or only the bottom edge not match: should be on the top of
+ // the parent container.
+ case 0b0100, 0b1110 -> CONTAINER_POSITION_TOP;
+ // Only the right edge match or only the left edge not match: should be on the right of
+ // the parent container.
+ case 0b0010, 0b0111 -> CONTAINER_POSITION_RIGHT;
+ // Only the bottom edge match or only the top edge not match: should be on the bottom of
+ // the parent container.
+ case 0b0001, 0b1011 -> CONTAINER_POSITION_BOTTOM;
+ default -> {
+ Log.w(TAG, "Unsupported position:" + Integer.toBinaryString(directionFlag)
+ + " fallback to treat it as right. Relative parent bounds: "
+ + relativeParentBounds + ", relative overlay bounds:" + relativeBounds);
+ yield CONTAINER_POSITION_RIGHT;
+ }
+ };
+ return position;
+ }
+
+ @AnimRes
+ private static int getOpenAnimationResourcesId(@ContainerPosition int position) {
+ return switch (position) {
+ case CONTAINER_POSITION_LEFT -> R.anim.overlay_task_fragment_open_from_left;
+ case CONTAINER_POSITION_TOP -> R.anim.overlay_task_fragment_open_from_top;
+ case CONTAINER_POSITION_RIGHT -> R.anim.overlay_task_fragment_open_from_right;
+ case CONTAINER_POSITION_BOTTOM -> R.anim.overlay_task_fragment_open_from_bottom;
+ default -> {
+ Log.w(TAG, "Unknown position:" + position);
+ yield Resources.ID_NULL;
+ }
+ };
+ }
+
+ @AnimRes
+ private static int getCloseAnimationResourcesId(@ContainerPosition int position) {
+ return switch (position) {
+ case CONTAINER_POSITION_LEFT -> R.anim.overlay_task_fragment_close_to_left;
+ case CONTAINER_POSITION_TOP -> R.anim.overlay_task_fragment_close_to_top;
+ case CONTAINER_POSITION_RIGHT -> R.anim.overlay_task_fragment_close_to_right;
+ case CONTAINER_POSITION_BOTTOM -> R.anim.overlay_task_fragment_close_to_bottom;
+ default -> {
+ Log.w(TAG, "Unknown position:" + position);
+ yield Resources.ID_NULL;
+ }
+ };
+ }
+
/**
* Returns the expanded bounds if the {@code relBounds} violate minimum dimension or are not
* fully covered by the task bounds. Otherwise, returns {@code relBounds}.
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 7a0b9a0..3257502 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -30,6 +30,11 @@
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPlaceholderRuleBuilder;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTfContainer;
+import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_BOTTOM;
+import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_LEFT;
+import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_RIGHT;
+import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_TOP;
+import static androidx.window.extensions.embedding.SplitPresenter.getOverlayPosition;
import static androidx.window.extensions.embedding.SplitPresenter.sanitizeBounds;
import static androidx.window.extensions.embedding.WindowAttributes.DIM_AREA_ON_TASK;
@@ -666,8 +671,8 @@
attributes.getRelativeBounds());
verify(mSplitPresenter).updateTaskFragmentWindowingModeIfRegistered(mTransaction, container,
WINDOWING_MODE_MULTI_WINDOW);
- verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
- TaskFragmentAnimationParams.DEFAULT);
+ verify(mSplitPresenter).updateAnimationParams(eq(mTransaction), eq(token),
+ any(TaskFragmentAnimationParams.class));
verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, true);
verify(mSplitPresenter, never()).setTaskFragmentPinned(any(),
any(TaskFragmentContainer.class), anyBoolean());
@@ -691,8 +696,8 @@
attributes.getRelativeBounds());
verify(mSplitPresenter).updateTaskFragmentWindowingModeIfRegistered(mTransaction,
container, WINDOWING_MODE_MULTI_WINDOW);
- verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
- TaskFragmentAnimationParams.DEFAULT);
+ verify(mSplitPresenter).updateAnimationParams(eq(mTransaction), eq(token),
+ any(TaskFragmentAnimationParams.class));
verify(mSplitPresenter, never()).setTaskFragmentIsolatedNavigation(any(),
any(TaskFragmentContainer.class), anyBoolean());
verify(mSplitPresenter).setTaskFragmentPinned(mTransaction, container, true);
@@ -870,6 +875,59 @@
eq(overlayContainer.getTaskFragmentToken()), eq(activityToken));
}
+ // TODO(b/243518738): Rewrite with TestParameter.
+ @Test
+ public void testGetOverlayPosition() {
+ assertWithMessage("It must be position left for left overlay.")
+ .that(getOverlayPosition(new Rect(
+ TASK_BOUNDS.left,
+ TASK_BOUNDS.top,
+ TASK_BOUNDS.right / 2,
+ TASK_BOUNDS.bottom), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_LEFT);
+ assertWithMessage("It must be position left for shrunk left overlay.")
+ .that(getOverlayPosition(new Rect(
+ TASK_BOUNDS.left,
+ TASK_BOUNDS.top + 20,
+ TASK_BOUNDS.right / 2,
+ TASK_BOUNDS.bottom - 20), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_LEFT);
+ assertWithMessage("It must be position left for top overlay.")
+ .that(getOverlayPosition(new Rect(
+ TASK_BOUNDS.left,
+ TASK_BOUNDS.top,
+ TASK_BOUNDS.right,
+ TASK_BOUNDS.bottom / 2), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_TOP);
+ assertWithMessage("It must be position left for shrunk top overlay.")
+ .that(getOverlayPosition(new Rect(
+ TASK_BOUNDS.left + 20,
+ TASK_BOUNDS.top,
+ TASK_BOUNDS.right - 20,
+ TASK_BOUNDS.bottom / 2), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_TOP);
+ assertWithMessage("It must be position left for right overlay.")
+ .that(getOverlayPosition(new Rect(
+ TASK_BOUNDS.right / 2,
+ TASK_BOUNDS.top,
+ TASK_BOUNDS.right,
+ TASK_BOUNDS.bottom), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_RIGHT);
+ assertWithMessage("It must be position left for shrunk right overlay.")
+ .that(getOverlayPosition(new Rect(
+ TASK_BOUNDS.right / 2,
+ TASK_BOUNDS.top + 20,
+ TASK_BOUNDS.right,
+ TASK_BOUNDS.bottom - 20), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_RIGHT);
+ assertWithMessage("It must be position left for bottom overlay.")
+ .that(getOverlayPosition(new Rect(
+ TASK_BOUNDS.left,
+ TASK_BOUNDS.bottom / 2,
+ TASK_BOUNDS.right,
+ TASK_BOUNDS.bottom), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_BOTTOM);
+ assertWithMessage("It must be position left for shrunk bottom overlay.")
+ .that(getOverlayPosition(new Rect(
+ TASK_BOUNDS.left + 20,
+ TASK_BOUNDS.bottom / 20,
+ TASK_BOUNDS.right - 20,
+ TASK_BOUNDS.bottom), TASK_BOUNDS)).isEqualTo(CONTAINER_POSITION_BOTTOM);
+ }
+
/**
* A simplified version of {@link SplitController#createOrUpdateOverlayTaskFragmentIfNeeded}
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index 5a42817..d270d2b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -265,7 +265,7 @@
for (TransitionInfo.Change change : openingChanges) {
final Animation animation =
animationProvider.get(info, change, openingWholeScreenBounds);
- if (animation.getDuration() == 0) {
+ if (shouldUseJumpCutForAnimation(animation)) {
continue;
}
final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
@@ -290,7 +290,7 @@
}
final Animation animation =
animationProvider.get(info, change, closingWholeScreenBounds);
- if (animation.getDuration() == 0) {
+ if (shouldUseJumpCutForAnimation(animation)) {
continue;
}
final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
@@ -444,8 +444,16 @@
calculateParentBounds(change, boundsAnimationChange, parentBounds);
// There are two animations in the array. The first one is for the start leash
// (snapshot), and the second one is for the end leash (TaskFragment).
- final Animation[] animations = mAnimationSpec.createChangeBoundsChangeAnimations(change,
- parentBounds);
+ final Animation[] animations =
+ mAnimationSpec.createChangeBoundsChangeAnimations(info, change, parentBounds);
+ // Jump cut if either animation has zero for duration.
+ if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
+ for (Animation animation : animations) {
+ if (shouldUseJumpCutForAnimation(animation)) {
+ return new ArrayList<>();
+ }
+ }
+ }
// Keep track as we might need to add background color for the animation.
// Although there may be multiple change animation, record one of them is sufficient
// because the background color will be added to the root leash for the whole animation.
@@ -492,12 +500,19 @@
// window without bounds change.
animation = ActivityEmbeddingAnimationSpec.createNoopAnimation(change);
} else if (TransitionUtil.isClosingType(change.getMode())) {
- animation = mAnimationSpec.createChangeBoundsCloseAnimation(change, parentBounds);
+ animation =
+ mAnimationSpec.createChangeBoundsCloseAnimation(info, change, parentBounds);
shouldShowBackgroundColor = false;
} else {
- animation = mAnimationSpec.createChangeBoundsOpenAnimation(change, parentBounds);
+ animation =
+ mAnimationSpec.createChangeBoundsOpenAnimation(info, change, parentBounds);
shouldShowBackgroundColor = false;
}
+ if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
+ if (shouldUseJumpCutForAnimation(animation)) {
+ return new ArrayList<>();
+ }
+ }
adapters.add(new ActivityEmbeddingAnimationAdapter(animation, change,
TransitionUtil.getRootFor(change, info)));
}
@@ -640,6 +655,12 @@
return true;
}
+ /** Whether or not to use jump cut based on the animation. */
+ @VisibleForTesting
+ static boolean shouldUseJumpCutForAnimation(@NonNull Animation animation) {
+ return animation.getDuration() == 0;
+ }
+
/** Updates the changes to end states in {@code startTransaction} for jump cut animation. */
private void prepareForJumpCut(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index 8d49614..f49b90d0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -46,7 +46,6 @@
import com.android.wm.shell.shared.TransitionUtil;
/** Animation spec for ActivityEmbedding transition. */
-// TODO(b/206557124): provide an easier way to customize animation
class ActivityEmbeddingAnimationSpec {
private static final String TAG = "ActivityEmbeddingAnimSpec";
@@ -95,8 +94,14 @@
/** Animation for window that is opening in a change transition. */
@NonNull
- Animation createChangeBoundsOpenAnimation(@NonNull TransitionInfo.Change change,
- @NonNull Rect parentBounds) {
+ Animation createChangeBoundsOpenAnimation(@NonNull TransitionInfo info,
+ @NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {
+ if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
+ final Animation customAnimation = loadCustomAnimation(info, change);
+ if (customAnimation != null) {
+ return customAnimation;
+ }
+ }
// Use end bounds for opening.
final Rect bounds = change.getEndAbsBounds();
final int startLeft;
@@ -123,8 +128,14 @@
/** Animation for window that is closing in a change transition. */
@NonNull
- Animation createChangeBoundsCloseAnimation(@NonNull TransitionInfo.Change change,
- @NonNull Rect parentBounds) {
+ Animation createChangeBoundsCloseAnimation(@NonNull TransitionInfo info,
+ @NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {
+ if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
+ final Animation customAnimation = loadCustomAnimation(info, change);
+ if (customAnimation != null) {
+ return customAnimation;
+ }
+ }
// Use start bounds for closing.
final Rect bounds = change.getStartAbsBounds();
final int endTop;
@@ -155,8 +166,17 @@
* the second one is for the end leash.
*/
@NonNull
- Animation[] createChangeBoundsChangeAnimations(@NonNull TransitionInfo.Change change,
- @NonNull Rect parentBounds) {
+ Animation[] createChangeBoundsChangeAnimations(@NonNull TransitionInfo info,
+ @NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {
+ if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
+ // TODO(b/293658614): Support more complicated animations that may need more than a noop
+ // animation as the start leash.
+ final Animation noopAnimation = createNoopAnimation(change);
+ final Animation customAnimation = loadCustomAnimation(info, change);
+ if (customAnimation != null) {
+ return new Animation[]{noopAnimation, customAnimation};
+ }
+ }
// Both start bounds and end bounds are in screen coordinates. We will post translate
// to the local coordinates in ActivityEmbeddingAnimationAdapter#onAnimationUpdate
final Rect startBounds = change.getStartAbsBounds();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
index 731f75bf..55b6bd2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
@@ -21,10 +21,12 @@
import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
import static com.android.wm.shell.activityembedding.ActivityEmbeddingAnimationRunner.calculateParentBounds;
+import static com.android.wm.shell.activityembedding.ActivityEmbeddingAnimationRunner.shouldUseJumpCutForAnimation;
import static com.android.wm.shell.transition.Transitions.TRANSIT_TASK_FRAGMENT_DRAG_RESIZE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
@@ -40,6 +42,8 @@
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
import android.window.TransitionInfo;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -281,6 +285,18 @@
actualParentBounds);
}
+ @Test
+ public void testShouldUseJumpCutForAnimation() {
+ final Animation noopAnimation = new AlphaAnimation(0f, 1f);
+ assertTrue("Animation without duration should use jump cut.",
+ shouldUseJumpCutForAnimation(noopAnimation));
+
+ final Animation alphaAnimation = new AlphaAnimation(0f, 1f);
+ alphaAnimation.setDuration(100);
+ assertFalse("Animation with duration should not use jump cut.",
+ shouldUseJumpCutForAnimation(alphaAnimation));
+ }
+
@NonNull
private static TransitionInfo.Change prepareChangeForParentBoundsCalculationTest(
@NonNull Point endRelOffset, @NonNull Rect endAbsBounds, @NonNull Point endParentSize) {
diff --git a/media/tests/MediaFrameworkTest/Android.bp b/media/tests/MediaFrameworkTest/Android.bp
index 1325fc1..028c97e 100644
--- a/media/tests/MediaFrameworkTest/Android.bp
+++ b/media/tests/MediaFrameworkTest/Android.bp
@@ -24,6 +24,7 @@
"flag-junit",
"testng",
"truth",
+ "collector-device-lib-platform",
],
jni_libs: [
"libdexmakerjvmtiagent",
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
index 727c61c..a7e0464 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.kt
@@ -498,7 +498,7 @@
): Job =
coroutineScope.launch {
val wifiManager = context.getSystemService(WifiManager::class.java) ?: return@launch
- if (wifiManager.queryWepAllowed()) {
+ if (wifiManager.isWepSupported == true && wifiManager.queryWepAllowed()) {
onAllowed()
} else {
val intent = Intent(Intent.ACTION_MAIN).apply {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 05eb044..861c405 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -203,6 +203,12 @@
private static final String NULL_VALUE = "null";
+ // TOBO(b/312444587): remove after Test Mission 2.
+ // Bulk sync names
+ private static final String BULK_SYNC_MARKER = "aconfigd_marker/bulk_synced";
+ private static final String BULK_SYNC_TRIGGER_COUNTER =
+ "core_experiments_team_internal/BulkSyncTriggerCounterFlag__bulk_sync_trigger_counter";
+
private static final ArraySet<String> sSystemPackages = new ArraySet<>();
private final Object mWriteLock = new Object();
@@ -409,8 +415,7 @@
}
}
// TOBO(b/312444587): remove the comparison logic after Test Mission 2.
- if (mSettings.get("aconfigd_marker/bulk_synced").value.equals("true")
- && requests == null) {
+ if (requests == null) {
Map<String, AconfigdFlagInfo> aconfigdFlagMap =
AconfigdJavaUtils.listFlagsValueInNewStorage(localSocket);
compareFlagValueInNewStorage(
@@ -534,7 +539,7 @@
return null;
}
AconfigdFlagInfo flag = flagInfoDefault.get(fullFlagName);
- if (flag == null) {
+ if (flag == null || !namespace.equals(flag.getNamespace())) {
return null;
}
@@ -553,15 +558,33 @@
public ProtoOutputStream handleBulkSyncToNewStorage(
Map<String, AconfigdFlagInfo> aconfigFlagMap) {
// get marker or add marker if it does not exist
- final String bulkSyncMarkerName = new String("aconfigd_marker/bulk_synced");
- Setting markerSetting = mSettings.get(bulkSyncMarkerName);
+ Setting markerSetting = mSettings.get(BULK_SYNC_MARKER);
+ int localCounter = 0;
if (markerSetting == null) {
- markerSetting = new Setting(bulkSyncMarkerName, "false", false, "aconfig", "aconfig");
- mSettings.put(bulkSyncMarkerName, markerSetting);
+ markerSetting = new Setting(BULK_SYNC_MARKER, "0", false, "aconfig", "aconfig");
+ mSettings.put(BULK_SYNC_MARKER, markerSetting);
+ }
+ try {
+ localCounter = Integer.parseInt(markerSetting.value);
+ } catch(NumberFormatException e) {
+ // reset local counter
+ markerSetting.value = "0";
}
if (enableAconfigStorageDaemon()) {
- if (markerSetting.value.equals("true")) {
+ Setting bulkSyncCounter = mSettings.get(BULK_SYNC_TRIGGER_COUNTER);
+ int serverCounter = 0;
+ if (bulkSyncCounter != null) {
+ try {
+ serverCounter = Integer.parseInt(bulkSyncCounter.value);
+ } catch (NumberFormatException e) {
+ // reset the local value of server counter
+ bulkSyncCounter.value = "0";
+ }
+ }
+
+ boolean shouldSync = localCounter < serverCounter;
+ if (!shouldSync) {
// CASE 1, flag is on, bulk sync marker true, nothing to do
return null;
} else {
@@ -600,20 +623,12 @@
}
// mark sync has been done
- markerSetting.value = "true";
+ markerSetting.value = String.valueOf(serverCounter);
scheduleWriteIfNeededLocked();
return requests;
}
} else {
- if (markerSetting.value.equals("true")) {
- // CASE 3, flag is off, bulk sync marker true, clear the marker
- markerSetting.value = "false";
- scheduleWriteIfNeededLocked();
- return null;
- } else {
- // CASE 4, flag is off, bulk sync marker false, nothing to do
- return null;
- }
+ return null;
}
}
@@ -692,6 +707,7 @@
.setFlagName(flag.getName())
.setDefaultFlagValue(flagValue)
.setIsReadWrite(isReadWrite)
+ .setNamespace(flag.getNamespace())
.build());
}
}
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index 94aeb9b..4b4ced3 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -151,12 +151,14 @@
.setFlagName("flag1")
.setDefaultFlagValue("false")
.setIsReadWrite(true)
+ .setNamespace("test_namespace")
.build();
AconfigdFlagInfo flag2 = AconfigdFlagInfo.newBuilder()
.setPackageName("com.android.flags")
.setFlagName("flag2")
.setDefaultFlagValue("true")
.setIsReadWrite(false)
+ .setNamespace("test_namespace")
.build();
Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
@@ -1018,12 +1020,17 @@
.setFlagName("flag1")
.setDefaultFlagValue("false")
.setIsReadWrite(true)
+ .setNamespace("test_namespace")
.build();
flagInfoDefault.put(flag1.getFullFlagName(), flag1);
- // server override
+ // not the right namespace
+ assertNull(
+ settingsState.getFlagOverrideToSync(
+ "some_namespace/com.android.flags.flag1", "true", flagInfoDefault));
+ // server override
settingsState.getFlagOverrideToSync(
"test_namespace/com.android.flags.flag1", "true", flagInfoDefault);
assertEquals("com.android.flags", flag1.getPackageName());
@@ -1079,21 +1086,45 @@
.setIsReadWrite(false)
.build());
+ String bulkSyncMarker = "aconfigd_marker/bulk_synced";
+ String bulkSyncCounter =
+ "core_experiments_team_internal/" +
+ "BulkSyncTriggerCounterFlag__bulk_sync_trigger_counter";
+
synchronized (lock) {
- settingsState.insertSettingLocked(
- "aconfigd_marker/bulk_synced", "false", null, false, "aconfig");
+ settingsState.insertSettingLocked(bulkSyncMarker, "0", null, false, "aconfig");
+ settingsState.insertSettingLocked(bulkSyncCounter, "1", null, false,
+ "com.google.android.platform.core_experiments_team_internal");
// first bulk sync
ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(flags);
assertTrue(requests != null);
String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
- assertEquals("true", value);
+ assertEquals("1", value);
// send time should no longer bulk sync
requests = settingsState.handleBulkSyncToNewStorage(flags);
- assertTrue(requests == null);
+ assertNull(requests);
value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
- assertEquals("true", value);
+ assertEquals("1", value);
+
+ // won't sync if the marker is string
+ settingsState.insertSettingLocked(bulkSyncMarker, "true", null, false, "aconfig");
+ settingsState.insertSettingLocked(bulkSyncCounter, "0", null, false,
+ "com.google.android.platform.core_experiments_team_internal");
+ requests = settingsState.handleBulkSyncToNewStorage(flags);
+ assertNull(requests);
+ value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
+ assertEquals("0", value);
+
+ // won't sync if the marker and counter value are the same
+ settingsState.insertSettingLocked(bulkSyncMarker, "1", null, false, "aconfig");
+ settingsState.insertSettingLocked(bulkSyncCounter, "1", null, false,
+ "com.google.android.platform.core_experiments_team_internal");
+ requests = settingsState.handleBulkSyncToNewStorage(flags);
+ assertNull(requests);
+ value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
+ assertEquals("1", value);
}
}
@@ -1107,21 +1138,34 @@
SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
Map<String, AconfigdFlagInfo> flags = new HashMap<>();
+ String bulkSyncMarker = "aconfigd_marker/bulk_synced";
+ String bulkSyncCounter =
+ "core_experiments_team_internal/" +
+ "BulkSyncTriggerCounterFlag__bulk_sync_trigger_counter";
synchronized (lock) {
settingsState.insertSettingLocked("aconfigd_marker/bulk_synced",
"true", null, false, "aconfig");
// when aconfigd is off, should change the marker to false
ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(flags);
- assertTrue(requests == null);
+ assertNull(requests);
String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
- assertEquals("false", value);
+ assertEquals("0", value);
// marker started with false value, after call, it should remain false
requests = settingsState.handleBulkSyncToNewStorage(flags);
- assertTrue(requests == null);
+ assertNull(requests);
value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
- assertEquals("false", value);
+ assertEquals("0", value);
+
+ // won't sync
+ settingsState.insertSettingLocked(bulkSyncMarker, "0", null, false, "aconfig");
+ settingsState.insertSettingLocked(bulkSyncCounter, "1", null, false,
+ "com.google.android.platform.core_experiments_team_internal");
+ requests = settingsState.handleBulkSyncToNewStorage(flags);
+ assertNull(requests);
+ value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
+ assertEquals("0", value);
}
}
@@ -1164,6 +1208,7 @@
.setFlagName("flag1")
.setDefaultFlagValue("false")
.setIsReadWrite(true)
+ .setNamespace("test_namespace")
.build();
flagInfoDefault.put(flag1.getFullFlagName(), flag1);
@@ -1186,6 +1231,7 @@
.setFlagName("flag2")
.setDefaultFlagValue("false")
.setIsReadWrite(true)
+ .setNamespace("test_namespace")
.build();
flagInfoDefault.put(flag2.getFullFlagName(), flag2);
synchronized (lock) {
@@ -1207,6 +1253,7 @@
.setFlagName("flag3")
.setDefaultFlagValue("false")
.setIsReadWrite(false)
+ .setNamespace("test_namespace")
.build();
flagInfoDefault.put(flag3.getFullFlagName(), flag3);
synchronized (lock) {
diff --git a/packages/SystemUI/res/color/connected_network_primary_color.xml b/packages/SystemUI/res/color/connected_network_primary_color.xml
new file mode 100644
index 0000000..f173c8d
--- /dev/null
+++ b/packages/SystemUI/res/color/connected_network_primary_color.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:color="?androidprv:attr/materialColorOnPrimaryContainer" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml
index 250188b..fab2d8d 100644
--- a/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml
+++ b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml
@@ -16,10 +16,11 @@
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:color="?android:attr/colorControlHighlight">
<item>
<shape android:shape="rectangle">
- <solid android:color="@color/settingslib_state_on_color"/>
+ <solid android:color="?androidprv:attr/materialColorPrimaryContainer"/>
<corners android:radius="@dimen/settingslib_switch_bar_radius"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_on.xml b/packages/SystemUI/res/drawable/settingslib_thumb_on.xml
index 5566ea3..e316a93 100644
--- a/packages/SystemUI/res/drawable/settingslib_thumb_on.xml
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_on.xml
@@ -15,7 +15,8 @@
limitations under the License.
-->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item
android:top="@dimen/settingslib_switch_thumb_margin"
android:bottom="@dimen/settingslib_switch_thumb_margin">
@@ -23,7 +24,7 @@
<size
android:height="@dimen/settingslib_switch_thumb_size"
android:width="@dimen/settingslib_switch_thumb_size"/>
- <solid android:color="@color/settingslib_state_on_color"/>
+ <solid android:color="?androidprv:attr/materialColorOnPrimary"/>
</shape>
</item>
</layer-list>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_on_background.xml b/packages/SystemUI/res/drawable/settingslib_track_on_background.xml
index 1d9dacd..e2e6468 100644
--- a/packages/SystemUI/res/drawable/settingslib_track_on_background.xml
+++ b/packages/SystemUI/res/drawable/settingslib_track_on_background.xml
@@ -16,11 +16,12 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle"
android:width="@dimen/settingslib_switch_track_width"
android:height="@dimen/settingslib_switch_track_height">
<padding android:left="@dimen/settingslib_switch_thumb_margin"
android:right="@dimen/settingslib_switch_thumb_margin"/>
- <solid android:color="@color/settingslib_track_on_color"/>
+ <solid android:color="?androidprv:attr/materialColorPrimary"/>
<corners android:radius="@dimen/settingslib_switch_track_radius"/>
</shape>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index d377e01..21f1cfb 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -104,10 +104,6 @@
<color name="people_tile_background">@color/material_dynamic_secondary20</color>
- <!-- Internet Dialog -->
- <color name="connected_network_primary_color">@color/material_dynamic_primary80</color>
- <color name="connected_network_secondary_color">@color/material_dynamic_secondary80</color>
-
<!-- Keyboard shortcut helper dialog -->
<color name="ksh_key_item_color">@*android:color/system_on_surface_variant_dark</color>
</resources>
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 546bf1c..e9dd039f3 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -60,18 +60,6 @@
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
</style>
- <style name="TextAppearance.InternetDialog.Active">
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textSize">16sp</item>
- <item name="android:textColor">@color/material_dynamic_primary80</item>
- <item name="android:textDirection">locale</item>
- </style>
-
- <style name="TextAppearance.InternetDialog.Secondary.Active">
- <item name="android:textSize">14sp</item>
- <item name="android:textColor">@color/material_dynamic_secondary80</item>
- </style>
-
<style name="ShortcutHelperTheme" parent="@style/ShortcutHelperThemeCommon">
<item name="android:windowLightNavigationBar">false</item>
</style>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index b3d3021..0350cd7 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -236,11 +236,8 @@
<!-- Internet Dialog -->
<!-- Material next state on color-->
<color name="settingslib_state_on_color">@color/settingslib_state_on</color>
- <!-- Material next track on color-->
- <color name="settingslib_track_on_color">@color/settingslib_track_on</color>
<!-- Material next track off color-->
<color name="settingslib_track_off_color">@color/settingslib_track_off</color>
- <color name="connected_network_primary_color">#191C18</color>
<color name="connected_network_secondary_color">#41493D</color>
<color name="dream_overlay_camera_mic_off_dot_color">#FCBE03</color>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 73b7586..7475eb2 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -1315,7 +1315,7 @@
<item name="android:background">?android:attr/selectableItemBackground</item>
</style>
- <style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault">
+ <style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault.DayNight">
<item name="android:switchMinWidth">@dimen/settingslib_min_switch_width</item>
</style>
@@ -1358,6 +1358,7 @@
<style name="InternetDialog.NetworkTitle.Active">
<item name="android:textAppearance">@style/TextAppearance.InternetDialog.Active</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item>
</style>
<style name="InternetDialog.NetworkSummary">
@@ -1370,18 +1371,19 @@
<style name="InternetDialog.NetworkSummary.Active">
<item name="android:textAppearance">@style/TextAppearance.InternetDialog.Secondary.Active
</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item>
</style>
<style name="TextAppearance.InternetDialog">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:textSize">16sp</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
<item name="android:textDirection">locale</item>
</style>
<style name="TextAppearance.InternetDialog.Secondary">
<item name="android:textSize">14sp</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
</style>
<style name="TextAppearance.InternetDialog.Active"/>
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
index fec6ff1..0458f53 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
@@ -28,6 +28,9 @@
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.util.concurrent.CopyOnWriteArrayList
+import java.util.concurrent.atomic.AtomicInteger
+
+private const val INVALID_ROTATION = -1
/**
* Allows to subscribe to rotation changes. Updates are provided for the display associated to
@@ -45,7 +48,7 @@
private val listeners = CopyOnWriteArrayList<RotationListener>()
private val displayListener = RotationDisplayListener()
- private var lastRotation: Int? = null
+ private val lastRotation = AtomicInteger(INVALID_ROTATION)
override fun addCallback(listener: RotationListener) {
bgHandler.post {
@@ -61,7 +64,7 @@
listeners -= listener
if (listeners.isEmpty()) {
unsubscribeToRotation()
- lastRotation = null
+ lastRotation.set(INVALID_ROTATION)
}
}
}
@@ -100,9 +103,8 @@
if (displayId == display.displayId) {
val currentRotation = display.rotation
- if (lastRotation == null || lastRotation != currentRotation) {
+ if (lastRotation.compareAndSet(lastRotation.get(), currentRotation)) {
listeners.forEach { it.onRotationChanged(currentRotation) }
- lastRotation = currentRotation
}
}
} finally {
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 18a9986..886857c 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -1079,6 +1079,21 @@
}
@Override // Binder call
+ public void finishSelfOneway(IBinder token, boolean immediate) {
+ // Requires no permission, called by Dream from an arbitrary process.
+ if (token == null) {
+ throw new IllegalArgumentException("token must not be null");
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ finishSelfInternal(token, immediate);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
public void startDozing(
IBinder token, int screenState, @Display.StateReason int reason,
int screenBrightness) {
@@ -1096,6 +1111,23 @@
}
@Override // Binder call
+ public void startDozingOneway(
+ IBinder token, int screenState, @Display.StateReason int reason,
+ int screenBrightness) {
+ // Requires no permission, called by Dream from an arbitrary process.
+ if (token == null) {
+ throw new IllegalArgumentException("token must not be null");
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ startDozingInternal(token, screenState, reason, screenBrightness);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
public void stopDozing(IBinder token) {
// Requires no permission, called by Dream from an arbitrary process.
if (token == null) {
diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
index aab3374..356bc40 100644
--- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
@@ -53,10 +53,10 @@
import java.util.Objects;
/**
- * The default implementation of {@link ImeVisibilityApplier} used in
- * {@link InputMethodManagerService}.
+ * A stateless helper class for IME visibility operations like show/hide and update Z-ordering
+ * relative to the IME targeted window.
*/
-final class DefaultImeVisibilityApplier implements ImeVisibilityApplier {
+final class DefaultImeVisibilityApplier {
private static final String TAG = "DefaultImeVisibilityApplier";
@@ -75,9 +75,18 @@
mImeTargetVisibilityPolicy = LocalServices.getService(ImeTargetVisibilityPolicy.class);
}
+ /**
+ * Performs showing IME on top of the given window.
+ *
+ * @param showInputToken a token that represents the requester to show IME
+ * @param statsToken the token tracking the current IME request
+ * @param resultReceiver if non-null, this will be called back to the caller when
+ * it has processed request to tell what it has done
+ * @param reason yhe reason for requesting to show IME
+ * @param userId the target user when performing show IME
+ */
@GuardedBy("ImfLock.class")
- @Override
- public void performShowIme(IBinder showInputToken, @NonNull ImeTracker.Token statsToken,
+ void performShowIme(IBinder showInputToken, @NonNull ImeTracker.Token statsToken,
@InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason, @UserIdInt int userId) {
final var bindingController = mService.getInputMethodBindingController(userId);
@@ -105,9 +114,18 @@
}
}
+ /**
+ * Performs hiding IME to the given window
+ *
+ * @param hideInputToken a token that represents the requester to hide IME
+ * @param statsToken the token tracking the current IME request
+ * @param resultReceiver if non-null, this will be called back to the caller when
+ * it has processed request to tell what it has done
+ * @param reason the reason for requesting to hide IME
+ * @param userId the target user when performing hide IME
+ */
@GuardedBy("ImfLock.class")
- @Override
- public void performHideIme(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken,
+ void performHideIme(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason,
@UserIdInt int userId) {
final var bindingController = mService.getInputMethodBindingController(userId);
@@ -139,9 +157,18 @@
}
}
+ /**
+ * Applies the IME visibility from {@link android.inputmethodservice.InputMethodService} with
+ * according to the given visibility state.
+ *
+ * @param windowToken the token of a window for applying the IME visibility
+ * @param statsToken the token tracking the current IME request
+ * @param state the new IME visibility state for the applier to handle
+ * @param reason one of {@link SoftInputShowHideReason}
+ * @param userId the target user when applying the IME visibility state
+ */
@GuardedBy("ImfLock.class")
- @Override
- public void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
+ void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
@ImeVisibilityStateComputer.VisibilityState int state,
@SoftInputShowHideReason int reason, @UserIdInt int userId) {
final var bindingController = mService.getInputMethodBindingController(userId);
@@ -211,9 +238,16 @@
}
}
+ /**
+ * Shows the IME screenshot and attach it to the given IME target window.
+ *
+ * @param imeTarget the token of a window to show the IME screenshot
+ * @param displayId the unique id to identify the display
+ * @param userId the target user when when showing the IME screenshot
+ * @return {@code true} if success, {@code false} otherwise
+ */
@GuardedBy("ImfLock.class")
- @Override
- public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId,
+ boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId,
@UserIdInt int userId) {
if (mImeTargetVisibilityPolicy.showImeScreenshot(imeTarget, displayId)) {
mService.onShowHideSoftInputRequested(false /* show */, imeTarget,
@@ -223,9 +257,15 @@
return false;
}
+ /**
+ * Removes the IME screenshot on the given display.
+ *
+ * @param displayId the target display of showing IME screenshot
+ * @param userId the target user of showing IME screenshot
+ * @return {@code true} if success, {@code false} otherwise
+ */
@GuardedBy("ImfLock.class")
- @Override
- public boolean removeImeScreenshot(int displayId, @UserIdInt int userId) {
+ boolean removeImeScreenshot(int displayId, @UserIdInt int userId) {
final var userData = mService.getUserData(userId);
if (mImeTargetVisibilityPolicy.removeImeScreenshot(displayId)) {
mService.onShowHideSoftInputRequested(false /* show */,
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
deleted file mode 100644
index b693a66..0000000
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2022 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.inputmethod;
-
-import android.annotation.NonNull;
-import android.annotation.UserIdInt;
-import android.os.IBinder;
-import android.os.ResultReceiver;
-import android.view.inputmethod.ImeTracker;
-import android.view.inputmethod.InputMethod;
-
-import com.android.internal.inputmethod.SoftInputShowHideReason;
-
-/**
- * Interface for IME visibility operations like show/hide and update Z-ordering relative to the IME
- * targeted window.
- */
-interface ImeVisibilityApplier {
- /**
- * Performs showing IME on top of the given window.
- *
- * @param showInputToken a token that represents the requester to show IME
- * @param statsToken the token tracking the current IME request
- * @param resultReceiver if non-null, this will be called back to the caller when
- * it has processed request to tell what it has done
- * @param reason yhe reason for requesting to show IME
- * @param userId the target user when performing show IME
- */
- default void performShowIme(IBinder showInputToken, @NonNull ImeTracker.Token statsToken,
- @InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason, @UserIdInt int userId) {
- }
-
- /**
- * Performs hiding IME to the given window
- *
- * @param hideInputToken a token that represents the requester to hide IME
- * @param statsToken the token tracking the current IME request
- * @param resultReceiver if non-null, this will be called back to the caller when
- * it has processed request to tell what it has done
- * @param reason the reason for requesting to hide IME
- * @param userId the target user when performing hide IME
- */
- default void performHideIme(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason,
- @UserIdInt int userId) {
- }
-
- /**
- * Applies the IME visibility from {@link android.inputmethodservice.InputMethodService} with
- * according to the given visibility state.
- *
- * @param windowToken the token of a window for applying the IME visibility
- * @param statsToken the token tracking the current IME request
- * @param state the new IME visibility state for the applier to handle
- * @param reason the reason why the input window is visible or hidden
- * @param userId the target user when applying the IME visibility state
- */
- default void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
- @ImeVisibilityStateComputer.VisibilityState int state,
- @SoftInputShowHideReason int reason, @UserIdInt int userId) {
- }
-
- /**
- * Updates the IME Z-ordering relative to the given window.
- *
- * This used to adjust the IME relative layer of the window during
- * {@link InputMethodManagerService} is in switching IME clients.
- *
- * @param windowToken the token of a window to update the Z-ordering relative to the IME
- */
- default void updateImeLayeringByTarget(IBinder windowToken) {
- // TODO: add a method in WindowManagerInternal to call DC#updateImeInputAndControlTarget
- // here to end up updating IME layering after IMMS#attachNewInputLocked called.
- }
-
- /**
- * Shows the IME screenshot and attach it to the given IME target window.
- *
- * @param windowToken the token of a window to show the IME screenshot
- * @param displayId the unique id to identify the display
- * @param userId the target user when when showing the IME screenshot
- * @return {@code true} if success, {@code false} otherwise
- */
- default boolean showImeScreenshot(@NonNull IBinder windowToken, int displayId,
- @UserIdInt int userId) {
- return false;
- }
-
- /**
- * Removes the IME screenshot on the given display.
- *
- * @param displayId the target display of showing IME screenshot
- * @param userId the target user of showing IME screenshot
- * @return {@code true} if success, {@code false} otherwise
- */
- default boolean removeImeScreenshot(int displayId, @UserIdInt int userId) {
- return false;
- }
-}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 56ac760..1e76324 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4045,7 +4045,7 @@
if (!calledWithValidTokenLocked(token, userId)) {
return;
}
- final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
final InputMethodInfo imi = settings.getMethodMap().get(id);
if (imi == null || !canCallerAccessInputMethod(
imi.getPackageName(), callingUid, userId, settings)) {
@@ -4063,7 +4063,7 @@
if (!calledWithValidTokenLocked(token, userId)) {
return;
}
- final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
final InputMethodInfo imi = settings.getMethodMap().get(id);
if (imi == null || !canCallerAccessInputMethod(
imi.getPackageName(), callingUid, userId, settings)) {
@@ -4824,11 +4824,10 @@
}
}
+ @GuardedBy("ImfLock.class")
@VisibleForTesting
- ImeVisibilityApplier getVisibilityApplier() {
- synchronized (ImfLock.class) {
- return mVisibilityApplier;
- }
+ DefaultImeVisibilityApplier getVisibilityApplierLocked() {
+ return mVisibilityApplier;
}
void onApplyImeVisibilityFromComputer(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 6abd488..76fd57b 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2158,6 +2158,12 @@
// Use Task#setBoundsUnchecked to skip checking windowing mode as the windowing mode
// will be updated later after this is collected in transition.
rootTask.setBoundsUnchecked(taskFragment.getBounds());
+ // The exit-PIP activity resumes early for seamless transition. In certain
+ // scenarios, this introduces unintended addition to recents. To address this,
+ // we mark the root task for automatic removal from recents. This ensures that
+ // after the pinned activity reparents to its original task, the root task is
+ // automatically removed from the recents list.
+ rootTask.autoRemoveRecents = true;
// Move the last recents animation transaction from original task to the new one.
if (task.mLastRecentsAnimationTransaction != null) {
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index f8e196e..33ea9b4 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -74,9 +74,8 @@
@Before
public void setUp() throws RemoteException {
super.setUp();
- mVisibilityApplier =
- (DefaultImeVisibilityApplier) mInputMethodManagerService.getVisibilityApplier();
synchronized (ImfLock.class) {
+ mVisibilityApplier = mInputMethodManagerService.getVisibilityApplierLocked();
mUserId = mInputMethodManagerService.getCurrentImeUserIdLocked();
mInputMethodManagerService.setAttachedClientForTesting(requireNonNull(
mInputMethodManagerService.getClientStateLocked(mMockInputMethodClient)));
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
index a22cacb..337d5c1 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
@@ -58,8 +58,8 @@
import org.mockito.ArgumentCaptor;
/**
- * Test the behavior of {@link ImeVisibilityStateComputer} and {@link ImeVisibilityApplier} when
- * requesting the IME visibility.
+ * Test the behavior of {@link ImeVisibilityStateComputer} and {@link DefaultImeVisibilityApplier}
+ * when requesting the IME visibility.
*
* <p> Build/Install/Run:
* atest FrameworksInputMethodSystemServerTests:ImeVisibilityStateComputerTest
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index eb79118..3078df0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -392,6 +392,8 @@
assertEquals(newPipTask, mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask());
assertNotEquals(newPipTask, activity1.getTask());
assertFalse("Created PiP task must not be in recents", newPipTask.inRecents);
+ assertThat(newPipTask.autoRemoveRecents).isTrue();
+ assertThat(activity1.getTask().autoRemoveRecents).isFalse();
}
/**
@@ -427,6 +429,7 @@
bounds.scale(0.5f);
task.setBounds(bounds);
assertFalse(activity.isLetterboxedForFixedOrientationAndAspectRatio());
+ assertThat(task.autoRemoveRecents).isFalse();
}
/**
@@ -451,6 +454,7 @@
// Ensure a task has moved over.
ensureTaskPlacement(task, activity);
assertTrue(task.inPinnedWindowingMode());
+ assertThat(task.autoRemoveRecents).isFalse();
}
/**
@@ -480,6 +484,8 @@
ensureTaskPlacement(fullscreenTask, secondActivity);
assertTrue(pinnedRootTask.inPinnedWindowingMode());
assertEquals(WINDOWING_MODE_FULLSCREEN, fullscreenTask.getWindowingMode());
+ assertThat(pinnedRootTask.autoRemoveRecents).isTrue();
+ assertThat(secondActivity.getTask().autoRemoveRecents).isFalse();
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index e01cea3..ef0aa9e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -42,6 +42,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.android.server.wm.TaskFragment.EMBEDDED_DIM_AREA_PARENT_TASK;
import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
@@ -222,6 +223,27 @@
}
@Test
+ public void testReparentPinnedActivityBackToOriginalTask() {
+ final ActivityRecord activityMain = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task originalTask = activityMain.getTask();
+ final ActivityRecord activityPip = new ActivityBuilder(mAtm).setTask(originalTask).build();
+ activityPip.setState(RESUMED, "test");
+ mAtm.mRootWindowContainer.moveActivityToPinnedRootTask(activityPip,
+ null /* launchIntoPipHostActivity */, "test");
+ final Task pinnedActivityTask = activityPip.getTask();
+
+ // Simulate pinnedActivityTask unintentionally added to recent during top activity resume.
+ mAtm.getRecentTasks().getRawTasks().add(pinnedActivityTask);
+
+ // Reparent the activity back to its original task when exiting PIP mode.
+ pinnedActivityTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ assertThat(activityPip.getTask()).isEqualTo(originalTask);
+ assertThat(originalTask.autoRemoveRecents).isFalse();
+ assertThat(mAtm.getRecentTasks().getRawTasks()).containsExactly(originalTask);
+ }
+
+ @Test
public void testReparent_BetweenDisplays() {
// Create first task on primary display.
final Task rootTask1 = createTask(mDisplayContent);