Loading packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java +143 −5 Original line number Diff line number Diff line Loading @@ -16,43 +16,181 @@ package com.android.systemui.communal; import android.content.Context; import android.util.Log; import android.view.View; import android.view.ViewGroup; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.ViewController; import com.google.common.util.concurrent.ListenableFuture; import java.lang.ref.WeakReference; import java.util.concurrent.Executor; import javax.inject.Inject; /** * Injectable controller for {@link CommunalHostView}. */ public class CommunalHostViewController extends ViewController<CommunalHostView> { private static final String TAG = "CommunalController"; private static final boolean DEBUG = false; private static final AnimationProperties ANIMATION_PROPERTIES = new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); private final Executor mMainExecutor; private final KeyguardStateController mKeyguardStateController; private final StatusBarStateController mStatusBarStateController; private WeakReference<CommunalSource> mLastSource; private int mState; private static final int STATE_KEYGUARD_SHOWING = 1 << 0; private static final int STATE_DOZING = 1 << 1; // Only show communal view when keyguard is showing and not dozing. private static final int SHOW_COMMUNAL_VIEW_REQUIRED_STATES = STATE_KEYGUARD_SHOWING; private static final int SHOW_COMMUNAL_VIEW_INVALID_STATES = STATE_DOZING; private KeyguardStateController.Callback mKeyguardCallback = new KeyguardStateController.Callback() { @Override public void onKeyguardShowingChanged() { final boolean isShowing = mKeyguardStateController.isShowing(); if (DEBUG) { Log.d(TAG, "setKeyguardShowing:" + isShowing); } setState(STATE_KEYGUARD_SHOWING, isShowing); } }; private StatusBarStateController.StateListener mDozeCallback = new StatusBarStateController.StateListener() { @Override public void onDozingChanged(boolean isDozing) { if (DEBUG) { Log.d(TAG, "setDozing:" + isDozing); } setState(STATE_DOZING, isDozing); } }; @Inject protected CommunalHostViewController(CommunalHostView view) { protected CommunalHostViewController(@Main Executor mainExecutor, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, CommunalHostView view) { super(view); mMainExecutor = mainExecutor; mKeyguardStateController = keyguardStateController; mStatusBarStateController = statusBarStateController; mState = 0; if (mKeyguardStateController.isShowing()) { mState |= STATE_KEYGUARD_SHOWING; } if (mStatusBarStateController.isDozing()) { mState |= STATE_DOZING; } mKeyguardStateController.addCallback(mKeyguardCallback); mStatusBarStateController.addCallback(mDozeCallback); } @Override protected void onViewAttached() { mKeyguardStateController.removeCallback(mKeyguardCallback); mKeyguardStateController.addCallback(mKeyguardCallback); mStatusBarStateController.removeCallback(mDozeCallback); mStatusBarStateController.addCallback(mDozeCallback); } @Override protected void onViewDetached() { mKeyguardStateController.removeCallback(mKeyguardCallback); mStatusBarStateController.removeCallback(mDozeCallback); } private void setState(int stateFlag, boolean enabled) { final int existingState = mState; if (DEBUG) { Log.d(TAG, "setState flag:" + stateFlag + " enabled:" + enabled); } if (enabled) { mState |= stateFlag; } else { mState &= ~stateFlag; } if (DEBUG) { Log.d(TAG, "updated state:" + mState); } if (existingState != mState) { showSource(); } } private void showSource() { // Make sure all necessary states are present for showing communal and all invalid states // are absent mMainExecutor.execute(() -> { final CommunalSource currentSource = mLastSource != null ? mLastSource.get() : null; if ((mState & SHOW_COMMUNAL_VIEW_REQUIRED_STATES) == SHOW_COMMUNAL_VIEW_REQUIRED_STATES && (mState & SHOW_COMMUNAL_VIEW_INVALID_STATES) == 0 && currentSource != null) { mView.removeAllViews(); // Make view visible. mView.setVisibility(View.VISIBLE); final Context context = mView.getContext(); final ListenableFuture<View> listenableFuture = currentSource.requestCommunalView(context); if (listenableFuture == null) { Log.e(TAG, "could not request communal view"); return; } listenableFuture.addListener(() -> { try { final View view = listenableFuture.get(); view.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); mView.addView(view); } catch (Exception e) { Log.e(TAG, "could not obtain communal view through callback:" + e); } }, mMainExecutor); } else { mView.removeAllViews(); mView.setVisibility(View.INVISIBLE); } }); } /** * Sets whether the {@link CommunalHostView} is visible. * Instructs {@link CommunalHostViewController} to display provided source. * * @param visible {@code true} if the view should be shown, {@code false} otherwise. * @param source The new {@link CommunalSource}, {@code null} if not set. */ public void show(boolean visible) { mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); public void show(WeakReference<CommunalSource> source) { mLastSource = source; showSource(); } /** Loading packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java 0 → 100644 +63 −0 Original line number Diff line number Diff line /* * 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.communal; import android.content.Context; import android.view.View; import com.google.common.util.concurrent.ListenableFuture; /** * {@link CommunalSource} defines an interface for working with a source for communal data. Clients * may request a communal surface that can be shown within a {@link android.view.SurfaceView}. * Callbacks may also be registered to listen to state changes. */ public interface CommunalSource { /** * Requests a communal surface that can be displayed inside {@link CommunalHostView}. * * @param context The {@link View} {@link Context} to build the resulting view from * @return A future that can be listened upon for the resulting {@link View}. The value will be * {@code null} in case of a failure. */ ListenableFuture<View> requestCommunalView(Context context); /** * Adds a {@link Callback} to receive future status updates regarding this * {@link CommunalSource}. * * @param callback The {@link Callback} to be added. */ void addCallback(Callback callback); /** * Removes a {@link Callback} from receiving future updates. * * @param callback The {@link Callback} to be removed. */ void removeCallback(Callback callback); /** * An interface for receiving updates on the state of the {@link CommunalSource}. */ interface Callback { /** * Invoked when the {@link CommunalSource} is no longer available for use. */ void onDisconnected(); } } packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java 0 → 100644 +116 −0 Original line number Diff line number Diff line /* * 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.communal; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.dagger.SysUISingleton; import com.google.android.collect.Lists; import java.lang.ref.WeakReference; import java.util.ArrayList; import javax.inject.Inject; /** * A Monitor for reporting a {@link CommunalSource} presence. */ @SysUISingleton public class CommunalSourceMonitor { // A list of {@link Callback} that have registered to receive updates. private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList(); private CommunalSource mCurrentSource; private CommunalSource.Callback mSourceCallback = new CommunalSource.Callback() { @Override public void onDisconnected() { // Clear source reference. setSource(null /* source */); } }; @VisibleForTesting @Inject public CommunalSourceMonitor() { } /** * Sets the current {@link CommunalSource}, informing any callbacks. Any existing * {@link CommunalSource} will be disconnected. * * @param source The new {@link CommunalSource}. */ public void setSource(CommunalSource source) { if (mCurrentSource != null) { mCurrentSource.removeCallback(mSourceCallback); } mCurrentSource = source; // If the new source is valid, inform registered Callbacks of its presence. for (int i = 0; i < mCallbacks.size(); i++) { Callback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onSourceAvailable( mCurrentSource != null ? new WeakReference<>(mCurrentSource) : null); } } // Add callback to be informed when the source disconnects. if (mCurrentSource != null) { mCurrentSource.addCallback(mSourceCallback); } } /** * Adds a {@link Callback} to receive {@link CommunalSource} updates. * * @param callback The {@link Callback} to add. */ public void addCallback(Callback callback) { mCallbacks.add(new WeakReference<>(callback)); // Inform the callback of any already present CommunalSource. if (mCurrentSource != null) { callback.onSourceAvailable(new WeakReference<>(mCurrentSource)); } } /** * Removes the specified {@link Callback} from receive future updates if present. * * @param callback The {@link Callback} to add. */ public void removeCallback(Callback callback) { mCallbacks.removeIf(el -> el.get() == callback); } /** * Interface implemented to be notified when new {@link CommunalSource} become available. */ public interface Callback { /** * Called when a new {@link CommunalSource} has been registered. This will also be invoked * when a {@link Callback} is first registered and a {@link CommunalSource} is already * registered. * * @param source The new {@link CommunalSource}. */ void onSourceAvailable(WeakReference<CommunalSource> source); } } packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +67 −10 Original line number Diff line number Diff line Loading @@ -108,6 +108,8 @@ import com.android.systemui.classifier.Classifier; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.communal.CommunalHostView; import com.android.systemui.communal.CommunalHostViewController; import com.android.systemui.communal.CommunalSource; import com.android.systemui.communal.CommunalSourceMonitor; import com.android.systemui.communal.dagger.CommunalViewComponent; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; Loading Loading @@ -177,6 +179,7 @@ import com.android.wm.shell.animation.FlingAnimationUtils; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; import java.util.List; Loading Loading @@ -318,6 +321,7 @@ public class NotificationPanelViewController extends PanelViewController { private final PulseExpansionHandler mPulseExpansionHandler; private final KeyguardBypassController mKeyguardBypassController; private final KeyguardUpdateMonitor mUpdateMonitor; private final CommunalSourceMonitor mCommunalSourceMonitor; private final ConversationNotificationManager mConversationNotificationManager; private final AuthController mAuthController; private final MediaHierarchyManager mMediaHierarchyManager; Loading Loading @@ -357,7 +361,7 @@ public class NotificationPanelViewController extends PanelViewController { @VisibleForTesting QS mQs; private FrameLayout mQsFrame; @Nullable private CommunalHostViewController mCommunalHostViewController; private CommunalHostViewController mCommunalViewController; private KeyguardStatusViewController mKeyguardStatusViewController; private LockIconViewController mLockIconViewController; private NotificationsQuickSettingsContainer mNotificationContainerParent; Loading @@ -369,7 +373,7 @@ public class NotificationPanelViewController extends PanelViewController { private VelocityTracker mQsVelocityTracker; private boolean mQsTracking; private CommunalHostView mCommunalHostView; private CommunalHostView mCommunalView; /** * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and Loading Loading @@ -518,6 +522,24 @@ public class NotificationPanelViewController extends PanelViewController { mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN); private final NotificationEntryManager mEntryManager; private final CommunalSourceMonitor.Callback mCommunalSourceMonitorCallback = new CommunalSourceMonitor.Callback() { @Override public void onSourceAvailable(WeakReference<CommunalSource> source) { setCommunalSource(source); } }; private WeakReference<CommunalSource> mCommunalSource; private final CommunalSource.Callback mCommunalSourceCallback = new CommunalSource.Callback() { @Override public void onDisconnected() { setCommunalSource(null /*source*/); } }; private final CommandQueue mCommandQueue; private final NotificationLockscreenUserManager mLockscreenUserManager; private final UserManager mUserManager; Loading Loading @@ -700,7 +722,8 @@ public class NotificationPanelViewController extends PanelViewController { DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper, LatencyTracker latencyTracker, PowerManager powerManager, AccessibilityManager accessibilityManager, @DisplayId int displayId, KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger, KeyguardUpdateMonitor keyguardUpdateMonitor, CommunalSourceMonitor communalSourceMonitor, MetricsLogger metricsLogger, ActivityManager activityManager, ConfigurationController configurationController, Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder, Loading Loading @@ -800,6 +823,7 @@ public class NotificationPanelViewController extends PanelViewController { mThemeResId = mView.getContext().getThemeResId(); mKeyguardBypassController = bypassController; mUpdateMonitor = keyguardUpdateMonitor; mCommunalSourceMonitor = communalSourceMonitor; mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); KeyguardStateController.Callback keyguardMonitorCallback = Loading Loading @@ -861,7 +885,7 @@ public class NotificationPanelViewController extends PanelViewController { loadDimens(); mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header); mBigClockContainer = mView.findViewById(R.id.big_clock_container); mCommunalHostView = mView.findViewById(R.id.communal_host); mCommunalView = mView.findViewById(R.id.communal_host); UserAvatarView userAvatarView = null; KeyguardUserSwitcherView keyguardUserSwitcherView = null; Loading @@ -881,7 +905,7 @@ public class NotificationPanelViewController extends PanelViewController { userAvatarView, mKeyguardStatusBar, keyguardUserSwitcherView, mCommunalHostView); mCommunalView); mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent); NotificationStackScrollLayout stackScrollLayout = mView.findViewById( R.id.notification_stack_scroller); Loading Loading @@ -998,7 +1022,7 @@ public class NotificationPanelViewController extends PanelViewController { if (communalView != null) { CommunalViewComponent communalViewComponent = mCommunalViewComponentFactory.build(communalView); mCommunalHostViewController = mCommunalViewController = communalViewComponent.getCommunalHostViewController(); } Loading Loading @@ -1067,6 +1091,7 @@ public class NotificationPanelViewController extends PanelViewController { ensureAllViewsHaveIds(mNotificationContainerParent); ConstraintSet constraintSet = new ConstraintSet(); constraintSet.clone(mNotificationContainerParent); if (mShouldUseSplitNotificationShade) { // width = 0 to take up all available space within constraints qsWidth = 0; Loading Loading @@ -1161,7 +1186,7 @@ public class NotificationPanelViewController extends PanelViewController { mBigClockContainer.removeAllViews(); updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView, mKeyguardStatusBar, keyguardUserSwitcherView, mCommunalHostView); mKeyguardStatusBar, keyguardUserSwitcherView, mCommunalView); // Update keyguard bottom area int index = mView.indexOfChild(mKeyguardBottomArea); Loading Loading @@ -1371,8 +1396,8 @@ public class NotificationPanelViewController extends PanelViewController { mClockPositionResult.clockX, mClockPositionResult.clockY, mClockPositionResult.clockScale, animateClock); // CommunalView's height is constrained to KeyguardStatusView. Match Y offset as well. if (mCommunalHostViewController != null) { mCommunalHostViewController.updatePositionY(mClockPositionResult.clockY, animateClock); if (mCommunalViewController != null) { mCommunalViewController.updatePositionY(mClockPositionResult.clockY, animateClock); } if (mKeyguardQsUserSwitchController != null) { mKeyguardQsUserSwitchController.updatePosition( Loading @@ -1393,7 +1418,9 @@ public class NotificationPanelViewController extends PanelViewController { private void updateKeyguardStatusViewAlignment(boolean animate) { boolean hasVisibleNotifications = mNotificationStackScrollLayoutController .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia(); boolean shouldBeCentered = !mShouldUseSplitNotificationShade || !hasVisibleNotifications; boolean hasCommunalSurface = mCommunalSource != null && mCommunalSource.get() != null; boolean shouldBeCentered = !mShouldUseSplitNotificationShade || (!hasVisibleNotifications && !hasCommunalSurface); if (mStatusViewCentered != shouldBeCentered) { mStatusViewCentered = shouldBeCentered; ConstraintSet constraintSet = new ConstraintSet(); Loading Loading @@ -1436,6 +1463,11 @@ public class NotificationPanelViewController extends PanelViewController { * @return the maximum keyguard notifications that can fit on the screen */ private int computeMaxKeyguardNotifications() { // Do not show any notifications on the keyguard if a communal source is set. if (mCommunalSource != null && mCommunalSource.get() != null) { return 0; } float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding(); int notificationPadding = Math.max( 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height)); Loading Loading @@ -4540,6 +4572,27 @@ public class NotificationPanelViewController extends PanelViewController { setExpandedFraction(1f); } private void setCommunalSource(WeakReference<CommunalSource> source) { CommunalSource existingSource = mCommunalSource != null ? mCommunalSource.get() : null; if (existingSource != null) { existingSource.removeCallback(mCommunalSourceCallback); mCommunalViewController.show(null /*source*/); } mCommunalSource = source; CommunalSource currentSource = mCommunalSource != null ? mCommunalSource.get() : null; // Set source and register callback if (currentSource != null && mCommunalViewController != null) { currentSource.addCallback(mCommunalSourceCallback); mCommunalViewController.show(source); } updateKeyguardStatusViewAlignment(true /*animate*/); updateMaxDisplayedNotifications(true /*recompute*/); } /** * Sets the overstretch amount in raw pixels when dragging down. */ Loading @@ -4556,6 +4609,7 @@ public class NotificationPanelViewController extends PanelViewController { mStatusBarStateController.addCallback(mStatusBarStateListener); mConfigurationController.addCallback(mConfigurationListener); mUpdateMonitor.registerCallback(mKeyguardUpdateCallback); mCommunalSourceMonitor.addCallback(mCommunalSourceMonitorCallback); // Theme might have changed between inflating this view and attaching it to the // window, so // force a call to onThemeChanged Loading @@ -4573,6 +4627,9 @@ public class NotificationPanelViewController extends PanelViewController { mStatusBarStateController.removeCallback(mStatusBarStateListener); mConfigurationController.removeCallback(mConfigurationListener); mUpdateMonitor.removeCallback(mKeyguardUpdateCallback); mCommunalSourceMonitor.removeCallback(mCommunalSourceMonitorCallback); // Clear source when detached. setCommunalSource(null /*source*/); mFalsingManager.removeTapListener(mFalsingTapListener); } } Loading packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java +45 −3 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java +143 −5 Original line number Diff line number Diff line Loading @@ -16,43 +16,181 @@ package com.android.systemui.communal; import android.content.Context; import android.util.Log; import android.view.View; import android.view.ViewGroup; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.ViewController; import com.google.common.util.concurrent.ListenableFuture; import java.lang.ref.WeakReference; import java.util.concurrent.Executor; import javax.inject.Inject; /** * Injectable controller for {@link CommunalHostView}. */ public class CommunalHostViewController extends ViewController<CommunalHostView> { private static final String TAG = "CommunalController"; private static final boolean DEBUG = false; private static final AnimationProperties ANIMATION_PROPERTIES = new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); private final Executor mMainExecutor; private final KeyguardStateController mKeyguardStateController; private final StatusBarStateController mStatusBarStateController; private WeakReference<CommunalSource> mLastSource; private int mState; private static final int STATE_KEYGUARD_SHOWING = 1 << 0; private static final int STATE_DOZING = 1 << 1; // Only show communal view when keyguard is showing and not dozing. private static final int SHOW_COMMUNAL_VIEW_REQUIRED_STATES = STATE_KEYGUARD_SHOWING; private static final int SHOW_COMMUNAL_VIEW_INVALID_STATES = STATE_DOZING; private KeyguardStateController.Callback mKeyguardCallback = new KeyguardStateController.Callback() { @Override public void onKeyguardShowingChanged() { final boolean isShowing = mKeyguardStateController.isShowing(); if (DEBUG) { Log.d(TAG, "setKeyguardShowing:" + isShowing); } setState(STATE_KEYGUARD_SHOWING, isShowing); } }; private StatusBarStateController.StateListener mDozeCallback = new StatusBarStateController.StateListener() { @Override public void onDozingChanged(boolean isDozing) { if (DEBUG) { Log.d(TAG, "setDozing:" + isDozing); } setState(STATE_DOZING, isDozing); } }; @Inject protected CommunalHostViewController(CommunalHostView view) { protected CommunalHostViewController(@Main Executor mainExecutor, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, CommunalHostView view) { super(view); mMainExecutor = mainExecutor; mKeyguardStateController = keyguardStateController; mStatusBarStateController = statusBarStateController; mState = 0; if (mKeyguardStateController.isShowing()) { mState |= STATE_KEYGUARD_SHOWING; } if (mStatusBarStateController.isDozing()) { mState |= STATE_DOZING; } mKeyguardStateController.addCallback(mKeyguardCallback); mStatusBarStateController.addCallback(mDozeCallback); } @Override protected void onViewAttached() { mKeyguardStateController.removeCallback(mKeyguardCallback); mKeyguardStateController.addCallback(mKeyguardCallback); mStatusBarStateController.removeCallback(mDozeCallback); mStatusBarStateController.addCallback(mDozeCallback); } @Override protected void onViewDetached() { mKeyguardStateController.removeCallback(mKeyguardCallback); mStatusBarStateController.removeCallback(mDozeCallback); } private void setState(int stateFlag, boolean enabled) { final int existingState = mState; if (DEBUG) { Log.d(TAG, "setState flag:" + stateFlag + " enabled:" + enabled); } if (enabled) { mState |= stateFlag; } else { mState &= ~stateFlag; } if (DEBUG) { Log.d(TAG, "updated state:" + mState); } if (existingState != mState) { showSource(); } } private void showSource() { // Make sure all necessary states are present for showing communal and all invalid states // are absent mMainExecutor.execute(() -> { final CommunalSource currentSource = mLastSource != null ? mLastSource.get() : null; if ((mState & SHOW_COMMUNAL_VIEW_REQUIRED_STATES) == SHOW_COMMUNAL_VIEW_REQUIRED_STATES && (mState & SHOW_COMMUNAL_VIEW_INVALID_STATES) == 0 && currentSource != null) { mView.removeAllViews(); // Make view visible. mView.setVisibility(View.VISIBLE); final Context context = mView.getContext(); final ListenableFuture<View> listenableFuture = currentSource.requestCommunalView(context); if (listenableFuture == null) { Log.e(TAG, "could not request communal view"); return; } listenableFuture.addListener(() -> { try { final View view = listenableFuture.get(); view.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); mView.addView(view); } catch (Exception e) { Log.e(TAG, "could not obtain communal view through callback:" + e); } }, mMainExecutor); } else { mView.removeAllViews(); mView.setVisibility(View.INVISIBLE); } }); } /** * Sets whether the {@link CommunalHostView} is visible. * Instructs {@link CommunalHostViewController} to display provided source. * * @param visible {@code true} if the view should be shown, {@code false} otherwise. * @param source The new {@link CommunalSource}, {@code null} if not set. */ public void show(boolean visible) { mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); public void show(WeakReference<CommunalSource> source) { mLastSource = source; showSource(); } /** Loading
packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java 0 → 100644 +63 −0 Original line number Diff line number Diff line /* * 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.communal; import android.content.Context; import android.view.View; import com.google.common.util.concurrent.ListenableFuture; /** * {@link CommunalSource} defines an interface for working with a source for communal data. Clients * may request a communal surface that can be shown within a {@link android.view.SurfaceView}. * Callbacks may also be registered to listen to state changes. */ public interface CommunalSource { /** * Requests a communal surface that can be displayed inside {@link CommunalHostView}. * * @param context The {@link View} {@link Context} to build the resulting view from * @return A future that can be listened upon for the resulting {@link View}. The value will be * {@code null} in case of a failure. */ ListenableFuture<View> requestCommunalView(Context context); /** * Adds a {@link Callback} to receive future status updates regarding this * {@link CommunalSource}. * * @param callback The {@link Callback} to be added. */ void addCallback(Callback callback); /** * Removes a {@link Callback} from receiving future updates. * * @param callback The {@link Callback} to be removed. */ void removeCallback(Callback callback); /** * An interface for receiving updates on the state of the {@link CommunalSource}. */ interface Callback { /** * Invoked when the {@link CommunalSource} is no longer available for use. */ void onDisconnected(); } }
packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java 0 → 100644 +116 −0 Original line number Diff line number Diff line /* * 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.communal; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.dagger.SysUISingleton; import com.google.android.collect.Lists; import java.lang.ref.WeakReference; import java.util.ArrayList; import javax.inject.Inject; /** * A Monitor for reporting a {@link CommunalSource} presence. */ @SysUISingleton public class CommunalSourceMonitor { // A list of {@link Callback} that have registered to receive updates. private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList(); private CommunalSource mCurrentSource; private CommunalSource.Callback mSourceCallback = new CommunalSource.Callback() { @Override public void onDisconnected() { // Clear source reference. setSource(null /* source */); } }; @VisibleForTesting @Inject public CommunalSourceMonitor() { } /** * Sets the current {@link CommunalSource}, informing any callbacks. Any existing * {@link CommunalSource} will be disconnected. * * @param source The new {@link CommunalSource}. */ public void setSource(CommunalSource source) { if (mCurrentSource != null) { mCurrentSource.removeCallback(mSourceCallback); } mCurrentSource = source; // If the new source is valid, inform registered Callbacks of its presence. for (int i = 0; i < mCallbacks.size(); i++) { Callback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onSourceAvailable( mCurrentSource != null ? new WeakReference<>(mCurrentSource) : null); } } // Add callback to be informed when the source disconnects. if (mCurrentSource != null) { mCurrentSource.addCallback(mSourceCallback); } } /** * Adds a {@link Callback} to receive {@link CommunalSource} updates. * * @param callback The {@link Callback} to add. */ public void addCallback(Callback callback) { mCallbacks.add(new WeakReference<>(callback)); // Inform the callback of any already present CommunalSource. if (mCurrentSource != null) { callback.onSourceAvailable(new WeakReference<>(mCurrentSource)); } } /** * Removes the specified {@link Callback} from receive future updates if present. * * @param callback The {@link Callback} to add. */ public void removeCallback(Callback callback) { mCallbacks.removeIf(el -> el.get() == callback); } /** * Interface implemented to be notified when new {@link CommunalSource} become available. */ public interface Callback { /** * Called when a new {@link CommunalSource} has been registered. This will also be invoked * when a {@link Callback} is first registered and a {@link CommunalSource} is already * registered. * * @param source The new {@link CommunalSource}. */ void onSourceAvailable(WeakReference<CommunalSource> source); } }
packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +67 −10 Original line number Diff line number Diff line Loading @@ -108,6 +108,8 @@ import com.android.systemui.classifier.Classifier; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.communal.CommunalHostView; import com.android.systemui.communal.CommunalHostViewController; import com.android.systemui.communal.CommunalSource; import com.android.systemui.communal.CommunalSourceMonitor; import com.android.systemui.communal.dagger.CommunalViewComponent; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; Loading Loading @@ -177,6 +179,7 @@ import com.android.wm.shell.animation.FlingAnimationUtils; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; import java.util.List; Loading Loading @@ -318,6 +321,7 @@ public class NotificationPanelViewController extends PanelViewController { private final PulseExpansionHandler mPulseExpansionHandler; private final KeyguardBypassController mKeyguardBypassController; private final KeyguardUpdateMonitor mUpdateMonitor; private final CommunalSourceMonitor mCommunalSourceMonitor; private final ConversationNotificationManager mConversationNotificationManager; private final AuthController mAuthController; private final MediaHierarchyManager mMediaHierarchyManager; Loading Loading @@ -357,7 +361,7 @@ public class NotificationPanelViewController extends PanelViewController { @VisibleForTesting QS mQs; private FrameLayout mQsFrame; @Nullable private CommunalHostViewController mCommunalHostViewController; private CommunalHostViewController mCommunalViewController; private KeyguardStatusViewController mKeyguardStatusViewController; private LockIconViewController mLockIconViewController; private NotificationsQuickSettingsContainer mNotificationContainerParent; Loading @@ -369,7 +373,7 @@ public class NotificationPanelViewController extends PanelViewController { private VelocityTracker mQsVelocityTracker; private boolean mQsTracking; private CommunalHostView mCommunalHostView; private CommunalHostView mCommunalView; /** * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and Loading Loading @@ -518,6 +522,24 @@ public class NotificationPanelViewController extends PanelViewController { mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN); private final NotificationEntryManager mEntryManager; private final CommunalSourceMonitor.Callback mCommunalSourceMonitorCallback = new CommunalSourceMonitor.Callback() { @Override public void onSourceAvailable(WeakReference<CommunalSource> source) { setCommunalSource(source); } }; private WeakReference<CommunalSource> mCommunalSource; private final CommunalSource.Callback mCommunalSourceCallback = new CommunalSource.Callback() { @Override public void onDisconnected() { setCommunalSource(null /*source*/); } }; private final CommandQueue mCommandQueue; private final NotificationLockscreenUserManager mLockscreenUserManager; private final UserManager mUserManager; Loading Loading @@ -700,7 +722,8 @@ public class NotificationPanelViewController extends PanelViewController { DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper, LatencyTracker latencyTracker, PowerManager powerManager, AccessibilityManager accessibilityManager, @DisplayId int displayId, KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger, KeyguardUpdateMonitor keyguardUpdateMonitor, CommunalSourceMonitor communalSourceMonitor, MetricsLogger metricsLogger, ActivityManager activityManager, ConfigurationController configurationController, Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder, Loading Loading @@ -800,6 +823,7 @@ public class NotificationPanelViewController extends PanelViewController { mThemeResId = mView.getContext().getThemeResId(); mKeyguardBypassController = bypassController; mUpdateMonitor = keyguardUpdateMonitor; mCommunalSourceMonitor = communalSourceMonitor; mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); KeyguardStateController.Callback keyguardMonitorCallback = Loading Loading @@ -861,7 +885,7 @@ public class NotificationPanelViewController extends PanelViewController { loadDimens(); mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header); mBigClockContainer = mView.findViewById(R.id.big_clock_container); mCommunalHostView = mView.findViewById(R.id.communal_host); mCommunalView = mView.findViewById(R.id.communal_host); UserAvatarView userAvatarView = null; KeyguardUserSwitcherView keyguardUserSwitcherView = null; Loading @@ -881,7 +905,7 @@ public class NotificationPanelViewController extends PanelViewController { userAvatarView, mKeyguardStatusBar, keyguardUserSwitcherView, mCommunalHostView); mCommunalView); mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent); NotificationStackScrollLayout stackScrollLayout = mView.findViewById( R.id.notification_stack_scroller); Loading Loading @@ -998,7 +1022,7 @@ public class NotificationPanelViewController extends PanelViewController { if (communalView != null) { CommunalViewComponent communalViewComponent = mCommunalViewComponentFactory.build(communalView); mCommunalHostViewController = mCommunalViewController = communalViewComponent.getCommunalHostViewController(); } Loading Loading @@ -1067,6 +1091,7 @@ public class NotificationPanelViewController extends PanelViewController { ensureAllViewsHaveIds(mNotificationContainerParent); ConstraintSet constraintSet = new ConstraintSet(); constraintSet.clone(mNotificationContainerParent); if (mShouldUseSplitNotificationShade) { // width = 0 to take up all available space within constraints qsWidth = 0; Loading Loading @@ -1161,7 +1186,7 @@ public class NotificationPanelViewController extends PanelViewController { mBigClockContainer.removeAllViews(); updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView, mKeyguardStatusBar, keyguardUserSwitcherView, mCommunalHostView); mKeyguardStatusBar, keyguardUserSwitcherView, mCommunalView); // Update keyguard bottom area int index = mView.indexOfChild(mKeyguardBottomArea); Loading Loading @@ -1371,8 +1396,8 @@ public class NotificationPanelViewController extends PanelViewController { mClockPositionResult.clockX, mClockPositionResult.clockY, mClockPositionResult.clockScale, animateClock); // CommunalView's height is constrained to KeyguardStatusView. Match Y offset as well. if (mCommunalHostViewController != null) { mCommunalHostViewController.updatePositionY(mClockPositionResult.clockY, animateClock); if (mCommunalViewController != null) { mCommunalViewController.updatePositionY(mClockPositionResult.clockY, animateClock); } if (mKeyguardQsUserSwitchController != null) { mKeyguardQsUserSwitchController.updatePosition( Loading @@ -1393,7 +1418,9 @@ public class NotificationPanelViewController extends PanelViewController { private void updateKeyguardStatusViewAlignment(boolean animate) { boolean hasVisibleNotifications = mNotificationStackScrollLayoutController .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia(); boolean shouldBeCentered = !mShouldUseSplitNotificationShade || !hasVisibleNotifications; boolean hasCommunalSurface = mCommunalSource != null && mCommunalSource.get() != null; boolean shouldBeCentered = !mShouldUseSplitNotificationShade || (!hasVisibleNotifications && !hasCommunalSurface); if (mStatusViewCentered != shouldBeCentered) { mStatusViewCentered = shouldBeCentered; ConstraintSet constraintSet = new ConstraintSet(); Loading Loading @@ -1436,6 +1463,11 @@ public class NotificationPanelViewController extends PanelViewController { * @return the maximum keyguard notifications that can fit on the screen */ private int computeMaxKeyguardNotifications() { // Do not show any notifications on the keyguard if a communal source is set. if (mCommunalSource != null && mCommunalSource.get() != null) { return 0; } float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding(); int notificationPadding = Math.max( 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height)); Loading Loading @@ -4540,6 +4572,27 @@ public class NotificationPanelViewController extends PanelViewController { setExpandedFraction(1f); } private void setCommunalSource(WeakReference<CommunalSource> source) { CommunalSource existingSource = mCommunalSource != null ? mCommunalSource.get() : null; if (existingSource != null) { existingSource.removeCallback(mCommunalSourceCallback); mCommunalViewController.show(null /*source*/); } mCommunalSource = source; CommunalSource currentSource = mCommunalSource != null ? mCommunalSource.get() : null; // Set source and register callback if (currentSource != null && mCommunalViewController != null) { currentSource.addCallback(mCommunalSourceCallback); mCommunalViewController.show(source); } updateKeyguardStatusViewAlignment(true /*animate*/); updateMaxDisplayedNotifications(true /*recompute*/); } /** * Sets the overstretch amount in raw pixels when dragging down. */ Loading @@ -4556,6 +4609,7 @@ public class NotificationPanelViewController extends PanelViewController { mStatusBarStateController.addCallback(mStatusBarStateListener); mConfigurationController.addCallback(mConfigurationListener); mUpdateMonitor.registerCallback(mKeyguardUpdateCallback); mCommunalSourceMonitor.addCallback(mCommunalSourceMonitorCallback); // Theme might have changed between inflating this view and attaching it to the // window, so // force a call to onThemeChanged Loading @@ -4573,6 +4627,9 @@ public class NotificationPanelViewController extends PanelViewController { mStatusBarStateController.removeCallback(mStatusBarStateListener); mConfigurationController.removeCallback(mConfigurationListener); mUpdateMonitor.removeCallback(mKeyguardUpdateCallback); mCommunalSourceMonitor.removeCallback(mCommunalSourceMonitorCallback); // Clear source when detached. setCommunalSource(null /*source*/); mFalsingManager.removeTapListener(mFalsingTapListener); } } Loading
packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java +45 −3 File changed.Preview size limit exceeded, changes collapsed. Show changes