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

Commit ea7cb6fd authored by Will's avatar Will
Browse files

Add logic to show all status bar icons.

This change adds all the logic to show/hide the dream overlay status bar
icons.

Test: atest DreamOverlayStatusBarControllerTest
Bug: 216831310
Change-Id: I6e138c3f7e1cabbb92ebcb5032e057ef31f41f66
parent ffe63c31
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@
        android:layout_width="@dimen/dream_overlay_notification_indicator_size"
        android:layout_height="@dimen/dream_overlay_notification_indicator_size"
        android:visibility="gone"
        android:contentDescription="@string/dream_overlay_status_bar_notification_indicator"
        app:dotColor="@android:color/white"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
@@ -68,7 +67,7 @@
            android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
            android:layout_height="match_parent"
            android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
            android:src="@drawable/ic_remove_circle"
            android:src="@drawable/ic_qs_dnd_on"
            android:tint="@android:color/white"
            android:visibility="gone"
            android:contentDescription="@string/dream_overlay_status_bar_priority_mode" />
+5 −2
Original line number Diff line number Diff line
@@ -2421,6 +2421,9 @@
    <string name="dream_overlay_status_bar_assistant_guest_mode_enabled">Assistant guest mode enabled</string>
    <!-- Content description for the camera and mic off icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
    <string name="dream_overlay_status_bar_camera_mic_off">Camera and mic are off</string>
    <!-- Content description for the camera and mic off icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
    <string name="dream_overlay_status_bar_notification_indicator">There are notifications</string>
    <!-- Content description for the notifications indicator icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
    <string name="dream_overlay_status_bar_notification_indicator">{count, plural,
    =1 {# notification}
    other {# notifications}
    }</string>
</resources>
+52 −12
Original line number Diff line number Diff line
@@ -16,23 +16,44 @@

package com.android.systemui.dreams;

import android.annotation.IntDef;
import android.annotation.Nullable;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;

import androidx.constraintlayout.widget.ConstraintLayout;

import com.android.internal.util.Preconditions;
import com.android.systemui.R;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * {@link DreamOverlayStatusBarView} is the view responsible for displaying the status bar in a
 * dream. The status bar displays conditional status icons such as "priority mode" and "no wifi".
 */
public class DreamOverlayStatusBarView extends ConstraintLayout {

    private ImageView mWifiStatusView;
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = { "STATUS_ICON_" }, value = {
            STATUS_ICON_NOTIFICATIONS,
            STATUS_ICON_WIFI_UNAVAILABLE,
            STATUS_ICON_ALARM_SET,
            STATUS_ICON_MIC_CAMERA_DISABLED,
            STATUS_ICON_PRIORITY_MODE_ON
    })
    public @interface StatusIconType {}
    public static final int STATUS_ICON_NOTIFICATIONS = 0;
    public static final int STATUS_ICON_WIFI_UNAVAILABLE = 1;
    public static final int STATUS_ICON_ALARM_SET = 2;
    public static final int STATUS_ICON_MIC_CAMERA_DISABLED = 3;
    public static final int STATUS_ICON_PRIORITY_MODE_ON = 4;

    private final Map<Integer, View> mStatusIcons = new HashMap<>();

    public DreamOverlayStatusBarView(Context context) {
        this(context, null);
@@ -55,16 +76,35 @@ public class DreamOverlayStatusBarView extends ConstraintLayout {
    protected void onFinishInflate() {
        super.onFinishInflate();

        mWifiStatusView = Preconditions.checkNotNull(findViewById(R.id.dream_overlay_wifi_status),
                "R.id.dream_overlay_wifi_status must not be null");
        mStatusIcons.put(STATUS_ICON_WIFI_UNAVAILABLE,
                fetchStatusIconForResId(R.id.dream_overlay_wifi_status));
        mStatusIcons.put(STATUS_ICON_ALARM_SET,
                fetchStatusIconForResId(R.id.dream_overlay_alarm_set));
        mStatusIcons.put(STATUS_ICON_MIC_CAMERA_DISABLED,
                fetchStatusIconForResId(R.id.dream_overlay_camera_mic_off));
        mStatusIcons.put(STATUS_ICON_NOTIFICATIONS,
                fetchStatusIconForResId(R.id.dream_overlay_notification_indicator));
        mStatusIcons.put(STATUS_ICON_PRIORITY_MODE_ON,
                fetchStatusIconForResId(R.id.dream_overlay_priority_mode));
    }

    /**
     * Whether to show the wifi status icon.
     * @param show True if the wifi status icon should be shown.
     */
    void showWifiStatus(boolean show) {
        // Only show the wifi status icon when wifi isn't available.
        mWifiStatusView.setVisibility(show ? View.VISIBLE : View.GONE);
    void showIcon(@StatusIconType int iconType, boolean show) {
        showIcon(iconType, show, null);
    }

    void showIcon(@StatusIconType int iconType, boolean show, @Nullable String contentDescription) {
        View icon = mStatusIcons.get(iconType);
        if (icon == null) {
            return;
        }
        if (show && contentDescription != null) {
            icon.setContentDescription(contentDescription);
        }
        icon.setVisibility(show ? View.VISIBLE : View.GONE);
    }

    private View fetchStatusIconForResId(int resId) {
        final View statusIcon = findViewById(resId);
        return Objects.requireNonNull(statusIcon);
    }
}
+170 −36
Original line number Diff line number Diff line
@@ -16,19 +16,35 @@

package com.android.systemui.dreams;

import android.annotation.IntDef;
import android.app.AlarmManager;
import android.content.res.Resources;
import android.hardware.SensorPrivacyManager;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;

import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.text.format.DateFormat;
import android.util.PluralsMessageFormatter;

import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.time.DateFormatUtil;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Locale;
import java.util.Map;

import javax.inject.Inject;

@@ -37,19 +53,15 @@ import javax.inject.Inject;
 */
@DreamOverlayComponent.DreamOverlayScope
public class DreamOverlayStatusBarViewController extends ViewController<DreamOverlayStatusBarView> {
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = { "WIFI_STATUS_" }, value = {
            WIFI_STATUS_UNKNOWN,
            WIFI_STATUS_UNAVAILABLE,
            WIFI_STATUS_AVAILABLE
    })
    private @interface WifiStatus {}
    private static final int WIFI_STATUS_UNKNOWN = 0;
    private static final int WIFI_STATUS_UNAVAILABLE = 1;
    private static final int WIFI_STATUS_AVAILABLE = 2;

    private final ConnectivityManager mConnectivityManager;
    private final TouchInsetManager.TouchInsetSession mTouchInsetSession;
    private final NextAlarmController mNextAlarmController;
    private final AlarmManager mAlarmManager;
    private final Resources mResources;
    private final DateFormatUtil mDateFormatUtil;
    private final IndividualSensorPrivacyController mSensorPrivacyController;
    private final NotificationListener mNotificationListener;
    private final ZenModeController mZenModeController;

    private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
            .clearCapabilities()
@@ -59,61 +71,183 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
        @Override
        public void onCapabilitiesChanged(
                Network network, NetworkCapabilities networkCapabilities) {
            onWifiAvailabilityChanged(
                    networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
            updateWifiUnavailableStatusIcon();
        }

        @Override
        public void onAvailable(Network network) {
            onWifiAvailabilityChanged(true);
            updateWifiUnavailableStatusIcon();
        }

        @Override
        public void onLost(Network network) {
            onWifiAvailabilityChanged(false);
            updateWifiUnavailableStatusIcon();
        }
    };

    private final IndividualSensorPrivacyController.Callback mSensorCallback =
            (sensor, blocked) -> updateMicCameraBlockedStatusIcon();

    private final NextAlarmController.NextAlarmChangeCallback mNextAlarmCallback =
            nextAlarm -> updateAlarmStatusIcon();

    private final NotificationHandler mNotificationHandler = new NotificationHandler() {
        @Override
        public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
            updateNotificationsStatusIcon();
        }

        @Override
        public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
            updateNotificationsStatusIcon();
        }

        @Override
        public void onNotificationRemoved(
                StatusBarNotification sbn,
                RankingMap rankingMap,
                int reason) {
            updateNotificationsStatusIcon();
        }

        @Override
        public void onNotificationRankingUpdate(RankingMap rankingMap) {
        }

        @Override
        public void onNotificationsInitialized() {
            updateNotificationsStatusIcon();
        }
    };

    private @WifiStatus int mWifiStatus = WIFI_STATUS_UNKNOWN;
    private final ZenModeController.Callback mZenModeCallback = new ZenModeController.Callback() {
        @Override
        public void onZenChanged(int zen) {
            updatePriorityModeStatusIcon();
        }
    };

    @Inject
    public DreamOverlayStatusBarViewController(
            DreamOverlayStatusBarView view,
            @Main Resources resources,
            ConnectivityManager connectivityManager,
            TouchInsetManager.TouchInsetSession touchInsetSession) {
            TouchInsetManager.TouchInsetSession touchInsetSession,
            AlarmManager alarmManager,
            NextAlarmController nextAlarmController,
            DateFormatUtil dateFormatUtil,
            IndividualSensorPrivacyController sensorPrivacyController,
            NotificationListener notificationListener,
            ZenModeController zenModeController) {
        super(view);
        mResources = resources;
        mConnectivityManager = connectivityManager;
        mTouchInsetSession = touchInsetSession;
        mAlarmManager = alarmManager;
        mNextAlarmController = nextAlarmController;
        mDateFormatUtil = dateFormatUtil;
        mSensorPrivacyController = sensorPrivacyController;
        mNotificationListener = notificationListener;
        mZenModeController = zenModeController;

        // Handlers can be added to NotificationListener, but apparently they can't be removed. So
        // add the handler here in the constructor rather than in onViewAttached to avoid confusion.
        mNotificationListener.addNotificationHandler(mNotificationHandler);
    }

    @Override
    protected void onViewAttached() {
        updateNotificationsStatusIcon();

        mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
        updateWifiUnavailableStatusIcon();

        mNextAlarmController.addCallback(mNextAlarmCallback);
        updateAlarmStatusIcon();

        mSensorPrivacyController.addCallback(mSensorCallback);
        updateMicCameraBlockedStatusIcon();

        mZenModeController.addCallback(mZenModeCallback);
        updatePriorityModeStatusIcon();

        NetworkCapabilities capabilities =
                mConnectivityManager.getNetworkCapabilities(
                        mConnectivityManager.getActiveNetwork());
        onWifiAvailabilityChanged(
                capabilities != null
                        && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
        mTouchInsetSession.addViewToTracking(mView);
    }

    @Override
    protected void onViewDetached() {
        mZenModeController.removeCallback(mZenModeCallback);
        mSensorPrivacyController.removeCallback(mSensorCallback);
        mNextAlarmController.removeCallback(mNextAlarmCallback);
        mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
        mTouchInsetSession.clear();
    }

    /**
     * Wifi availability has changed. Update the wifi status icon as appropriate.
     * @param available Whether wifi is available.
     */
    private void onWifiAvailabilityChanged(boolean available) {
        final int newWifiStatus = available ? WIFI_STATUS_AVAILABLE : WIFI_STATUS_UNAVAILABLE;
        if (mWifiStatus != newWifiStatus) {
            mWifiStatus = newWifiStatus;
            mView.showWifiStatus(mWifiStatus == WIFI_STATUS_UNAVAILABLE);
    private void updateWifiUnavailableStatusIcon() {
        final NetworkCapabilities capabilities =
                mConnectivityManager.getNetworkCapabilities(
                        mConnectivityManager.getActiveNetwork());
        final boolean available = capabilities != null
                && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
        mView.showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available);
    }

    private void updateAlarmStatusIcon() {
        final AlarmManager.AlarmClockInfo alarm =
                mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
        final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
        mView.showIcon(
                DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET,
                hasAlarm,
                hasAlarm ? buildAlarmContentDescription(alarm) : null);
    }

    private String buildAlarmContentDescription(AlarmManager.AlarmClockInfo alarm) {
        final String skeleton = mDateFormatUtil.is24HourFormat() ? "EHm" : "Ehma";
        final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
        final String dateString = DateFormat.format(pattern, alarm.getTriggerTime()).toString();

        return mResources.getString(R.string.accessibility_quick_settings_alarm, dateString);
    }

    private void updateMicCameraBlockedStatusIcon() {
        final boolean micBlocked = mSensorPrivacyController
                .isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE);
        final boolean cameraBlocked = mSensorPrivacyController
                .isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA);
        mView.showIcon(
                DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
                micBlocked && cameraBlocked);
    }

    private void updateNotificationsStatusIcon() {
        if (mView == null) {
            // It is possible for this method to be called before the view is attached, which makes
            // null-checking necessary.
            return;
        }

        final StatusBarNotification[] notifications =
                mNotificationListener.getActiveNotifications();
        final int notificationCount = notifications != null ? notifications.length : 0;
        mView.showIcon(
                DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS,
                notificationCount > 0,
                notificationCount > 0
                        ? buildNotificationsContentDescription(notificationCount)
                        : null);
    }

    private String buildNotificationsContentDescription(int notificationCount) {
        return PluralsMessageFormatter.format(
                mResources,
                Map.of("count", notificationCount),
                R.string.dream_overlay_status_bar_notification_indicator);
    }

    private void updatePriorityModeStatusIcon() {
        mView.showIcon(
                DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON,
                mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF);
    }
}
+251 −18

File changed.

Preview size limit exceeded, changes collapsed.