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

Commit ddedd72a authored by Ioana Alexandru's avatar Ioana Alexandru Committed by Android (Google) Code Review
Browse files

Merge "Introduce new FooterView stack." into main

parents 7f68fdfe e0520b08
Loading
Loading
Loading
Loading
+70 −8
Original line number Diff line number Diff line
@@ -19,6 +19,9 @@ package com.android.systemui.statusbar.notification.footer.ui.view;
import static android.graphics.PorterDuff.Mode.SRC_ATOP;

import android.annotation.ColorInt;
import android.annotation.DrawableRes;
import android.annotation.StringRes;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
@@ -35,6 +38,7 @@ import androidx.annotation.NonNull;

import com.android.settingslib.Utils;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.row.FooterViewButton;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
@@ -44,6 +48,8 @@ import com.android.systemui.util.DumpUtilsKt;
import java.io.PrintWriter;

public class FooterView extends StackScrollerDecorView {
    private static final String TAG = "FooterView";

    private FooterViewButton mClearAllButton;
    private FooterViewButton mManageButton;
    private boolean mShowHistory;
@@ -57,6 +63,9 @@ public class FooterView extends StackScrollerDecorView {
    private String mSeenNotifsFilteredText;
    private Drawable mSeenNotifsFilteredIcon;

    private @StringRes int mMessageStringId;
    private @DrawableRes int mMessageIconId;

    public FooterView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
@@ -84,6 +93,50 @@ public class FooterView extends StackScrollerDecorView {
        });
    }

    /** Set the string for a message to be shown instead of the buttons. */
    public void setMessageString(@StringRes int messageId) {
        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return;
        if (mMessageStringId == messageId) {
            return; // nothing changed
        }
        mMessageStringId = messageId;
        updateMessageString();
    }

    private void updateMessageString() {
        if (mMessageStringId == 0) {
            return; // not initialized yet
        }
        String messageString = getContext().getString(mMessageStringId);
        mSeenNotifsFooterTextView.setText(messageString);
    }


    /** Set the icon to be shown before the message (see {@link #setMessageString(int)}). */
    public void setMessageIcon(@DrawableRes int iconId) {
        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return;
        if (mMessageIconId == iconId) {
            return; // nothing changed
        }
        mMessageIconId = iconId;
        updateMessageIcon();
    }

    private void updateMessageIcon() {
        if (mMessageIconId == 0) {
            return; // not initialized yet
        }
        int unlockIconSize = getResources()
                .getDimensionPixelSize(R.dimen.notifications_unseen_footer_icon_size);
        @SuppressLint("UseCompatLoadingForDrawables")
        Drawable messageIcon = getContext().getDrawable(mMessageIconId);
        if (messageIcon != null) {
            messageIcon.setBounds(0, 0, unlockIconSize, unlockIconSize);
            mSeenNotifsFooterTextView
                    .setCompoundDrawablesRelative(messageIcon, null, null, null);
        }
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
@@ -148,10 +201,12 @@ public class FooterView extends StackScrollerDecorView {
            mManageButton.setText(mManageNotificationText);
            mManageButton.setContentDescription(mManageNotificationText);
        }
        if (!FooterViewRefactor.isEnabled()) {
            mSeenNotifsFooterTextView.setText(mSeenNotifsFilteredText);
            mSeenNotifsFooterTextView
                    .setCompoundDrawablesRelative(mSeenNotifsFilteredIcon, null, null, null);
        }
    }

    /** Whether the start button shows "History" (true) or "Manage" (false). */
    public boolean isHistoryShown() {
@@ -167,6 +222,11 @@ public class FooterView extends StackScrollerDecorView {
                mContext.getString(R.string.accessibility_clear_all));
        updateResources();
        updateContent();

        if (FooterViewRefactor.isEnabled()) {
            updateMessageString();
            updateMessageIcon();
        }
    }

    /**
@@ -200,12 +260,14 @@ public class FooterView extends StackScrollerDecorView {
        mManageNotificationText = getContext().getString(R.string.manage_notifications_text);
        mManageNotificationHistoryText = getContext()
                .getString(R.string.manage_notifications_history_text);
        if (!FooterViewRefactor.isEnabled()) {
            int unlockIconSize = getResources()
                    .getDimensionPixelSize(R.dimen.notifications_unseen_footer_icon_size);
            mSeenNotifsFilteredText = getContext().getString(R.string.unlock_to_see_notif_text);
            mSeenNotifsFilteredIcon = getContext().getDrawable(R.drawable.ic_friction_lock_closed);
            mSeenNotifsFilteredIcon.setBounds(0, 0, unlockIconSize, unlockIconSize);
        }
    }

    @Override
    @NonNull
+43 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.statusbar.notification.footer.ui.viewbinder

import androidx.lifecycle.lifecycleScope
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.launch

/** Binds a [FooterView] to its [view model][FooterViewModel]. */
object FooterViewBinder {
    fun bind(
        footer: FooterView,
        viewModel: FooterViewModel,
    ): DisposableHandle {
        return footer.repeatWhenAttached {
            // Listen for changes when the view is attached.
            lifecycleScope.launch {
                viewModel.message.collect { message ->
                    footer.setFooterLabelVisible(message.visible)
                    footer.setMessageString(message.messageId)
                    footer.setMessageIcon(message.iconId)
                }
            }
        }
    }
}
+27 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.statusbar.notification.footer.ui.viewmodel

