Merge "Synchronize screen turning on and unfold overlay" into sc-v2-dev am: 6ba778585f
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15920768 Change-Id: I707d47888d0ed436f247287474273b42f5463c5e
This commit is contained in:
@@ -292,11 +292,18 @@ public class SurfaceControlViewHost {
|
|||||||
*/
|
*/
|
||||||
@TestApi
|
@TestApi
|
||||||
public void relayout(WindowManager.LayoutParams attrs) {
|
public void relayout(WindowManager.LayoutParams attrs) {
|
||||||
|
relayout(attrs, SurfaceControl.Transaction::apply);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces relayout and draw and allows to set a custom callback when it is finished
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public void relayout(WindowManager.LayoutParams attrs,
|
||||||
|
WindowlessWindowManager.ResizeCompleteCallback callback) {
|
||||||
mViewRoot.setLayoutParams(attrs, false);
|
mViewRoot.setLayoutParams(attrs, false);
|
||||||
mViewRoot.setReportNextDraw();
|
mViewRoot.setReportNextDraw();
|
||||||
mWm.setCompletionCallback(mViewRoot.mWindow.asBinder(), (SurfaceControl.Transaction t) -> {
|
mWm.setCompletionCallback(mViewRoot.mWindow.asBinder(), callback);
|
||||||
t.apply();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.wm.shell;
|
||||||
|
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.view.SurfaceControl;
|
||||||
|
import android.window.DisplayAreaAppearedInfo;
|
||||||
|
import android.window.DisplayAreaInfo;
|
||||||
|
import android.window.DisplayAreaOrganizer;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/** Display area organizer for the root display areas */
|
||||||
|
public class RootDisplayAreaOrganizer extends DisplayAreaOrganizer {
|
||||||
|
|
||||||
|
private static final String TAG = RootDisplayAreaOrganizer.class.getSimpleName();
|
||||||
|
|
||||||
|
/** {@link DisplayAreaInfo} list, which is mapped by display IDs. */
|
||||||
|
private final SparseArray<DisplayAreaInfo> mDisplayAreasInfo = new SparseArray<>();
|
||||||
|
/** Display area leashes, which is mapped by display IDs. */
|
||||||
|
private final SparseArray<SurfaceControl> mLeashes = new SparseArray<>();
|
||||||
|
|
||||||
|
public RootDisplayAreaOrganizer(Executor executor) {
|
||||||
|
super(executor);
|
||||||
|
List<DisplayAreaAppearedInfo> infos = registerOrganizer(FEATURE_ROOT);
|
||||||
|
for (int i = infos.size() - 1; i >= 0; --i) {
|
||||||
|
onDisplayAreaAppeared(infos.get(i).getDisplayAreaInfo(), infos.get(i).getLeash());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attachToDisplayArea(int displayId, SurfaceControl.Builder b) {
|
||||||
|
final SurfaceControl sc = mLeashes.get(displayId);
|
||||||
|
if (sc != null) {
|
||||||
|
b.setParent(sc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo,
|
||||||
|
@NonNull SurfaceControl leash) {
|
||||||
|
if (displayAreaInfo.featureId != FEATURE_ROOT) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Unknown feature: " + displayAreaInfo.featureId
|
||||||
|
+ "displayAreaInfo:" + displayAreaInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int displayId = displayAreaInfo.displayId;
|
||||||
|
if (mDisplayAreasInfo.get(displayId) != null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Duplicate DA for displayId: " + displayId
|
||||||
|
+ " displayAreaInfo:" + displayAreaInfo
|
||||||
|
+ " mDisplayAreasInfo.get():" + mDisplayAreasInfo.get(displayId));
|
||||||
|
}
|
||||||
|
|
||||||
|
mDisplayAreasInfo.put(displayId, displayAreaInfo);
|
||||||
|
mLeashes.put(displayId, leash);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisplayAreaVanished(@NonNull DisplayAreaInfo displayAreaInfo) {
|
||||||
|
final int displayId = displayAreaInfo.displayId;
|
||||||
|
if (mDisplayAreasInfo.get(displayId) == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"onDisplayAreaVanished() Unknown DA displayId: " + displayId
|
||||||
|
+ " displayAreaInfo:" + displayAreaInfo
|
||||||
|
+ " mDisplayAreasInfo.get():" + mDisplayAreasInfo.get(displayId));
|
||||||
|
}
|
||||||
|
|
||||||
|
mDisplayAreasInfo.remove(displayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisplayAreaInfoChanged(@NonNull DisplayAreaInfo displayAreaInfo) {
|
||||||
|
final int displayId = displayAreaInfo.displayId;
|
||||||
|
if (mDisplayAreasInfo.get(displayId) == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"onDisplayAreaInfoChanged() Unknown DA displayId: " + displayId
|
||||||
|
+ " displayAreaInfo:" + displayAreaInfo
|
||||||
|
+ " mDisplayAreasInfo.get():" + mDisplayAreasInfo.get(displayId));
|
||||||
|
}
|
||||||
|
|
||||||
|
mDisplayAreasInfo.put(displayId, displayAreaInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dump(@NonNull PrintWriter pw, String prefix) {
|
||||||
|
final String innerPrefix = prefix + " ";
|
||||||
|
final String childPrefix = innerPrefix + " ";
|
||||||
|
pw.println(prefix + this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return TAG + "#" + mDisplayAreasInfo.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.wm.shell.displayareahelper;
|
||||||
|
|
||||||
|
import android.view.SurfaceControl;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that allows to perform various display area related actions
|
||||||
|
*/
|
||||||
|
public interface DisplayAreaHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates SurfaceControl builder to reparent it to the root display area
|
||||||
|
* @param displayId id of the display to which root display area it should be reparented to
|
||||||
|
* @param builder surface control builder that should be updated
|
||||||
|
* @param onUpdated callback that is invoked after updating the builder, called on
|
||||||
|
* the shell main thread
|
||||||
|
*/
|
||||||
|
default void attachToRootDisplayArea(int displayId, SurfaceControl.Builder builder,
|
||||||
|
Consumer<SurfaceControl.Builder> onUpdated) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.wm.shell.displayareahelper;
|
||||||
|
|
||||||
|
import android.view.SurfaceControl;
|
||||||
|
|
||||||
|
import com.android.wm.shell.RootDisplayAreaOrganizer;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class DisplayAreaHelperController implements DisplayAreaHelper {
|
||||||
|
|
||||||
|
private final Executor mExecutor;
|
||||||
|
private final RootDisplayAreaOrganizer mRootDisplayAreaOrganizer;
|
||||||
|
|
||||||
|
public DisplayAreaHelperController(Executor executor,
|
||||||
|
RootDisplayAreaOrganizer rootDisplayAreaOrganizer) {
|
||||||
|
mExecutor = executor;
|
||||||
|
mRootDisplayAreaOrganizer = rootDisplayAreaOrganizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attachToRootDisplayArea(int displayId, SurfaceControl.Builder builder,
|
||||||
|
Consumer<SurfaceControl.Builder> onUpdated) {
|
||||||
|
mExecutor.execute(() -> {
|
||||||
|
mRootDisplayAreaOrganizer.attachToDisplayArea(displayId, builder);
|
||||||
|
onUpdated.accept(builder);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
package com.android.systemui.unfold.progress
|
package com.android.systemui.unfold.progress
|
||||||
|
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
|
import android.util.Log
|
||||||
import android.util.MathUtils.saturate
|
import android.util.MathUtils.saturate
|
||||||
import androidx.dynamicanimation.animation.DynamicAnimation
|
import androidx.dynamicanimation.animation.DynamicAnimation
|
||||||
import androidx.dynamicanimation.animation.FloatPropertyCompat
|
import androidx.dynamicanimation.animation.FloatPropertyCompat
|
||||||
@@ -92,8 +93,15 @@ internal class PhysicsBasedUnfoldTransitionProgressProvider(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
FOLD_UPDATE_FINISH_FULL_OPEN -> {
|
FOLD_UPDATE_FINISH_FULL_OPEN -> {
|
||||||
|
// Do not cancel if we haven't started the transition yet.
|
||||||
|
// This could happen when we fully unfolded the device before the screen
|
||||||
|
// became available. In this case we start and immediately cancel the animation
|
||||||
|
// in FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE event handler, so we don't need to
|
||||||
|
// cancel it here.
|
||||||
|
if (isTransitionRunning) {
|
||||||
cancelTransition(endValue = 1f, animate = true)
|
cancelTransition(endValue = 1f, animate = true)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
FOLD_UPDATE_FINISH_CLOSED -> {
|
FOLD_UPDATE_FINISH_CLOSED -> {
|
||||||
cancelTransition(endValue = 0f, animate = false)
|
cancelTransition(endValue = 0f, animate = false)
|
||||||
}
|
}
|
||||||
@@ -101,6 +109,10 @@ internal class PhysicsBasedUnfoldTransitionProgressProvider(
|
|||||||
startTransition(startValue = 1f)
|
startTransition(startValue = 1f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "onFoldUpdate = $update")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cancelTransition(endValue: Float, animate: Boolean) {
|
private fun cancelTransition(endValue: Float, animate: Boolean) {
|
||||||
@@ -118,6 +130,10 @@ internal class PhysicsBasedUnfoldTransitionProgressProvider(
|
|||||||
listeners.forEach {
|
listeners.forEach {
|
||||||
it.onTransitionFinished()
|
it.onTransitionFinished()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "onTransitionFinished")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,6 +153,10 @@ internal class PhysicsBasedUnfoldTransitionProgressProvider(
|
|||||||
it.onTransitionStarted()
|
it.onTransitionStarted()
|
||||||
}
|
}
|
||||||
isTransitionRunning = true
|
isTransitionRunning = true
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "onTransitionStarted")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startTransition(startValue: Float) {
|
private fun startTransition(startValue: Float) {
|
||||||
@@ -189,6 +209,9 @@ internal class PhysicsBasedUnfoldTransitionProgressProvider(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const val TAG = "PhysicsBasedUnfoldTransitionProgressProvider"
|
||||||
|
private const val DEBUG = true
|
||||||
|
|
||||||
private const val TRANSITION_TIMEOUT_MILLIS = 2000L
|
private const val TRANSITION_TIMEOUT_MILLIS = 2000L
|
||||||
private const val SPRING_STIFFNESS = 200.0f
|
private const val SPRING_STIFFNESS = 200.0f
|
||||||
private const val MINIMAL_VISIBLE_CHANGE = 0.001f
|
private const val MINIMAL_VISIBLE_CHANGE = 0.001f
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES
|
|||||||
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
|
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
|
|
||||||
internal class DeviceFoldStateProvider(
|
class DeviceFoldStateProvider(
|
||||||
context: Context,
|
context: Context,
|
||||||
private val hingeAngleProvider: HingeAngleProvider,
|
private val hingeAngleProvider: HingeAngleProvider,
|
||||||
private val screenStatusProvider: ScreenStatusProvider,
|
private val screenStatusProvider: ScreenStatusProvider,
|
||||||
@@ -43,6 +43,7 @@ internal class DeviceFoldStateProvider(
|
|||||||
private val foldStateListener = FoldStateListener(context)
|
private val foldStateListener = FoldStateListener(context)
|
||||||
|
|
||||||
private var isFolded = false
|
private var isFolded = false
|
||||||
|
private var isUnfoldHandled = true
|
||||||
|
|
||||||
override fun start() {
|
override fun start() {
|
||||||
deviceStateManager.registerCallback(
|
deviceStateManager.registerCallback(
|
||||||
@@ -104,6 +105,7 @@ internal class DeviceFoldStateProvider(
|
|||||||
lastFoldUpdate = FOLD_UPDATE_FINISH_CLOSED
|
lastFoldUpdate = FOLD_UPDATE_FINISH_CLOSED
|
||||||
outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) }
|
outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) }
|
||||||
hingeAngleProvider.stop()
|
hingeAngleProvider.stop()
|
||||||
|
isUnfoldHandled = false
|
||||||
} else {
|
} else {
|
||||||
lastFoldUpdate = FOLD_UPDATE_START_OPENING
|
lastFoldUpdate = FOLD_UPDATE_START_OPENING
|
||||||
outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_START_OPENING) }
|
outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_START_OPENING) }
|
||||||
@@ -115,8 +117,15 @@ internal class DeviceFoldStateProvider(
|
|||||||
ScreenStatusProvider.ScreenListener {
|
ScreenStatusProvider.ScreenListener {
|
||||||
|
|
||||||
override fun onScreenTurnedOn() {
|
override fun onScreenTurnedOn() {
|
||||||
if (!isFolded) {
|
// Trigger this event only if we are unfolded and this is the first screen
|
||||||
|
// turned on event since unfold started. This prevents running the animation when
|
||||||
|
// turning on the internal display using the power button.
|
||||||
|
// Initially isUnfoldHandled is true so it will be reset to false *only* when we
|
||||||
|
// receive 'folded' event. If SystemUI started when device is already folded it will
|
||||||
|
// still receive 'folded' event on startup.
|
||||||
|
if (!isFolded && !isUnfoldHandled) {
|
||||||
outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }
|
outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }
|
||||||
|
isUnfoldHandled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import com.android.systemui.statusbar.policy.CallbackController
|
|||||||
* Allows to subscribe to main events related to fold/unfold process such as hinge angle update,
|
* Allows to subscribe to main events related to fold/unfold process such as hinge angle update,
|
||||||
* start folding/unfolding, screen availability
|
* start folding/unfolding, screen availability
|
||||||
*/
|
*/
|
||||||
internal interface FoldStateProvider : CallbackController<FoldUpdatesListener> {
|
interface FoldStateProvider : CallbackController<FoldUpdatesListener> {
|
||||||
fun start()
|
fun start()
|
||||||
fun stop()
|
fun stop()
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import com.android.systemui.statusbar.policy.CallbackController
|
|||||||
* For foldable devices usually 0 corresponds to fully closed (folded) state and
|
* For foldable devices usually 0 corresponds to fully closed (folded) state and
|
||||||
* 180 degrees corresponds to fully open (flat) state
|
* 180 degrees corresponds to fully open (flat) state
|
||||||
*/
|
*/
|
||||||
internal interface HingeAngleProvider : CallbackController<Consumer<Float>> {
|
interface HingeAngleProvider : CallbackController<Consumer<Float>> {
|
||||||
fun start()
|
fun start()
|
||||||
fun stop()
|
fun stop()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ public class SystemUIFactory {
|
|||||||
.setTaskViewFactory(mWMComponent.getTaskViewFactory())
|
.setTaskViewFactory(mWMComponent.getTaskViewFactory())
|
||||||
.setTransitions(mWMComponent.getTransitions())
|
.setTransitions(mWMComponent.getTransitions())
|
||||||
.setStartingSurface(mWMComponent.getStartingSurface())
|
.setStartingSurface(mWMComponent.getStartingSurface())
|
||||||
|
.setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
|
||||||
.setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper());
|
.setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper());
|
||||||
} else {
|
} else {
|
||||||
// TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
|
// TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
|
||||||
@@ -133,6 +134,7 @@ public class SystemUIFactory {
|
|||||||
.setAppPairs(Optional.ofNullable(null))
|
.setAppPairs(Optional.ofNullable(null))
|
||||||
.setTaskViewFactory(Optional.ofNullable(null))
|
.setTaskViewFactory(Optional.ofNullable(null))
|
||||||
.setTransitions(Transitions.createEmptyForTesting())
|
.setTransitions(Transitions.createEmptyForTesting())
|
||||||
|
.setDisplayAreaHelper(Optional.ofNullable(null))
|
||||||
.setStartingSurface(Optional.ofNullable(null))
|
.setStartingSurface(Optional.ofNullable(null))
|
||||||
.setTaskSurfaceHelper(Optional.ofNullable(null));
|
.setTaskSurfaceHelper(Optional.ofNullable(null));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import com.android.wm.shell.ShellCommandHandler;
|
|||||||
import com.android.wm.shell.TaskViewFactory;
|
import com.android.wm.shell.TaskViewFactory;
|
||||||
import com.android.wm.shell.apppairs.AppPairs;
|
import com.android.wm.shell.apppairs.AppPairs;
|
||||||
import com.android.wm.shell.bubbles.Bubbles;
|
import com.android.wm.shell.bubbles.Bubbles;
|
||||||
|
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
|
||||||
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
|
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
|
||||||
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
|
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
|
||||||
import com.android.wm.shell.onehanded.OneHanded;
|
import com.android.wm.shell.onehanded.OneHanded;
|
||||||
@@ -95,6 +96,9 @@ public interface SysUIComponent {
|
|||||||
@BindsInstance
|
@BindsInstance
|
||||||
Builder setStartingSurface(Optional<StartingSurface> s);
|
Builder setStartingSurface(Optional<StartingSurface> s);
|
||||||
|
|
||||||
|
@BindsInstance
|
||||||
|
Builder setDisplayAreaHelper(Optional<DisplayAreaHelper> h);
|
||||||
|
|
||||||
@BindsInstance
|
@BindsInstance
|
||||||
Builder setTaskSurfaceHelper(Optional<TaskSurfaceHelper> t);
|
Builder setTaskSurfaceHelper(Optional<TaskSurfaceHelper> t);
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import com.android.wm.shell.ShellInit;
|
|||||||
import com.android.wm.shell.TaskViewFactory;
|
import com.android.wm.shell.TaskViewFactory;
|
||||||
import com.android.wm.shell.apppairs.AppPairs;
|
import com.android.wm.shell.apppairs.AppPairs;
|
||||||
import com.android.wm.shell.bubbles.Bubbles;
|
import com.android.wm.shell.bubbles.Bubbles;
|
||||||
|
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
|
||||||
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
|
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
|
||||||
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
|
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
|
||||||
import com.android.wm.shell.onehanded.OneHanded;
|
import com.android.wm.shell.onehanded.OneHanded;
|
||||||
@@ -104,6 +105,9 @@ public interface WMComponent {
|
|||||||
@WMSingleton
|
@WMSingleton
|
||||||
Optional<StartingSurface> getStartingSurface();
|
Optional<StartingSurface> getStartingSurface();
|
||||||
|
|
||||||
|
@WMSingleton
|
||||||
|
Optional<DisplayAreaHelper> getDisplayAreaHelper();
|
||||||
|
|
||||||
@WMSingleton
|
@WMSingleton
|
||||||
Optional<TaskSurfaceHelper> getTaskSurfaceHelper();
|
Optional<TaskSurfaceHelper> getTaskSurfaceHelper();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,12 +119,15 @@ import com.android.systemui.statusbar.phone.StatusBar;
|
|||||||
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
|
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
|
||||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||||
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
||||||
|
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
|
||||||
|
import com.android.systemui.unfold.config.UnfoldTransitionConfig;
|
||||||
import com.android.systemui.util.DeviceConfigProxy;
|
import com.android.systemui.util.DeviceConfigProxy;
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
|
|
||||||
@@ -815,6 +818,10 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
|||||||
private DeviceConfigProxy mDeviceConfig;
|
private DeviceConfigProxy mDeviceConfig;
|
||||||
private DozeParameters mDozeParameters;
|
private DozeParameters mDozeParameters;
|
||||||
|
|
||||||
|
private final UnfoldTransitionConfig mUnfoldTransitionConfig;
|
||||||
|
private final Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealAnimation;
|
||||||
|
private final AtomicInteger mPendingDrawnTasks = new AtomicInteger();
|
||||||
|
|
||||||
private final KeyguardStateController mKeyguardStateController;
|
private final KeyguardStateController mKeyguardStateController;
|
||||||
private final Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
|
private final Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
|
||||||
private boolean mWallpaperSupportsAmbientMode;
|
private boolean mWallpaperSupportsAmbientMode;
|
||||||
@@ -837,6 +844,8 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
|||||||
NavigationModeController navigationModeController,
|
NavigationModeController navigationModeController,
|
||||||
KeyguardDisplayManager keyguardDisplayManager,
|
KeyguardDisplayManager keyguardDisplayManager,
|
||||||
DozeParameters dozeParameters,
|
DozeParameters dozeParameters,
|
||||||
|
UnfoldTransitionConfig unfoldTransitionConfig,
|
||||||
|
Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
|
||||||
SysuiStatusBarStateController statusBarStateController,
|
SysuiStatusBarStateController statusBarStateController,
|
||||||
KeyguardStateController keyguardStateController,
|
KeyguardStateController keyguardStateController,
|
||||||
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationControllerLazy,
|
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationControllerLazy,
|
||||||
@@ -870,6 +879,8 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
|||||||
mInGestureNavigationMode = QuickStepContract.isGesturalMode(mode);
|
mInGestureNavigationMode = QuickStepContract.isGesturalMode(mode);
|
||||||
}));
|
}));
|
||||||
mDozeParameters = dozeParameters;
|
mDozeParameters = dozeParameters;
|
||||||
|
mUnfoldTransitionConfig = unfoldTransitionConfig;
|
||||||
|
mUnfoldLightRevealAnimation = unfoldLightRevealOverlayAnimation;
|
||||||
mStatusBarStateController = statusBarStateController;
|
mStatusBarStateController = statusBarStateController;
|
||||||
statusBarStateController.addCallback(this);
|
statusBarStateController.addCallback(this);
|
||||||
|
|
||||||
@@ -2552,6 +2563,24 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
|||||||
Trace.beginSection("KeyguardViewMediator#handleNotifyScreenTurningOn");
|
Trace.beginSection("KeyguardViewMediator#handleNotifyScreenTurningOn");
|
||||||
synchronized (KeyguardViewMediator.this) {
|
synchronized (KeyguardViewMediator.this) {
|
||||||
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurningOn");
|
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurningOn");
|
||||||
|
|
||||||
|
if (mUnfoldTransitionConfig.isEnabled()) {
|
||||||
|
mPendingDrawnTasks.set(2); // unfold overlay and keyguard drawn
|
||||||
|
|
||||||
|
mUnfoldLightRevealAnimation.get()
|
||||||
|
.onScreenTurningOn(() -> {
|
||||||
|
if (mPendingDrawnTasks.decrementAndGet() == 0) {
|
||||||
|
try {
|
||||||
|
callback.onDrawn();
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Slog.w(TAG, "Exception calling onDrawn():", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
mPendingDrawnTasks.set(1); // only keyguard drawn
|
||||||
|
}
|
||||||
|
|
||||||
mKeyguardViewControllerLazy.get().onScreenTurningOn();
|
mKeyguardViewControllerLazy.get().onScreenTurningOn();
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
if (mWakeAndUnlocking) {
|
if (mWakeAndUnlocking) {
|
||||||
@@ -2582,11 +2611,13 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
|||||||
|
|
||||||
private void notifyDrawn(final IKeyguardDrawnCallback callback) {
|
private void notifyDrawn(final IKeyguardDrawnCallback callback) {
|
||||||
Trace.beginSection("KeyguardViewMediator#notifyDrawn");
|
Trace.beginSection("KeyguardViewMediator#notifyDrawn");
|
||||||
|
if (mPendingDrawnTasks.decrementAndGet() == 0) {
|
||||||
try {
|
try {
|
||||||
callback.onDrawn();
|
callback.onDrawn();
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Slog.w(TAG, "Exception calling onDrawn():", e);
|
Slog.w(TAG, "Exception calling onDrawn():", e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Trace.endSection();
|
Trace.endSection();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2738,6 +2769,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
|
|||||||
pw.print(" mHideAnimationRun: "); pw.println(mHideAnimationRun);
|
pw.print(" mHideAnimationRun: "); pw.println(mHideAnimationRun);
|
||||||
pw.print(" mPendingReset: "); pw.println(mPendingReset);
|
pw.print(" mPendingReset: "); pw.println(mPendingReset);
|
||||||
pw.print(" mPendingLock: "); pw.println(mPendingLock);
|
pw.print(" mPendingLock: "); pw.println(mPendingLock);
|
||||||
|
pw.print(" mPendingDrawnTasks: "); pw.println(mPendingDrawnTasks.get());
|
||||||
pw.print(" mWakeAndUnlocking: "); pw.println(mWakeAndUnlocking);
|
pw.print(" mWakeAndUnlocking: "); pw.println(mWakeAndUnlocking);
|
||||||
pw.print(" mDrawnCallback: "); pw.println(mDrawnCallback);
|
pw.print(" mDrawnCallback: "); pw.println(mDrawnCallback);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ import com.android.systemui.statusbar.phone.StatusBar;
|
|||||||
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
|
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
|
||||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||||
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
||||||
|
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
|
||||||
|
import com.android.systemui.unfold.config.UnfoldTransitionConfig;
|
||||||
import com.android.systemui.util.DeviceConfigProxy;
|
import com.android.systemui.util.DeviceConfigProxy;
|
||||||
import com.android.systemui.util.sensors.AsyncSensorManager;
|
import com.android.systemui.util.sensors.AsyncSensorManager;
|
||||||
import com.android.systemui.util.settings.GlobalSettings;
|
import com.android.systemui.util.settings.GlobalSettings;
|
||||||
@@ -99,6 +101,8 @@ public class KeyguardModule {
|
|||||||
NavigationModeController navigationModeController,
|
NavigationModeController navigationModeController,
|
||||||
KeyguardDisplayManager keyguardDisplayManager,
|
KeyguardDisplayManager keyguardDisplayManager,
|
||||||
DozeParameters dozeParameters,
|
DozeParameters dozeParameters,
|
||||||
|
UnfoldTransitionConfig unfoldTransitionConfig,
|
||||||
|
Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
|
||||||
SysuiStatusBarStateController statusBarStateController,
|
SysuiStatusBarStateController statusBarStateController,
|
||||||
KeyguardStateController keyguardStateController,
|
KeyguardStateController keyguardStateController,
|
||||||
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController,
|
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController,
|
||||||
@@ -121,6 +125,8 @@ public class KeyguardModule {
|
|||||||
navigationModeController,
|
navigationModeController,
|
||||||
keyguardDisplayManager,
|
keyguardDisplayManager,
|
||||||
dozeParameters,
|
dozeParameters,
|
||||||
|
unfoldTransitionConfig,
|
||||||
|
unfoldLightRevealOverlayAnimation,
|
||||||
statusBarStateController,
|
statusBarStateController,
|
||||||
keyguardStateController,
|
keyguardStateController,
|
||||||
keyguardUnlockAnimationController,
|
keyguardUnlockAnimationController,
|
||||||
|
|||||||
@@ -19,13 +19,27 @@ import android.content.Context
|
|||||||
import android.graphics.PixelFormat
|
import android.graphics.PixelFormat
|
||||||
import android.hardware.devicestate.DeviceStateManager
|
import android.hardware.devicestate.DeviceStateManager
|
||||||
import android.hardware.devicestate.DeviceStateManager.FoldStateListener
|
import android.hardware.devicestate.DeviceStateManager.FoldStateListener
|
||||||
|
import android.hardware.display.DisplayManager
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Trace
|
||||||
|
import android.view.Choreographer
|
||||||
|
import android.view.Display
|
||||||
|
import android.view.DisplayInfo
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
|
import android.view.SurfaceControl
|
||||||
|
import android.view.SurfaceControlViewHost
|
||||||
|
import android.view.SurfaceSession
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
|
import android.view.WindowlessWindowManager
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
import com.android.systemui.dagger.qualifiers.Main
|
import com.android.systemui.dagger.qualifiers.Main
|
||||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
import com.android.systemui.dagger.qualifiers.UiBackground
|
||||||
|
import com.android.systemui.statusbar.LightRevealEffect
|
||||||
import com.android.systemui.statusbar.LightRevealScrim
|
import com.android.systemui.statusbar.LightRevealScrim
|
||||||
import com.android.systemui.statusbar.LinearLightRevealEffect
|
import com.android.systemui.statusbar.LinearLightRevealEffect
|
||||||
|
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
||||||
|
import com.android.wm.shell.displayareahelper.DisplayAreaHelper
|
||||||
|
import java.util.Optional
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -34,53 +48,148 @@ import javax.inject.Inject
|
|||||||
class UnfoldLightRevealOverlayAnimation @Inject constructor(
|
class UnfoldLightRevealOverlayAnimation @Inject constructor(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val deviceStateManager: DeviceStateManager,
|
private val deviceStateManager: DeviceStateManager,
|
||||||
|
private val displayManager: DisplayManager,
|
||||||
private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
|
private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
|
||||||
|
private val displayAreaHelper: Optional<DisplayAreaHelper>,
|
||||||
@Main private val executor: Executor,
|
@Main private val executor: Executor,
|
||||||
private val windowManager: WindowManager
|
@Main private val handler: Handler,
|
||||||
|
@UiBackground private val backgroundExecutor: Executor
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val transitionListener = TransitionListener()
|
private val transitionListener = TransitionListener()
|
||||||
|
private val displayListener = DisplayChangeListener()
|
||||||
|
|
||||||
|
private lateinit var wwm: WindowlessWindowManager
|
||||||
|
private lateinit var unfoldedDisplayInfo: DisplayInfo
|
||||||
|
private lateinit var overlayContainer: SurfaceControl
|
||||||
|
|
||||||
|
private var root: SurfaceControlViewHost? = null
|
||||||
private var scrimView: LightRevealScrim? = null
|
private var scrimView: LightRevealScrim? = null
|
||||||
|
private var isFolded: Boolean = false
|
||||||
|
private var isUnfoldHandled: Boolean = true
|
||||||
|
|
||||||
|
private var currentRotation: Int = context.display!!.rotation
|
||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
deviceStateManager.registerCallback(executor, FoldListener())
|
deviceStateManager.registerCallback(executor, FoldListener())
|
||||||
unfoldTransitionProgressProvider.addCallback(transitionListener)
|
unfoldTransitionProgressProvider.addCallback(transitionListener)
|
||||||
}
|
|
||||||
|
|
||||||
private inner class TransitionListener : TransitionProgressListener {
|
val containerBuilder = SurfaceControl.Builder(SurfaceSession())
|
||||||
|
.setContainerLayer()
|
||||||
|
.setName("unfold-overlay-container")
|
||||||
|
|
||||||
override fun onTransitionProgress(progress: Float) {
|
displayAreaHelper.get().attachToRootDisplayArea(Display.DEFAULT_DISPLAY,
|
||||||
scrimView?.revealAmount = progress
|
containerBuilder) { builder ->
|
||||||
}
|
executor.execute {
|
||||||
|
overlayContainer = builder.build()
|
||||||
|
|
||||||
override fun onTransitionFinished() {
|
SurfaceControl.Transaction()
|
||||||
removeOverlayView()
|
.setLayer(overlayContainer, Integer.MAX_VALUE)
|
||||||
}
|
.show(overlayContainer)
|
||||||
|
.apply()
|
||||||
|
|
||||||
override fun onTransitionStarted() {
|
wwm = WindowlessWindowManager(context.resources.configuration,
|
||||||
// When unfolding the view is added earlier, add view for folding case
|
overlayContainer, null)
|
||||||
if (scrimView == null) {
|
|
||||||
addOverlayView()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class FoldListener : FoldStateListener(context, Consumer { isFolded ->
|
displayManager.registerDisplayListener(displayListener, handler,
|
||||||
if (isFolded) {
|
DisplayManager.EVENT_FLAG_DISPLAY_CHANGED)
|
||||||
removeOverlayView()
|
|
||||||
|
// Get unfolded display size immediately as 'current display info' might be
|
||||||
|
// not up-to-date during unfolding
|
||||||
|
unfoldedDisplayInfo = getUnfoldedDisplayInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when screen starts turning on, the contents of the screen might not be visible yet.
|
||||||
|
* This method reports back that the overlay is ready in [onOverlayReady] callback.
|
||||||
|
*
|
||||||
|
* @param onOverlayReady callback when the overlay is drawn and visible on the screen
|
||||||
|
* @see [com.android.systemui.keyguard.KeyguardViewMediator]
|
||||||
|
*/
|
||||||
|
fun onScreenTurningOn(onOverlayReady: Runnable) {
|
||||||
|
Trace.beginSection("UnfoldLightRevealOverlayAnimation#onScreenTurningOn")
|
||||||
|
try {
|
||||||
|
// Add the view only if we are unfolding and this is the first screen on
|
||||||
|
if (!isFolded && !isUnfoldHandled) {
|
||||||
|
addView(onOverlayReady)
|
||||||
|
isUnfoldHandled = true
|
||||||
} else {
|
} else {
|
||||||
// Add overlay view before starting the transition as soon as we unfolded the device
|
// No unfold transition, immediately report that overlay is ready
|
||||||
addOverlayView()
|
ensureOverlayRemoved()
|
||||||
|
onOverlayReady.run()
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Trace.endSection()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
private fun addOverlayView() {
|
private fun addView(onOverlayReady: Runnable? = null) {
|
||||||
|
if (!::wwm.isInitialized) {
|
||||||
|
// Surface overlay is not created yet on the first SysUI launch
|
||||||
|
onOverlayReady?.run()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureOverlayRemoved()
|
||||||
|
|
||||||
|
val newRoot = SurfaceControlViewHost(context, context.display!!, wwm, false)
|
||||||
|
val newView = LightRevealScrim(context, null)
|
||||||
|
.apply {
|
||||||
|
revealEffect = createLightRevealEffect()
|
||||||
|
isScrimOpaqueChangedListener = Consumer {}
|
||||||
|
revealAmount = 0f
|
||||||
|
}
|
||||||
|
|
||||||
|
val params = getLayoutParams()
|
||||||
|
newRoot.setView(newView, params)
|
||||||
|
|
||||||
|
onOverlayReady?.let { callback ->
|
||||||
|
Trace.beginAsyncSection(
|
||||||
|
"UnfoldLightRevealOverlayAnimation#relayout", 0)
|
||||||
|
|
||||||
|
newRoot.relayout(params) { transaction ->
|
||||||
|
val vsyncId = Choreographer.getSfInstance().vsyncId
|
||||||
|
|
||||||
|
backgroundExecutor.execute {
|
||||||
|
// Apply the transaction that contains the first frame of the overlay
|
||||||
|
// synchronously and apply another empty transaction with
|
||||||
|
// 'vsyncId + 1' to make sure that it is actually displayed on
|
||||||
|
// the screen. The second transaction is necessary to remove the screen blocker
|
||||||
|
// (turn on the brightness) only when the content is actually visible as it
|
||||||
|
// might be presented only in the next frame.
|
||||||
|
// See b/197538198
|
||||||
|
transaction.setFrameTimelineVsync(vsyncId)
|
||||||
|
.apply(/* sync */true)
|
||||||
|
|
||||||
|
transaction
|
||||||
|
.setFrameTimelineVsync(vsyncId + 1)
|
||||||
|
.apply(/* sync */ true)
|
||||||
|
|
||||||
|
Trace.endAsyncSection(
|
||||||
|
"UnfoldLightRevealOverlayAnimation#relayout", 0)
|
||||||
|
callback.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scrimView = newView
|
||||||
|
root = newRoot
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLayoutParams(): WindowManager.LayoutParams {
|
||||||
val params: WindowManager.LayoutParams = WindowManager.LayoutParams()
|
val params: WindowManager.LayoutParams = WindowManager.LayoutParams()
|
||||||
params.height = WindowManager.LayoutParams.MATCH_PARENT
|
|
||||||
params.width = WindowManager.LayoutParams.MATCH_PARENT
|
|
||||||
params.format = PixelFormat.TRANSLUCENT
|
|
||||||
|
|
||||||
// TODO(b/193801466): create a separate type for this overlay
|
val rotation = context.display!!.rotation
|
||||||
|
val isNatural = rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
|
||||||
|
|
||||||
|
params.height = if (isNatural)
|
||||||
|
unfoldedDisplayInfo.naturalHeight else unfoldedDisplayInfo.naturalWidth
|
||||||
|
params.width = if (isNatural)
|
||||||
|
unfoldedDisplayInfo.naturalWidth else unfoldedDisplayInfo.naturalHeight
|
||||||
|
|
||||||
|
params.format = PixelFormat.TRANSLUCENT
|
||||||
params.type = WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
|
params.type = WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
|
||||||
params.title = "Unfold Light Reveal Animation"
|
params.title = "Unfold Light Reveal Animation"
|
||||||
params.layoutInDisplayCutoutMode =
|
params.layoutInDisplayCutoutMode =
|
||||||
@@ -90,41 +199,72 @@ class UnfoldLightRevealOverlayAnimation @Inject constructor(
|
|||||||
or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
|
or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
|
||||||
params.setTrustedOverlay()
|
params.setTrustedOverlay()
|
||||||
|
|
||||||
val rotation = windowManager.defaultDisplay.rotation
|
val packageName: String = context.opPackageName
|
||||||
val isVerticalFold = rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
|
|
||||||
|
|
||||||
val newScrimView = LightRevealScrim(context, null)
|
|
||||||
.apply {
|
|
||||||
revealEffect = LinearLightRevealEffect(isVerticalFold)
|
|
||||||
isScrimOpaqueChangedListener = Consumer {}
|
|
||||||
revealAmount = 0f
|
|
||||||
}
|
|
||||||
|
|
||||||
val packageName: String = newScrimView.context.opPackageName
|
|
||||||
params.packageName = packageName
|
params.packageName = packageName
|
||||||
params.hideTimeoutMilliseconds = OVERLAY_HIDE_TIMEOUT_MILLIS
|
|
||||||
|
|
||||||
if (scrimView?.parent != null) {
|
return params
|
||||||
windowManager.removeView(scrimView)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.scrimView = newScrimView
|
private fun createLightRevealEffect(): LightRevealEffect {
|
||||||
|
val isVerticalFold = currentRotation == Surface.ROTATION_0 ||
|
||||||
try {
|
currentRotation == Surface.ROTATION_180
|
||||||
windowManager.addView(scrimView, params)
|
return LinearLightRevealEffect(isVertical = isVerticalFold)
|
||||||
} catch (e: WindowManager.BadTokenException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeOverlayView() {
|
private fun ensureOverlayRemoved() {
|
||||||
scrimView?.let {
|
root?.release()
|
||||||
if (it.parent != null) {
|
root = null
|
||||||
windowManager.removeViewImmediate(it)
|
|
||||||
}
|
|
||||||
scrimView = null
|
scrimView = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getUnfoldedDisplayInfo(): DisplayInfo =
|
||||||
|
displayManager.displays
|
||||||
|
.asSequence()
|
||||||
|
.map { DisplayInfo().apply { it.getDisplayInfo(this) } }
|
||||||
|
.filter { it.type == Display.TYPE_INTERNAL }
|
||||||
|
.maxByOrNull { it.naturalWidth }!!
|
||||||
|
|
||||||
|
private inner class TransitionListener : TransitionProgressListener {
|
||||||
|
|
||||||
|
override fun onTransitionProgress(progress: Float) {
|
||||||
|
scrimView?.revealAmount = progress
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTransitionFinished() {
|
||||||
|
ensureOverlayRemoved()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTransitionStarted() {
|
||||||
|
// Add view for folding case (when unfolding the view is added earlier)
|
||||||
|
if (scrimView == null) {
|
||||||
|
addView()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val OVERLAY_HIDE_TIMEOUT_MILLIS = 10_000L
|
private inner class DisplayChangeListener : DisplayManager.DisplayListener {
|
||||||
|
|
||||||
|
override fun onDisplayChanged(displayId: Int) {
|
||||||
|
val newRotation: Int = context.display!!.rotation
|
||||||
|
if (currentRotation != newRotation) {
|
||||||
|
currentRotation = newRotation
|
||||||
|
scrimView?.revealEffect = createLightRevealEffect()
|
||||||
|
root?.relayout(getLayoutParams())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDisplayAdded(displayId: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDisplayRemoved(displayId: Int) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class FoldListener : FoldStateListener(context, Consumer { isFolded ->
|
||||||
|
if (isFolded) {
|
||||||
|
ensureOverlayRemoved()
|
||||||
|
isUnfoldHandled = false
|
||||||
|
}
|
||||||
|
this.isFolded = isFolded
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import com.android.internal.logging.UiEventLogger;
|
|||||||
import com.android.internal.statusbar.IStatusBarService;
|
import com.android.internal.statusbar.IStatusBarService;
|
||||||
import com.android.systemui.dagger.WMComponent;
|
import com.android.systemui.dagger.WMComponent;
|
||||||
import com.android.systemui.dagger.WMSingleton;
|
import com.android.systemui.dagger.WMSingleton;
|
||||||
|
import com.android.wm.shell.RootDisplayAreaOrganizer;
|
||||||
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
||||||
import com.android.wm.shell.ShellCommandHandler;
|
import com.android.wm.shell.ShellCommandHandler;
|
||||||
import com.android.wm.shell.ShellCommandHandlerImpl;
|
import com.android.wm.shell.ShellCommandHandlerImpl;
|
||||||
@@ -54,6 +55,8 @@ import com.android.wm.shell.common.TransactionPool;
|
|||||||
import com.android.wm.shell.common.annotations.ShellAnimationThread;
|
import com.android.wm.shell.common.annotations.ShellAnimationThread;
|
||||||
import com.android.wm.shell.common.annotations.ShellMainThread;
|
import com.android.wm.shell.common.annotations.ShellMainThread;
|
||||||
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
|
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
|
||||||
|
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
|
||||||
|
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
|
||||||
import com.android.wm.shell.draganddrop.DragAndDropController;
|
import com.android.wm.shell.draganddrop.DragAndDropController;
|
||||||
import com.android.wm.shell.freeform.FreeformTaskListener;
|
import com.android.wm.shell.freeform.FreeformTaskListener;
|
||||||
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
|
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
|
||||||
@@ -342,13 +345,21 @@ public abstract class WMShellBaseModule {
|
|||||||
return taskSurfaceController.map((controller) -> controller.asTaskSurfaceHelper());
|
return taskSurfaceController.map((controller) -> controller.asTaskSurfaceHelper());
|
||||||
}
|
}
|
||||||
|
|
||||||
@WMSingleton
|
|
||||||
@Provides
|
@Provides
|
||||||
static Optional<TaskSurfaceHelperController> provideTaskSurfaceHelperController(
|
static Optional<TaskSurfaceHelperController> provideTaskSurfaceHelperController(
|
||||||
ShellTaskOrganizer taskOrganizer, @ShellMainThread ShellExecutor mainExecutor) {
|
ShellTaskOrganizer taskOrganizer, @ShellMainThread ShellExecutor mainExecutor) {
|
||||||
return Optional.ofNullable(new TaskSurfaceHelperController(taskOrganizer, mainExecutor));
|
return Optional.ofNullable(new TaskSurfaceHelperController(taskOrganizer, mainExecutor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WMSingleton
|
||||||
|
@Provides
|
||||||
|
static Optional<DisplayAreaHelper> provideDisplayAreaHelper(
|
||||||
|
@ShellMainThread ShellExecutor mainExecutor,
|
||||||
|
RootDisplayAreaOrganizer rootDisplayAreaOrganizer) {
|
||||||
|
return Optional.ofNullable(new DisplayAreaHelperController(mainExecutor,
|
||||||
|
rootDisplayAreaOrganizer));
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Pip (optional feature)
|
// Pip (optional feature)
|
||||||
//
|
//
|
||||||
@@ -422,6 +433,13 @@ public abstract class WMShellBaseModule {
|
|||||||
return new RootTaskDisplayAreaOrganizer(mainExecutor, context);
|
return new RootTaskDisplayAreaOrganizer(mainExecutor, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WMSingleton
|
||||||
|
@Provides
|
||||||
|
static RootDisplayAreaOrganizer provideRootDisplayAreaOrganizer(
|
||||||
|
@ShellMainThread ShellExecutor mainExecutor) {
|
||||||
|
return new RootDisplayAreaOrganizer(mainExecutor);
|
||||||
|
}
|
||||||
|
|
||||||
@WMSingleton
|
@WMSingleton
|
||||||
@Provides
|
@Provides
|
||||||
static Optional<SplitScreen> provideSplitScreen(
|
static Optional<SplitScreen> provideSplitScreen(
|
||||||
|
|||||||
@@ -34,12 +34,14 @@ import android.app.admin.DevicePolicyManager;
|
|||||||
import android.app.trust.TrustManager;
|
import android.app.trust.TrustManager;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.os.PowerManager.WakeLock;
|
import android.os.PowerManager.WakeLock;
|
||||||
|
import android.os.RemoteException;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
import android.testing.AndroidTestingRunner;
|
import android.testing.AndroidTestingRunner;
|
||||||
import android.testing.TestableLooper;
|
import android.testing.TestableLooper;
|
||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.internal.policy.IKeyguardDrawnCallback;
|
||||||
import com.android.internal.widget.LockPatternUtils;
|
import com.android.internal.widget.LockPatternUtils;
|
||||||
import com.android.keyguard.KeyguardDisplayManager;
|
import com.android.keyguard.KeyguardDisplayManager;
|
||||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||||
@@ -55,6 +57,8 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
|||||||
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
|
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
|
||||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||||
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
||||||
|
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
|
||||||
|
import com.android.systemui.unfold.config.UnfoldTransitionConfig;
|
||||||
import com.android.systemui.util.DeviceConfigProxy;
|
import com.android.systemui.util.DeviceConfigProxy;
|
||||||
import com.android.systemui.util.DeviceConfigProxyFake;
|
import com.android.systemui.util.DeviceConfigProxyFake;
|
||||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||||
@@ -63,6 +67,7 @@ import com.android.systemui.util.time.FakeSystemClock;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
@@ -85,11 +90,14 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
|
|||||||
private @Mock NavigationModeController mNavigationModeController;
|
private @Mock NavigationModeController mNavigationModeController;
|
||||||
private @Mock KeyguardDisplayManager mKeyguardDisplayManager;
|
private @Mock KeyguardDisplayManager mKeyguardDisplayManager;
|
||||||
private @Mock DozeParameters mDozeParameters;
|
private @Mock DozeParameters mDozeParameters;
|
||||||
|
private @Mock UnfoldTransitionConfig mUnfoldTransitionConfig;
|
||||||
|
private @Mock UnfoldLightRevealOverlayAnimation mUnfoldAnimation;
|
||||||
private @Mock SysuiStatusBarStateController mStatusBarStateController;
|
private @Mock SysuiStatusBarStateController mStatusBarStateController;
|
||||||
private @Mock KeyguardStateController mKeyguardStateController;
|
private @Mock KeyguardStateController mKeyguardStateController;
|
||||||
private @Mock NotificationShadeDepthController mNotificationShadeDepthController;
|
private @Mock NotificationShadeDepthController mNotificationShadeDepthController;
|
||||||
private @Mock KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
|
private @Mock KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
|
||||||
private @Mock UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
|
private @Mock UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
|
||||||
|
private @Mock IKeyguardDrawnCallback mKeyguardDrawnCallback;
|
||||||
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
|
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
|
||||||
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
|
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
|
||||||
|
|
||||||
@@ -120,6 +128,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
|
|||||||
mNavigationModeController,
|
mNavigationModeController,
|
||||||
mKeyguardDisplayManager,
|
mKeyguardDisplayManager,
|
||||||
mDozeParameters,
|
mDozeParameters,
|
||||||
|
mUnfoldTransitionConfig,
|
||||||
|
() -> mUnfoldAnimation,
|
||||||
mStatusBarStateController,
|
mStatusBarStateController,
|
||||||
mKeyguardStateController,
|
mKeyguardStateController,
|
||||||
() -> mKeyguardUnlockAnimationController,
|
() -> mKeyguardUnlockAnimationController,
|
||||||
@@ -147,6 +157,33 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
|
|||||||
verify(mStatusBarKeyguardViewManager).setKeyguardGoingAwayState(eq(false));
|
verify(mStatusBarKeyguardViewManager).setKeyguardGoingAwayState(eq(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||||
|
public void testUnfoldTransitionEnabledDrawnTasksReady_onScreenTurningOn_callsDrawnCallback()
|
||||||
|
throws RemoteException {
|
||||||
|
when(mUnfoldTransitionConfig.isEnabled()).thenReturn(true);
|
||||||
|
|
||||||
|
mViewMediator.onScreenTurningOn(mKeyguardDrawnCallback);
|
||||||
|
TestableLooper.get(this).processAllMessages();
|
||||||
|
onUnfoldOverlayReady();
|
||||||
|
|
||||||
|
// Should be called when both unfold overlay and keyguard drawn ready
|
||||||
|
verify(mKeyguardDrawnCallback).onDrawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||||
|
public void testUnfoldTransitionDisabledDrawnTasksReady_onScreenTurningOn_callsDrawnCallback()
|
||||||
|
throws RemoteException {
|
||||||
|
when(mUnfoldTransitionConfig.isEnabled()).thenReturn(false);
|
||||||
|
|
||||||
|
mViewMediator.onScreenTurningOn(mKeyguardDrawnCallback);
|
||||||
|
TestableLooper.get(this).processAllMessages();
|
||||||
|
|
||||||
|
// Should be called when only keyguard drawn
|
||||||
|
verify(mKeyguardDrawnCallback).onDrawn();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsAnimatingScreenOff() {
|
public void testIsAnimatingScreenOff() {
|
||||||
when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true);
|
when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true);
|
||||||
@@ -187,4 +224,11 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
|
|||||||
// then make sure it comes back
|
// then make sure it comes back
|
||||||
verify(mStatusBarKeyguardViewManager, atLeast(1)).show(null);
|
verify(mStatusBarKeyguardViewManager, atLeast(1)).show(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onUnfoldOverlayReady() {
|
||||||
|
ArgumentCaptor<Runnable> overlayReadyCaptor = ArgumentCaptor.forClass(Runnable.class);
|
||||||
|
verify(mUnfoldAnimation).onScreenTurningOn(overlayReadyCaptor.capture());
|
||||||
|
overlayReadyCaptor.getValue().run();
|
||||||
|
TestableLooper.get(this).processAllMessages();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.systemui.unfold.updates
|
||||||
|
|
||||||
|
import android.hardware.devicestate.DeviceStateManager
|
||||||
|
import android.hardware.devicestate.DeviceStateManager.FoldStateListener
|
||||||
|
import android.testing.AndroidTestingRunner
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
|
||||||
|
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
|
||||||
|
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
|
||||||
|
import com.android.systemui.util.mockito.any
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import org.junit.Assume.assumeTrue
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.ArgumentCaptor
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
@SmallTest
|
||||||
|
class DeviceFoldStateProviderTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var hingeAngleProvider: HingeAngleProvider
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var screenStatusProvider: ScreenStatusProvider
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var deviceStateManager: DeviceStateManager
|
||||||
|
|
||||||
|
private lateinit var foldStateProvider: FoldStateProvider
|
||||||
|
|
||||||
|
private val foldUpdates: MutableList<Int> = arrayListOf()
|
||||||
|
private val hingeAngleUpdates: MutableList<Float> = arrayListOf()
|
||||||
|
|
||||||
|
private val foldStateListenerCaptor = ArgumentCaptor.forClass(FoldStateListener::class.java)
|
||||||
|
private var foldedDeviceState: Int = 0
|
||||||
|
private var unfoldedDeviceState: Int = 0
|
||||||
|
|
||||||
|
private val screenOnListenerCaptor = ArgumentCaptor.forClass(ScreenListener::class.java)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
val foldedDeviceStates: IntArray = context.resources.getIntArray(
|
||||||
|
com.android.internal.R.array.config_foldedDeviceStates)
|
||||||
|
assumeTrue("Test should be launched on a foldable device",
|
||||||
|
foldedDeviceStates.isNotEmpty())
|
||||||
|
|
||||||
|
foldedDeviceState = foldedDeviceStates.maxOrNull()!!
|
||||||
|
unfoldedDeviceState = foldedDeviceState + 1
|
||||||
|
|
||||||
|
foldStateProvider = DeviceFoldStateProvider(
|
||||||
|
context,
|
||||||
|
hingeAngleProvider,
|
||||||
|
screenStatusProvider,
|
||||||
|
deviceStateManager,
|
||||||
|
context.mainExecutor
|
||||||
|
)
|
||||||
|
|
||||||
|
foldStateProvider.addCallback(object : FoldStateProvider.FoldUpdatesListener {
|
||||||
|
override fun onHingeAngleUpdate(angle: Float) {
|
||||||
|
hingeAngleUpdates.add(angle)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFoldUpdate(update: Int) {
|
||||||
|
foldUpdates.add(update)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
foldStateProvider.start()
|
||||||
|
|
||||||
|
verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
|
||||||
|
verify(screenStatusProvider).addCallback(screenOnListenerCaptor.capture())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOnFolded_emitsFinishClosedEvent() {
|
||||||
|
setFoldState(folded = true)
|
||||||
|
|
||||||
|
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_FINISH_CLOSED)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOnUnfolded_emitsStartOpeningEvent() {
|
||||||
|
setFoldState(folded = false)
|
||||||
|
|
||||||
|
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_OPENING)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOnFolded_stopsHingeAngleProvider() {
|
||||||
|
setFoldState(folded = true)
|
||||||
|
|
||||||
|
verify(hingeAngleProvider).stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOnUnfolded_startsHingeAngleProvider() {
|
||||||
|
setFoldState(folded = false)
|
||||||
|
|
||||||
|
verify(hingeAngleProvider).start()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFirstScreenOnEventWhenFolded_doesNotEmitEvents() {
|
||||||
|
setFoldState(folded = true)
|
||||||
|
foldUpdates.clear()
|
||||||
|
|
||||||
|
fireScreenOnEvent()
|
||||||
|
|
||||||
|
// Power button turn on
|
||||||
|
assertThat(foldUpdates).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFirstScreenOnEventWhenUnfolded_doesNotEmitEvents() {
|
||||||
|
setFoldState(folded = false)
|
||||||
|
foldUpdates.clear()
|
||||||
|
|
||||||
|
fireScreenOnEvent()
|
||||||
|
|
||||||
|
assertThat(foldUpdates).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFirstScreenOnEventAfterFoldAndUnfold_emitsUnfoldedScreenAvailableEvent() {
|
||||||
|
setFoldState(folded = false)
|
||||||
|
setFoldState(folded = true)
|
||||||
|
fireScreenOnEvent()
|
||||||
|
setFoldState(folded = false)
|
||||||
|
foldUpdates.clear()
|
||||||
|
|
||||||
|
fireScreenOnEvent()
|
||||||
|
|
||||||
|
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSecondScreenOnEventWhenUnfolded_doesNotEmitEvents() {
|
||||||
|
setFoldState(folded = false)
|
||||||
|
fireScreenOnEvent()
|
||||||
|
foldUpdates.clear()
|
||||||
|
|
||||||
|
fireScreenOnEvent()
|
||||||
|
|
||||||
|
// No events as this is power button turn on
|
||||||
|
assertThat(foldUpdates).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setFoldState(folded: Boolean) {
|
||||||
|
val state = if (folded) foldedDeviceState else unfoldedDeviceState
|
||||||
|
foldStateListenerCaptor.value.onStateChanged(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fireScreenOnEvent() {
|
||||||
|
screenOnListenerCaptor.value.onScreenTurnedOn()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -297,7 +297,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
|||||||
* The direct child layer of the display to put all non-overlay windows. This is also used for
|
* The direct child layer of the display to put all non-overlay windows. This is also used for
|
||||||
* screen rotation animation so that there is a parent layer to put the animation leash.
|
* screen rotation animation so that there is a parent layer to put the animation leash.
|
||||||
*/
|
*/
|
||||||
private final SurfaceControl mWindowingLayer;
|
private SurfaceControl mWindowingLayer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The window token of the layer of the hierarchy to mirror, or null if this DisplayContent
|
* The window token of the layer of the hierarchy to mirror, or null if this DisplayContent
|
||||||
@@ -329,7 +329,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
|||||||
private final ImeContainer mImeWindowsContainer = new ImeContainer(mWmService);
|
private final ImeContainer mImeWindowsContainer = new ImeContainer(mWmService);
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
final DisplayAreaPolicy mDisplayAreaPolicy;
|
DisplayAreaPolicy mDisplayAreaPolicy;
|
||||||
|
|
||||||
private WindowState mTmpWindow;
|
private WindowState mTmpWindow;
|
||||||
private boolean mUpdateImeTarget;
|
private boolean mUpdateImeTarget;
|
||||||
@@ -1104,41 +1104,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
|||||||
mDividerControllerLocked = new DockedTaskDividerController(this);
|
mDividerControllerLocked = new DockedTaskDividerController(this);
|
||||||
mPinnedTaskController = new PinnedTaskController(mWmService, this);
|
mPinnedTaskController = new PinnedTaskController(mWmService, this);
|
||||||
|
|
||||||
final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
|
final Transaction pendingTransaction = getPendingTransaction();
|
||||||
.setOpaque(true)
|
configureSurfaces(pendingTransaction);
|
||||||
.setContainerLayer()
|
pendingTransaction.apply();
|
||||||
.setCallsite("DisplayContent");
|
|
||||||
mSurfaceControl = b.setName("Root").setContainerLayer().build();
|
|
||||||
|
|
||||||
// Setup the policy and build the display area hierarchy.
|
|
||||||
mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(
|
|
||||||
mWmService, this /* content */, this /* root */, mImeWindowsContainer);
|
|
||||||
|
|
||||||
final List<DisplayArea<? extends WindowContainer>> areas =
|
|
||||||
mDisplayAreaPolicy.getDisplayAreas(FEATURE_WINDOWED_MAGNIFICATION);
|
|
||||||
final DisplayArea<?> area = areas.size() == 1 ? areas.get(0) : null;
|
|
||||||
if (area != null && area.getParent() == this) {
|
|
||||||
// The windowed magnification area should contain all non-overlay windows, so just use
|
|
||||||
// it as the windowing layer.
|
|
||||||
mWindowingLayer = area.mSurfaceControl;
|
|
||||||
} else {
|
|
||||||
// Need an additional layer for screen level animation, so move the layer containing
|
|
||||||
// the windows to the new root.
|
|
||||||
mWindowingLayer = mSurfaceControl;
|
|
||||||
mSurfaceControl = b.setName("RootWrapper").build();
|
|
||||||
getPendingTransaction().reparent(mWindowingLayer, mSurfaceControl)
|
|
||||||
.show(mWindowingLayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
mOverlayLayer = b.setName("Display Overlays").setParent(mSurfaceControl).build();
|
|
||||||
|
|
||||||
getPendingTransaction()
|
|
||||||
.setLayer(mSurfaceControl, 0)
|
|
||||||
.setLayerStack(mSurfaceControl, mDisplayId)
|
|
||||||
.show(mSurfaceControl)
|
|
||||||
.setLayer(mOverlayLayer, Integer.MAX_VALUE)
|
|
||||||
.show(mOverlayLayer);
|
|
||||||
getPendingTransaction().apply();
|
|
||||||
|
|
||||||
// Sets the display content for the children.
|
// Sets the display content for the children.
|
||||||
onDisplayChanged(this);
|
onDisplayChanged(this);
|
||||||
@@ -1152,6 +1120,77 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
|||||||
mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this);
|
mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void migrateToNewSurfaceControl(Transaction t) {
|
||||||
|
t.remove(mSurfaceControl);
|
||||||
|
|
||||||
|
mLastSurfacePosition.set(0, 0);
|
||||||
|
|
||||||
|
configureSurfaces(t);
|
||||||
|
|
||||||
|
for (int i = 0; i < mChildren.size(); i++) {
|
||||||
|
SurfaceControl sc = mChildren.get(i).getSurfaceControl();
|
||||||
|
if (sc != null) {
|
||||||
|
t.reparent(sc, mSurfaceControl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the surfaces hierarchy for DisplayContent
|
||||||
|
* This method always recreates the main surface control but reparents the children
|
||||||
|
* if they are already created.
|
||||||
|
* @param transaction as part of which to perform the configuration
|
||||||
|
*/
|
||||||
|
private void configureSurfaces(Transaction transaction) {
|
||||||
|
final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
|
||||||
|
.setOpaque(true)
|
||||||
|
.setContainerLayer()
|
||||||
|
.setCallsite("DisplayContent");
|
||||||
|
mSurfaceControl = b.setName(getName()).setContainerLayer().build();
|
||||||
|
|
||||||
|
if (mDisplayAreaPolicy == null) {
|
||||||
|
// Setup the policy and build the display area hierarchy.
|
||||||
|
// Build the hierarchy only after creating the surface so it is reparented correctly
|
||||||
|
mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(
|
||||||
|
mWmService, this /* content */, this /* root */,
|
||||||
|
mImeWindowsContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<DisplayArea<? extends WindowContainer>> areas =
|
||||||
|
mDisplayAreaPolicy.getDisplayAreas(FEATURE_WINDOWED_MAGNIFICATION);
|
||||||
|
final DisplayArea<?> area = areas.size() == 1 ? areas.get(0) : null;
|
||||||
|
|
||||||
|
if (area != null && area.getParent() == this) {
|
||||||
|
// The windowed magnification area should contain all non-overlay windows, so just use
|
||||||
|
// it as the windowing layer.
|
||||||
|
mWindowingLayer = area.mSurfaceControl;
|
||||||
|
transaction.reparent(mWindowingLayer, mSurfaceControl);
|
||||||
|
} else {
|
||||||
|
// Need an additional layer for screen level animation, so move the layer containing
|
||||||
|
// the windows to the new root.
|
||||||
|
mWindowingLayer = mSurfaceControl;
|
||||||
|
mSurfaceControl = b.setName("RootWrapper").build();
|
||||||
|
transaction.reparent(mWindowingLayer, mSurfaceControl)
|
||||||
|
.show(mWindowingLayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mOverlayLayer == null) {
|
||||||
|
mOverlayLayer = b.setName("Display Overlays").setParent(mSurfaceControl).build();
|
||||||
|
} else {
|
||||||
|
transaction.reparent(mOverlayLayer, mSurfaceControl);
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction
|
||||||
|
.setLayer(mSurfaceControl, 0)
|
||||||
|
.setLayerStack(mSurfaceControl, mDisplayId)
|
||||||
|
.show(mSurfaceControl)
|
||||||
|
.setLayer(mOverlayLayer, Integer.MAX_VALUE)
|
||||||
|
.show(mOverlayLayer);
|
||||||
|
}
|
||||||
|
|
||||||
boolean isReady() {
|
boolean isReady() {
|
||||||
// The display is ready when the system and the individual display are both ready.
|
// The display is ready when the system and the individual display are both ready.
|
||||||
return mWmService.mDisplayReady && mDisplayReady;
|
return mWmService.mDisplayReady && mDisplayReady;
|
||||||
|
|||||||
Reference in New Issue
Block a user