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

Commit 1d13c765 authored by Fabián Kozynski's avatar Fabián Kozynski
Browse files

Creates API for QS that avoids getHeader

As we are migrating to use Compose inside the fragment, we need an API
that will not require QS to have an actual header view. This new API
extracts all of the usages of QS.getHeader and replaces them (flagged)
with APIs that directly query the necessary values.

Flag: com.android.systemui.qs_ui_refactor_compose_fragment
Bug: 353254345
Test: atest QSImplTest QuickSettingsControllerImplTest
Test: atest NotificationStackScrollLayoutTest
Change-Id: Ie0425848a57943e8b6799d574427a1c7cfe73b9e
parent 8a225631
Loading
Loading
Loading
Loading
+39 −1
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@

package com.android.systemui.plugins.qs;

import android.graphics.Rect;
import android.view.View;

import androidx.annotation.FloatRange;
@@ -35,7 +36,7 @@ public interface QS extends FragmentBase {

    String ACTION = "com.android.systemui.action.PLUGIN_QS";

    int VERSION = 15;
    int VERSION = 16;

    String TAG = "QS";

@@ -89,8 +90,45 @@ public interface QS extends FragmentBase {
     */
    int getHeightDiff();

    /**
     * Returns the header view that contains QQS. This might return null (or throw) if there's no
     * actual header view.
     */
    View getHeader();

    /**
     * Returns the top of the header view that contains QQS wrt to the container view
     */
    int getHeaderTop();

    /**
     * Returns the bottom of the header view that contains QQS wrt to the container view
     */
    int getHeaderBottom();

    /**
     * Returns the left bound of the header view that contains QQS wrt to the container view
     */
    int getHeaderLeft();

    /**
     * Fills outBounds with the bounds of the header view (container of QQS) on the screen
     */
    void getHeaderBoundsOnScreen(Rect outBounds);

    /**
     * Returns the height of the header view that contains QQS. It defaults to bottom - top.
     */
    default int getHeaderHeight() {
        return getHeaderBottom() - getHeaderTop();
    }

    /**
     * Returns whether the header view that contains QQS is shown on screen (similar semantics to
     * View.isShown).
     */
    boolean isHeaderShown();

    default void setHasNotifications(boolean hasNotifications) {
    }

+48 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.qs;

import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Trace;
import android.view.ContextThemeWrapper;
@@ -30,6 +31,7 @@ import androidx.annotation.Nullable;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.qs.dagger.QSFragmentComponent;
import com.android.systemui.qs.flags.QSComposeFragment;
import com.android.systemui.res.R;
import com.android.systemui.settings.brightness.MirrorController;
import com.android.systemui.util.LifecycleFragment;
@@ -103,6 +105,7 @@ public class QSFragmentLegacy extends LifecycleFragment implements QS {

    @Override
    public View getHeader() {
        QSComposeFragment.assertInLegacyMode();
        if (mQsImpl != null) {
            return mQsImpl.getHeader();
        } else {
@@ -110,6 +113,51 @@ public class QSFragmentLegacy extends LifecycleFragment implements QS {
        }
    }

    @Override
    public int getHeaderTop() {
        if (mQsImpl != null) {
            return mQsImpl.getHeaderTop();
        } else {
            return 0;
        }
    }

    @Override
    public int getHeaderBottom() {
        if (mQsImpl != null) {
            return mQsImpl.getHeaderBottom();
        } else {
            return 0;
        }
    }

    @Override
    public int getHeaderLeft() {
        if (mQsImpl != null) {
            return mQsImpl.getHeaderLeft();
        } else {
            return 0;
        }
    }

    @Override
    public void getHeaderBoundsOnScreen(Rect outBounds) {
        if (mQsImpl != null) {
            mQsImpl.getHeaderBoundsOnScreen(outBounds);
        } else {
            outBounds.setEmpty();
        }
    }

    @Override
    public boolean isHeaderShown() {
        if (mQsImpl != null) {
            return mQsImpl.isHeaderShown();
        } else {
            return false;
        }
    }

    @Override
    public void setHasNotifications(boolean hasNotifications) {
        if (mQsImpl != null) {
+27 −0
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSComponent;
import com.android.systemui.qs.flags.QSComposeFragment;
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.res.R;
@@ -355,9 +356,35 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl

    @Override
    public View getHeader() {
        QSComposeFragment.assertInLegacyMode();
        return mHeader;
    }

    @Override
    public int getHeaderTop() {
        return mHeader.getTop();
    }

    @Override
    public int getHeaderBottom() {
        return mHeader.getBottom();
    }

    @Override
    public int getHeaderLeft() {
        return mHeader.getLeft();
    }

    @Override
    public void getHeaderBoundsOnScreen(Rect outBounds) {
        mHeader.getBoundsOnScreen(outBounds);
    }

    @Override
    public boolean isHeaderShown() {
        return mHeader.isShown();
    }

    @Override
    public void setHasNotifications(boolean hasNotifications) {
    }
+25 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.shade

import android.graphics.Rect

class QSHeaderBoundsProvider(
    val leftProvider: () -> Int,
    val heightProvider: () -> Int,
    val boundsOnScreenProvider: (Rect) -> Unit,
)
+56 −9
Original line number Diff line number Diff line
@@ -43,7 +43,6 @@ import android.util.Log;
import android.util.MathUtils;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowInsets;
@@ -75,6 +74,7 @@ import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.flags.QSComposeFragment;
import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.screenrecord.RecordingController;
@@ -110,6 +110,8 @@ import dalvik.annotation.optimization.NeverCompile;

import dagger.Lazy;

import kotlin.Unit;

import java.io.PrintWriter;

import javax.inject.Inject;
@@ -494,7 +496,15 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
    }

    int getHeaderHeight() {
        return isQsFragmentCreated() ? mQs.getHeader().getHeight() : 0;
        if (isQsFragmentCreated()) {
            if (QSComposeFragment.isEnabled()) {
                return mQs.getHeaderHeight();
            } else {
                return mQs.getHeader().getHeight();
            }
        } else {
            return 0;
        }
    }

    private boolean isRemoteInputActiveWithKeyboardUp() {
@@ -664,14 +674,26 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
                && mKeyguardBypassController.getBypassEnabled()) || mSplitShadeEnabled) {
            return false;
        }
        View header = keyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader();
        int headerTop, headerBottom;
        if (keyguardShowing || mQs == null) {
            headerTop = mKeyguardStatusBar.getTop();
            headerBottom = mKeyguardStatusBar.getBottom();
        } else {
            if (QSComposeFragment.isEnabled()) {
                headerTop = mQs.getHeaderTop();
                headerBottom = mQs.getHeaderBottom();
            } else {
                headerTop = mQs.getHeader().getTop();
                headerBottom = mQs.getHeader().getBottom();
            }
        }
        int frameTop = keyguardShowing
                || mQs == null ? 0 : mQsFrame.getTop();
        mInterceptRegion.set(
                /* left= */ (int) mQsFrame.getX(),
                /* top= */ header.getTop() + frameTop,
                /* top= */ headerTop + frameTop,
                /* right= */ (int) mQsFrame.getX() + mQsFrame.getWidth(),
                /* bottom= */ header.getBottom() + frameTop);
                /* bottom= */ headerBottom + frameTop);
        // Also allow QS to intercept if the touch is near the notch.
        mStatusBarTouchableRegionManager.updateRegionForNotch(mInterceptRegion);
        final boolean onHeader = mInterceptRegion.contains((int) x, (int) y);
@@ -718,9 +740,18 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
        if (mCollapsedOnDown || mBarState == KEYGUARD || getExpanded()) {
            return false;
        }
        View header = mQs == null ? mKeyguardStatusBar : mQs.getHeader();
        int headerBottom;
        if (mQs == null) {
            headerBottom = mKeyguardStatusBar.getBottom();
        } else {
            if (QSComposeFragment.isEnabled()) {
                headerBottom = mQs.getHeaderBottom();
            } else {
                headerBottom = mQs.getHeader().getBottom();
            }
        }
        return downX >= mQsFrame.getX() && downX <= mQsFrame.getX() + mQsFrame.getWidth()
                && downY <= header.getBottom();
                && downY <= headerBottom;
    }

    /** Closes the Qs customizer. */
@@ -2189,14 +2220,27 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
                        }
                    });
            mQs.setCollapsedMediaVisibilityChangedListener((visible) -> {
                if (mQs.getHeader().isShown()) {
                if (mQs.isHeaderShown()) {
                    setAnimateNextNotificationBounds(
                            StackStateAnimator.ANIMATION_DURATION_STANDARD, 0);
                    mNotificationStackScrollLayoutController.animateNextTopPaddingChange();
                }
            });
            mLockscreenShadeTransitionController.setQS(mQs);
            if (QSComposeFragment.isEnabled()) {
                QSHeaderBoundsProvider provider = new QSHeaderBoundsProvider(
                        mQs::getHeaderLeft,
                        mQs::getHeaderHeight,
                        rect -> {
                            mQs.getHeaderBoundsOnScreen(rect);
                            return Unit.INSTANCE;
                        }
                );

                mNotificationStackScrollLayoutController.setQsHeaderBoundsProvider(provider);
            } else {
                mNotificationStackScrollLayoutController.setQsHeader((ViewGroup) mQs.getHeader());
            }
            mQs.setScrollListener(mQsScrollListener);
            updateExpansion();
        }
@@ -2208,6 +2252,9 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
            // non-fragment and fragment code. Once we are using a fragment for the notification
            // panel, mQs will not need to be null cause it will be tied to the same lifecycle.
            if (fragment == mQs) {
                // Clear it to remove bindings to mQs from the provider.
                mNotificationStackScrollLayoutController.setQsHeaderBoundsProvider(null);
                mNotificationStackScrollLayoutController.setQsHeader(null);
                mQs = null;
            }
        }
Loading