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

Commit ef60981b authored by William Leshner's avatar William Leshner Committed by Android (Google) Code Review
Browse files

Merge "Add logic to show all status bar icons." into tm-dev

parents 045bc655 ea7cb6fd
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.