import android.annotation.DrawableRes
import android.annotation.StringRes

/** A ViewModel for the string message that can be shown in the footer. */
data class FooterMessageViewModel(
    @StringRes val messageId: Int,
    @DrawableRes val iconId: Int,
    val visible: Boolean,
)
+45 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.statusbar.notification.footer.ui.viewmodel

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

/** ViewModel for [FooterView]. */
@SysUISingleton
class FooterViewModel
@Inject
constructor(seenNotificationsInteractor: SeenNotificationsInteractor) {
    init {
        /* Check if */ FooterViewRefactor.isUnexpectedlyInLegacyMode()
    }

    val message: Flow<FooterMessageViewModel> =
        seenNotificationsInteractor.hasFilteredOutSeenNotifications.map { hasFilteredOutNotifs ->
            FooterMessageViewModel(
                messageId = R.string.unlock_to_see_notif_text,
                iconId = R.drawable.ic_friction_lock_closed,
                visible = hasFilteredOutNotifs,
            )
        }
}
+25 −13
Original line number Diff line number Diff line
@@ -107,6 +107,7 @@ import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -694,8 +695,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
        super.onFinishInflate();

        inflateEmptyShadeView();
        if (!FooterViewRefactor.isEnabled()) {
            inflateFooterView();
        }
    }

    /**
     * Sets whether keyguard bypass is enabled. If true, this layout will be rendered in bypass
@@ -729,9 +732,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
    }

    void reinflateViews() {
        if (!FooterViewRefactor.isEnabled()) {
            inflateFooterView();
        inflateEmptyShadeView();
            updateFooter();
        }
        inflateEmptyShadeView();
        mSectionsManager.reinflateViews();
    }

@@ -746,7 +751,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable

    @VisibleForTesting
    public void updateFooter() {
        if (mFooterView == null) {
        if (mFooterView == null || mController == null) {
            return;
        }
        // TODO: move this logic to controller, which will invoke updateFooterView directly
@@ -4546,7 +4551,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
        return mFooterView != null && mFooterView.isHistoryShown();
    }

    void setFooterView(@NonNull FooterView footerView) {
    /** Bind the {@link FooterView} to the NSSL. */
    public void setFooterView(@NonNull FooterView footerView) {
        int index = -1;
        if (mFooterView != null) {
            index = indexOfChild(mFooterView);
@@ -4557,6 +4563,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
        if (mManageButtonClickListener != null) {
            mFooterView.setManageButtonClickListener(mManageButtonClickListener);
        }
        mFooterView.setClearAllButtonClickListener(v -> {
            if (mFooterClearAllListener != null) {
                mFooterClearAllListener.onClearAll();
            }
            clearNotifications(ROWS_ALL, true /* closeShade */);
            footerView.setSecondaryVisible(false /* visible */, true /* animate */);
        });
        if (FooterViewRefactor.isEnabled()) {
            updateFooter();
        }
    }

    public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
@@ -4619,8 +4635,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
        mFooterView.setVisible(visible, animate);
        mFooterView.setSecondaryVisible(showDismissView, animate);
        mFooterView.showHistory(showHistory);
        if (!FooterViewRefactor.isEnabled()) {
            mFooterView.setFooterLabelVisible(mHasFilteredOutSeenNotifications);
        }
    }

    @VisibleForTesting
    public void setClearAllInProgress(boolean clearAllInProgress) {
@@ -5370,15 +5388,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable

    @VisibleForTesting
    protected void inflateFooterView() {
        FooterViewRefactor.assertInLegacyMode();
        FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate(
                R.layout.status_bar_notification_footer, this, false);
        footerView.setClearAllButtonClickListener(v -> {
            if (mFooterClearAllListener != null) {
                mFooterClearAllListener.onClearAll();
            }
            clearNotifications(ROWS_ALL, true /* closeShade */);
            footerView.setSecondaryVisible(false /* visible */, true /* animate */);
        });
        setFooterView(footerView);
    }

Loading