Merge "Adds the initial SmartSpace shared element transition!" into sc-dev

This commit is contained in:
Josh Tsuji
2021-05-27 00:29:00 +00:00
committed by Android (Google) Code Review
21 changed files with 572 additions and 22 deletions

View File

@@ -40,7 +40,8 @@ android_library {
name: "SystemUISharedLib",
srcs: [
"src/**/*.java",
"src/**/I*.aidl",
"src/**/*.kt",
"src/**/*.aidl",
":wm_shell-aidls",
],

View File

@@ -36,6 +36,9 @@ import java.util.StringJoiner;
* Various shared constants between Launcher and SysUI as part of quickstep
*/
public class QuickStepContract {
// Fully qualified name of the Launcher activity.
public static final String LAUNCHER_ACTIVITY_CLASS_NAME =
"com.google.android.apps.nexuslauncher.NexusLauncherActivity";
public static final String KEY_EXTRA_SYSUI_PROXY = "extra_sysui_proxy";
public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius";
@@ -52,6 +55,8 @@ public class QuickStepContract {
// See IStartingWindow.aidl
public static final String KEY_EXTRA_SHELL_STARTING_WINDOW =
"extra_shell_starting_window";
// See ISmartspaceTransitionController.aidl
public static final String KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER = "smartspace_transition";
public static final String NAV_BAR_MODE_2BUTTON_OVERLAY =
WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;

View File

@@ -0,0 +1,34 @@
/*
* 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.shared.system.smartspace;
import com.android.systemui.shared.system.smartspace.SmartspaceState;
// Methods for getting and setting the state of a SmartSpace. This is used to allow a remote process
// (such as System UI) to sync with and control a SmartSpace view hosted in another process (such as
// Launcher).
interface ISmartspaceCallback {
// Return information about the state of the SmartSpace, including location on-screen and
// currently selected page.
SmartspaceState getSmartspaceState();
// Set the currently selected page of this SmartSpace.
oneway void setSelectedPage(int selectedPage);
oneway void setVisibility(int visibility);
}

View File

@@ -0,0 +1,24 @@
/*
* 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.shared.system.smartspace;
import com.android.systemui.shared.system.smartspace.ISmartspaceCallback;
// Controller that keeps track of SmartSpace instances in remote processes (such as Launcher).
interface ISmartspaceTransitionController {
oneway void setSmartspace(ISmartspaceCallback callback);
}

View File

@@ -0,0 +1,21 @@
/*
* 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.shared.system.smartspace;
import com.android.systemui.shared.system.smartspace.SmartspaceState;
parcelable SmartspaceState;

View File

@@ -0,0 +1,59 @@
/*
* 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.shared.system.smartspace
import android.graphics.Rect
import android.os.Parcel
import android.os.Parcelable
/**
* Represents the state of a SmartSpace, including its location on screen and the index of the
* currently selected page. This object contains all of the information needed to synchronize two
* SmartSpace instances so that we can perform shared-element transitions between them.
*/
class SmartspaceState() : Parcelable {
var boundsOnScreen: Rect = Rect()
var selectedPage = 0
constructor(parcel: Parcel) : this() {
this.boundsOnScreen = parcel.readParcelable(Rect::javaClass.javaClass.classLoader)
this.selectedPage = parcel.readInt()
}
override fun writeToParcel(dest: Parcel?, flags: Int) {
dest?.writeParcelable(boundsOnScreen, 0)
dest?.writeInt(selectedPage)
}
override fun describeContents(): Int {
return 0
}
override fun toString(): String {
return "boundsOnScreen: $boundsOnScreen, selectedPage: $selectedPage"
}
companion object CREATOR : Parcelable.Creator<SmartspaceState> {
override fun createFromParcel(parcel: Parcel): SmartspaceState {
return SmartspaceState(parcel)
}
override fun newArray(size: Int): Array<SmartspaceState?> {
return arrayOfNulls(size)
}
}
}

View File

@@ -32,8 +32,10 @@ import com.android.keyguard.clock.ClockManager;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -44,7 +46,9 @@ import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.ViewController;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;
import javax.inject.Inject;
@@ -92,6 +96,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
// If set, will replace keyguard_status_area
private View mSmartspaceView;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private SmartspaceTransitionController mSmartspaceTransitionController;
@Inject
public KeyguardClockSwitchController(
KeyguardClockSwitch keyguardClockSwitch,
@@ -104,7 +111,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
BatteryController batteryController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
KeyguardBypassController bypassController,
LockscreenSmartspaceController smartspaceController) {
LockscreenSmartspaceController smartspaceController,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
SmartspaceTransitionController smartspaceTransitionController) {
super(keyguardClockSwitch);
mStatusBarStateController = statusBarStateController;
mColorExtractor = colorExtractor;
@@ -116,6 +125,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mBypassController = bypassController;
mSmartspaceController = smartspaceController;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
mSmartspaceTransitionController = smartspaceTransitionController;
}
/**
@@ -187,6 +199,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
nic.setLayoutParams(lp);
mView.setSmartspaceView(mSmartspaceView);
mSmartspaceTransitionController.setLockscreenSmartspace(mSmartspaceView);
}
}
@@ -285,12 +298,44 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
if (mSmartspaceView != null) {
PropertyAnimator.setProperty(mSmartspaceView, AnimatableProperty.TRANSLATION_X,
x, props, animate);
// If we're unlocking with the SmartSpace shared element transition, let the controller
// know that it should re-position our SmartSpace.
if (mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) {
mKeyguardUnlockAnimationController.updateLockscreenSmartSpacePosition();
} else {
// Otherwise, reset Y translation in case it's still offset from a previous shared
// element transition.
((View) mSmartspaceView).setTranslationY(0f);
}
}
mKeyguardSliceViewController.updatePosition(x, props, animate);
mNotificationIconAreaController.updatePosition(x, props, animate);
}
/** Sets an alpha value on every child view except for the smartspace. */
public void setChildrenAlphaExcludingSmartspace(float alpha) {
final Set<View> excludedViews = new HashSet<>();
if (mSmartspaceView != null) {
excludedViews.add(mSmartspaceView);
}
setChildrenAlphaExcluding(alpha, excludedViews);
}
/** Sets an alpha value on every child view except for the views in the provided set. */
public void setChildrenAlphaExcluding(float alpha, Set<View> excludedViews) {
for (int i = 0; i < mView.getChildCount(); i++) {
final View child = mView.getChildAt(i);
if (!excludedViews.contains(child)) {
child.setAlpha(alpha);
}
}
}
void updateTimeZone(TimeZone timeZone) {
mView.onTimeZoneChanged(timeZone);
if (mClockViewController != null) {

View File

@@ -28,6 +28,7 @@ import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridLayout;
import android.widget.TextView;
@@ -39,6 +40,7 @@ import com.android.systemui.statusbar.CrossFadeHelper;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Set;
/**
* View consisting of:
@@ -55,6 +57,7 @@ public class KeyguardStatusView extends GridLayout {
private final LockPatternUtils mLockPatternUtils;
private final IActivityManager mIActivityManager;
private ViewGroup mStatusViewContainer;
private TextView mLogoutView;
private KeyguardClockSwitch mClockView;
private TextView mOwnerInfo;
@@ -66,6 +69,7 @@ public class KeyguardStatusView extends GridLayout {
private float mDarkAmount = 0;
private int mTextColor;
private float mChildrenAlphaExcludingSmartSpace = 1f;
/**
* Bottom margin that defines the margin between bottom of smart space and top of notification
@@ -132,6 +136,7 @@ public class KeyguardStatusView extends GridLayout {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mStatusViewContainer = findViewById(R.id.status_view_container);
mLogoutView = findViewById(R.id.logout);
if (mLogoutView != null) {
mLogoutView.setOnClickListener(this::onLogoutClicked);
@@ -249,6 +254,27 @@ public class KeyguardStatusView extends GridLayout {
mClockView.setTextColor(blendedTextColor);
}
public void setChildrenAlphaExcludingClockView(float alpha) {
setChildrenAlphaExcluding(alpha, Set.of(mClockView));
}
/** Sets an alpha value on every view except for the views in the provided set. */
public void setChildrenAlphaExcluding(float alpha, Set<View> excludedViews) {
mChildrenAlphaExcludingSmartSpace = alpha;
for (int i = 0; i < mStatusViewContainer.getChildCount(); i++) {
final View child = mStatusViewContainer.getChildAt(i);
if (!excludedViews.contains(child)) {
child.setAlpha(alpha);
}
}
}
public float getChildrenAlphaExcludingSmartSpace() {
return mChildrenAlphaExcludingSmartSpace;
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("KeyguardStatusView:");
pw.println(" mOwnerInfo: " + (mOwnerInfo == null

View File

@@ -20,6 +20,8 @@ import android.graphics.Rect;
import android.os.UserHandle;
import android.util.Slog;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -49,6 +51,9 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
private final ConfigurationController mConfigurationController;
private final DozeParameters mDozeParameters;
private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private final KeyguardStateController mKeyguardStateController;
private SmartspaceTransitionController mSmartspaceTransitionController;
private final Rect mClipBounds = new Rect();
@Inject
@@ -59,15 +64,33 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
KeyguardStateController keyguardStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
ConfigurationController configurationController,
DozeParameters dozeParameters) {
DozeParameters dozeParameters,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
SmartspaceTransitionController smartspaceTransitionController) {
super(keyguardStatusView);
mKeyguardSliceViewController = keyguardSliceViewController;
mKeyguardClockSwitchController = keyguardClockSwitchController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mConfigurationController = configurationController;
mDozeParameters = dozeParameters;
mKeyguardStateController = keyguardStateController;
mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController,
dozeParameters);
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
mSmartspaceTransitionController = smartspaceTransitionController;
mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onKeyguardShowingChanged() {
// If we explicitly re-show the keyguard, make sure that all the child views are
// visible. They might have been animating out as part of the SmartSpace shared
// element transition.
if (keyguardStateController.isShowing()) {
mView.setChildrenAlphaExcludingClockView(1f);
mKeyguardClockSwitchController.setChildrenAlphaExcludingSmartspace(1f);
}
}
});
}
@Override
@@ -137,7 +160,24 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
*/
public void setAlpha(float alpha) {
if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
mView.setAlpha(alpha);
// If we're capable of performing the SmartSpace shared element transition, and we are
// going to (we're swiping to dismiss vs. bringing up the PIN screen), then fade out
// everything except for the SmartSpace.
if (mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) {
mView.setChildrenAlphaExcludingClockView(alpha);
mKeyguardClockSwitchController.setChildrenAlphaExcludingSmartspace(alpha);
} else if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
// Otherwise, we can just set the alpha for the entire container.
mView.setAlpha(alpha);
// If we previously unlocked with the shared element transition, some child views
// might still have alpha = 0f. Set them back to 1f since we're just using the
// parent container's alpha.
if (mView.getChildrenAlphaExcludingSmartSpace() < 1f) {
mView.setChildrenAlphaExcludingClockView(1f);
mKeyguardClockSwitchController.setChildrenAlphaExcludingSmartspace(1f);
}
}
}
}

View File

@@ -133,6 +133,7 @@ public class SystemUIFactory {
.setTaskViewFactory(Optional.ofNullable(null))
.setTransitions(Transitions.createEmptyForTesting())
.setStartingSurface(Optional.ofNullable(null));
}
mSysUIComponent = builder.build();
if (mInitializeComponents) {

View File

@@ -45,6 +45,7 @@ import com.android.systemui.power.dagger.PowerModule;
import com.android.systemui.recents.Recents;
import com.android.systemui.screenshot.dagger.ScreenshotModule;
import com.android.systemui.settings.dagger.SettingsModule;
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -173,6 +174,12 @@ public abstract class SystemUIModule {
return SystemUIFactory.getInstance();
}
@SysUISingleton
@Provides
static SmartspaceTransitionController provideSmartspaceTransitionController() {
return new SmartspaceTransitionController();
}
// TODO: This should provided by the WM component
/** Provides Optional of BubbleManager */
@SysUISingleton

View File

@@ -23,11 +23,14 @@ import android.content.Context
import android.graphics.Matrix
import android.view.RemoteAnimationTarget
import android.view.SyncRtSurfaceTransactionApplier
import android.view.View
import androidx.core.math.MathUtils
import com.android.internal.R
import com.android.keyguard.KeyguardClockSwitchController
import com.android.keyguard.KeyguardViewController
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
import javax.inject.Inject
@@ -85,7 +88,8 @@ class KeyguardUnlockAnimationController @Inject constructor(
context: Context,
private val keyguardStateController: KeyguardStateController,
private val keyguardViewMediator: Lazy<KeyguardViewMediator>,
private val keyguardViewController: KeyguardViewController
private val keyguardViewController: KeyguardViewController,
private val smartspaceTransitionController: SmartspaceTransitionController
) : KeyguardStateController.Callback {
/**
@@ -131,6 +135,21 @@ class KeyguardUnlockAnimationController @Inject constructor(
/** Rounded corner radius to apply to the surface behind the keyguard. */
private var roundedCornerRadius = 0f
/** The SmartSpace view on the lockscreen, provided by [KeyguardClockSwitchController]. */
public var lockscreenSmartSpace: View? = null
/**
* Whether we are currently in the process of unlocking the keyguard, and we are performing the
* shared element SmartSpace transition.
*/
private var unlockingWithSmartSpaceTransition: Boolean = false
/**
* Whether we tried to start the SmartSpace shared element transition for this unlock swipe.
* It's possible we're unable to do so (if the Launcher SmartSpace is not available).
*/
private var attemptedSmartSpaceTransitionForThisSwipe = false
init {
surfaceBehindAlphaAnimator.duration = 150
surfaceBehindAlphaAnimator.interpolator = Interpolators.ALPHA_IN
@@ -213,6 +232,24 @@ class KeyguardUnlockAnimationController @Inject constructor(
keyguardViewController.hide(surfaceBehindRemoteAnimationStartTime, 350)
}
/**
* Whether we are currently in the process of unlocking the keyguard, and we are performing the
* shared element SmartSpace transition.
*/
fun isUnlockingWithSmartSpaceTransition(): Boolean {
return unlockingWithSmartSpaceTransition
}
/**
* Update the lockscreen SmartSpace to be positioned according to the current dismiss amount. As
* the dismiss amount increases, we will increase our SmartSpace's progress to the destination
* bounds (the location of the Launcher SmartSpace).
*/
fun updateLockscreenSmartSpacePosition() {
smartspaceTransitionController.setProgressToDestinationBounds(
keyguardStateController.dismissAmount / DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD)
}
/**
* Scales in and translates up the surface behind the keyguard. This is used during unlock
* animations and swipe gestures to animate the surface's entry (and exit, if the swipe is
@@ -284,36 +321,85 @@ class KeyguardUnlockAnimationController @Inject constructor(
return
}
if (keyguardViewController.isShowing) {
updateKeyguardViewMediatorIfThresholdsReached()
// If the surface is visible or it's about to be, start updating its appearance to
// reflect the new dismiss amount.
if (keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() ||
keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) {
updateSurfaceBehindAppearAmount()
}
}
// The end of the SmartSpace transition can occur after the keyguard is hidden (when we tell
// Launcher's SmartSpace to become visible again), so update it even if the keyguard view is
// no longer showing.
updateSmartSpaceTransition()
}
/**
* Lets the KeyguardViewMediator know if the dismiss amount has crossed a threshold of interest,
* such as reaching the point in the dismiss swipe where we need to make the surface behind the
* keyguard visible.
*/
private fun updateKeyguardViewMediatorIfThresholdsReached() {
val dismissAmount = keyguardStateController.dismissAmount
// Hide the keyguard if we're fully dismissed, or if we're swiping to dismiss and have
// crossed the threshold to finish the dismissal.
val reachedHideKeyguardThreshold = (dismissAmount >= 1f ||
(keyguardStateController.isDismissingFromSwipe &&
// Don't hide if we're flinging during a swipe, since we need to finish
// animating it out. This will be called again after the fling ends.
!keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture &&
dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD))
// Don't hide if we're flinging during a swipe, since we need to finish
// animating it out. This will be called again after the fling ends.
!keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture &&
dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD))
if (dismissAmount >= DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
!keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) {
// We passed the threshold, and we're not yet showing the surface behind the keyguard.
// Animate it in.
// We passed the threshold, and we're not yet showing the surface behind the
// keyguard. Animate it in.
keyguardViewMediator.get().showSurfaceBehindKeyguard()
fadeInSurfaceBehind()
} else if (dismissAmount < DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) {
// We're no longer past the threshold but we are showing the surface. Animate it out.
// We're no longer past the threshold but we are showing the surface. Animate it
// out.
keyguardViewMediator.get().hideSurfaceBehindKeyguard()
fadeOutSurfaceBehind()
} else if (keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe &&
} else if (keyguardViewMediator.get()
.isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe &&
reachedHideKeyguardThreshold) {
keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished()
}
}
if (keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() ||
keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) {
updateSurfaceBehindAppearAmount()
/**
* Updates flags related to the SmartSpace transition in response to a change in keyguard
* dismiss amount, and also updates the SmartSpaceTransitionController, which will let Launcher
* know if it needs to do something as a result.
*/
private fun updateSmartSpaceTransition() {
val dismissAmount = keyguardStateController.dismissAmount
// If we've begun a swipe, and are capable of doing the SmartSpace transition, start it!
if (!attemptedSmartSpaceTransitionForThisSwipe &&
dismissAmount > 0f &&
dismissAmount < 1f &&
keyguardViewController.isShowing) {
attemptedSmartSpaceTransitionForThisSwipe = true
smartspaceTransitionController.prepareForUnlockTransition()
if (keyguardStateController.canPerformSmartSpaceTransition()) {
unlockingWithSmartSpaceTransition = true
smartspaceTransitionController.launcherSmartspace?.setVisibility(
View.INVISIBLE)
}
} else if (attemptedSmartSpaceTransitionForThisSwipe &&
(dismissAmount == 0f || dismissAmount == 1f)) {
attemptedSmartSpaceTransitionForThisSwipe = false
unlockingWithSmartSpaceTransition = false
smartspaceTransitionController.launcherSmartspace?.setVisibility(View.VISIBLE)
}
}

View File

@@ -29,6 +29,7 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHE
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
@@ -88,6 +89,7 @@ import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -148,6 +150,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
private final CommandQueue mCommandQueue;
private final ShellTransitions mShellTransitions;
private final Optional<StartingSurface> mStartingSurface;
private final SmartspaceTransitionController mSmartspaceTransitionController;
private Region mActiveNavBarRegion;
@@ -552,6 +555,9 @@ public class OverviewProxyService extends CurrentUserTracker implements
mStartingSurface.ifPresent((startingwindow) -> params.putBinder(
KEY_EXTRA_SHELL_STARTING_WINDOW,
startingwindow.createExternalInterface().asBinder()));
params.putBinder(
KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER,
mSmartspaceTransitionController.createExternalInterface().asBinder());
try {
Log.d(TAG_OPS + " b/182478748", "OverviewProxyService.onInitialize: curUser="
@@ -613,7 +619,8 @@ public class OverviewProxyService extends CurrentUserTracker implements
Optional<OneHanded> oneHandedOptional,
BroadcastDispatcher broadcastDispatcher,
ShellTransitions shellTransitions,
Optional<StartingSurface> startingSurface) {
Optional<StartingSurface> startingSurface,
SmartspaceTransitionController smartspaceTransitionController) {
super(broadcastDispatcher);
mContext = context;
mPipOptional = pipOptional;
@@ -675,6 +682,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
updateEnabledState();
startConnectionToCurrentUser();
mStartingSurface = startingSurface;
mSmartspaceTransitionController = smartspaceTransitionController;
}
@Override

View File

@@ -0,0 +1,143 @@
/*
* 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.shared.system.smartspace
import android.graphics.Rect
import android.view.View
import com.android.systemui.shared.system.ActivityManagerWrapper
import com.android.systemui.shared.system.QuickStepContract
import kotlin.math.min
/**
* Controller that keeps track of SmartSpace instances in remote processes (such as Launcher),
* allowing System UI to query or update their state during shared-element transitions.
*/
class SmartspaceTransitionController {
/**
* Implementation of [ISmartspaceTransitionController] that we provide to Launcher, allowing it
* to provide us with a callback to query and update the state of its Smartspace.
*/
private val ISmartspaceTransitionController = object : ISmartspaceTransitionController.Stub() {
override fun setSmartspace(callback: ISmartspaceCallback?) {
this@SmartspaceTransitionController.launcherSmartspace = callback
updateLauncherSmartSpaceState()
}
}
/**
* Callback provided by Launcher to allow us to query and update the state of its SmartSpace.
*/
public var launcherSmartspace: ISmartspaceCallback? = null
public var lockscreenSmartspace: View? = null
/**
* Cached state of the Launcher SmartSpace. Retrieving the state is an IPC, so we should avoid
* unnecessary
*/
public var mLauncherSmartspaceState: SmartspaceState? = null
/**
* The bounds of our SmartSpace when the shared element transition began. We'll interpolate
* between this and [smartspaceDestinationBounds] as the dismiss amount changes.
*/
private val smartspaceOriginBounds = Rect()
/** The bounds of the Launcher's SmartSpace, which is where we are animating our SmartSpace. */
private val smartspaceDestinationBounds = Rect()
fun createExternalInterface(): ISmartspaceTransitionController {
return ISmartspaceTransitionController
}
/**
* Updates [mLauncherSmartspaceState] and returns it. This will trigger a binder call, so use the
* cached [mLauncherSmartspaceState] if possible.
*/
fun updateLauncherSmartSpaceState(): SmartspaceState? {
return launcherSmartspace?.smartspaceState.also {
mLauncherSmartspaceState = it
}
}
fun prepareForUnlockTransition() {
updateLauncherSmartSpaceState().also { state ->
if (state?.boundsOnScreen != null && lockscreenSmartspace != null) {
lockscreenSmartspace!!.getBoundsOnScreen(smartspaceOriginBounds)
with(smartspaceDestinationBounds) {
set(state.boundsOnScreen)
offset(-lockscreenSmartspace!!.paddingLeft,
-lockscreenSmartspace!!.paddingTop)
}
}
}
}
fun setProgressToDestinationBounds(progress: Float) {
if (!isSmartspaceTransitionPossible()) {
return
}
val progressClamped = min(1f, progress)
// Calculate the distance (relative to the origin) that we need to be for the current
// progress value.
val progressX =
(smartspaceDestinationBounds.left - smartspaceOriginBounds.left) * progressClamped
val progressY =
(smartspaceDestinationBounds.top - smartspaceOriginBounds.top) * progressClamped
val lockscreenSmartspaceCurrentBounds = Rect().also {
lockscreenSmartspace!!.getBoundsOnScreen(it)
}
// Figure out how far that is from our present location on the screen. This approach
// compensates for the fact that our parent container is also translating to animate out.
val dx = smartspaceOriginBounds.left + progressX -
lockscreenSmartspaceCurrentBounds.left
var dy = smartspaceOriginBounds.top + progressY -
lockscreenSmartspaceCurrentBounds.top
with(lockscreenSmartspace!!) {
translationX = translationX + dx
translationY = translationY + dy
}
}
/**
* Whether we're capable of performing the Smartspace shared element transition when we unlock.
* This is true if:
*
* - The Launcher registered a Smartspace with us, it's reporting non-empty bounds on screen.
* - Launcher is behind the keyguard, and the Smartspace is visible on the currently selected
* page.
*/
public fun isSmartspaceTransitionPossible(): Boolean {
val smartSpaceNullOrBoundsEmpty = mLauncherSmartspaceState?.boundsOnScreen?.isEmpty ?: true
return isLauncherUnderneath() && !smartSpaceNullOrBoundsEmpty
}
companion object {
fun isLauncherUnderneath(): Boolean {
return ActivityManagerWrapper.getInstance()
.runningTask?.topActivity?.className?.equals(
QuickStepContract.LAUNCHER_ACTIVITY_CLASS_NAME) ?: false
}
}
}

View File

@@ -464,6 +464,7 @@ public class StatusBar extends SystemUI implements DemoMode,
protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final BrightnessSlider.Factory mBrightnessSliderFactory;
private final FeatureFlags mFeatureFlags;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private final List<ExpansionChangedListener> mExpansionChangedListeners;
@@ -885,6 +886,8 @@ public class StatusBar extends SystemUI implements DemoMode,
mAnimationScheduler = animationScheduler;
mStatusBarLocationPublisher = locationPublisher;
mFeatureFlags = featureFlags;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
lockscreenShadeTransitionController.setStatusbar(this);
@@ -1369,7 +1372,8 @@ public class StatusBar extends SystemUI implements DemoMode,
// are already animating the keyguard dismiss (since we will need to either finish or cancel
// the animation).
if (trackingTouch
|| mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe()) {
|| mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe()
|| mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) {
mKeyguardStateController.notifyKeyguardDismissAmountChanged(
1f - expansion, trackingTouch);
}

View File

@@ -49,6 +49,13 @@ public interface KeyguardStateController extends CallbackController<Callback> {
*/
boolean canDismissLockScreen();
/**
* Whether we can currently perform the shared element SmartSpace transition. This is true if
* we're on the lockscreen, it can be dismissed with a swipe, and the Launcher is underneath the
* keyguard and displaying a SmartSpace that it has registered with System UI.
*/
boolean canPerformSmartSpaceTransition();
/**
* If the device has PIN/pattern/password or a lock screen at all.
*/

View File

@@ -32,6 +32,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -53,6 +54,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
private final LockPatternUtils mLockPatternUtils;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
new UpdateMonitorCallback();
private final SmartspaceTransitionController mSmartspaceTransitionController;
private boolean mCanDismissLockScreen;
private boolean mShowing;
@@ -96,10 +98,12 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
*/
@Inject
public KeyguardStateControllerImpl(Context context,
KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) {
KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils,
SmartspaceTransitionController smartspaceTransitionController) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
mSmartspaceTransitionController = smartspaceTransitionController;
update(true /* updateAlways */);
if (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB) {
@@ -158,6 +162,11 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
mShowing = showing;
mOccluded = occluded;
notifyKeyguardChanged();
// Update the dismiss amount to the full 0f/1f if we explicitly show or hide the keyguard.
// Otherwise, the dismiss amount could be left at a random value if we show/hide during a
// dismiss gesture, canceling the gesture.
notifyKeyguardDismissAmountChanged(showing ? 0f : 1f, false);
}
private void notifyKeyguardChanged() {
@@ -227,6 +236,12 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
return mCanDismissLockScreen;
}
@Override
public boolean canPerformSmartSpaceTransition() {
return canDismissLockScreen()
&& mSmartspaceTransitionController.isSmartspaceTransitionPossible();
}
@Override
public boolean isFaceAuthEnabled() {
return mFaceAuthEnabled;

View File

@@ -40,8 +40,10 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -86,6 +88,9 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
@Mock
Resources mResources;
KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock
SmartspaceTransitionController mSmartSpaceTransitionController;
@Mock
private ClockPlugin mClockPlugin;
@Mock
@@ -135,7 +140,10 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
mBatteryController,
mKeyguardUpdateMonitor,
mBypassController,
mSmartspaceController);
mSmartspaceController,
mKeyguardUnlockAnimationController,
mSmartSpaceTransitionController
);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);

View File

@@ -22,6 +22,8 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -50,6 +52,10 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
ConfigurationController mConfigurationController;
@Mock
DozeParameters mDozeParameters;
@Mock
KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock
SmartspaceTransitionController mSmartSpaceTransitionController;
private KeyguardStatusViewController mController;
@@ -64,7 +70,9 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
mKeyguardStateController,
mKeyguardUpdateMonitor,
mConfigurationController,
mDozeParameters);
mDozeParameters,
mKeyguardUnlockAnimationController,
mSmartSpaceTransitionController);
}
@Test

View File

@@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import org.junit.Before;
import org.junit.Test;
@@ -49,12 +50,14 @@ public class KeyguardStateControllerTest extends SysuiTestCase {
@Mock
private LockPatternUtils mLockPatternUtils;
private KeyguardStateController mKeyguardStateController;
@Mock
private SmartspaceTransitionController mSmartSpaceTransitionController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mKeyguardStateController = new KeyguardStateControllerImpl(mContext,
mKeyguardUpdateMonitor, mLockPatternUtils);
mKeyguardUpdateMonitor, mLockPatternUtils, mSmartSpaceTransitionController);
}
@Test

View File

@@ -127,4 +127,9 @@ public class FakeKeyguardStateController implements KeyguardStateController {
public void notifyPanelFlingEnd() {
}
@Override
public boolean canPerformSmartSpaceTransition() {
return false;
}
}