Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8fc18b9d authored by Bryce Lee's avatar Bryce Lee Committed by Android (Google) Code Review
Browse files

Merge "CommunalSource Introduction."

parents ce8b17cd edd761c3
Loading
Loading
Loading
Loading
+143 −5
Original line number Diff line number Diff line
@@ -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();
    }

    /**
+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();
    }
}
+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);
    }
}
+67 −10
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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
@@ -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;
@@ -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,
@@ -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 =
@@ -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;
@@ -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);
@@ -998,7 +1022,7 @@ public class NotificationPanelViewController extends PanelViewController {
        if (communalView != null) {
            CommunalViewComponent communalViewComponent =
                    mCommunalViewComponentFactory.build(communalView);
            mCommunalHostViewController =
            mCommunalViewController =
                    communalViewComponent.getCommunalHostViewController();
        }

@@ -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;
@@ -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);
@@ -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(
@@ -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();
@@ -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));
@@ -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.
     */
@@ -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
@@ -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);
        }
    }
+45 −3

File changed.

Preview size limit exceeded, changes collapsed.

Loading