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

Commit a5e0f415 authored by Dan Sandler's avatar Dan Sandler
Browse files

SystemUI support for notification visibility.

In this implementation, DISABLE_NOTIFICATION_TICKER (which was never
really used on its own and can be safely subsumed by
DISABLE_NOTIFICATION_ICONS) is now DISABLE_PRIVATE_NOTIFICATIONS;
when this SystemUI bit is set by the keyguard, SystemUI knows to switch
its presentation into "public" mode, in which
VISIBILITY_PRIVATE notifications are replaced with their
publicVersion's contentView (or a placeholder view,
synthesized by SystemUI, that leaks no additional
information about the notification). VISIBILITY_SECRET
notifications are suppressed altogether in this mode.

This behavior is enabled but not activated by default. To
turn it on, run:

  $ adb shell settings put secure lock_screen_allow_notifications 1

and restart SystemUI.

Change-Id: Id660bef7737580e16a83f60567c22b53ee81c602
parent 0bf2ed8a
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -38,8 +38,11 @@ public class StatusBarManager {
    public static final int DISABLE_NOTIFICATION_ICONS = View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS;
    public static final int DISABLE_NOTIFICATION_ALERTS
            = View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS;
    @Deprecated
    public static final int DISABLE_NOTIFICATION_TICKER
            = View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER;
    public static final int DISABLE_PRIVATE_NOTIFICATIONS
            = View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER;
    public static final int DISABLE_SYSTEM_INFO = View.STATUS_BAR_DISABLE_SYSTEM_INFO;
    public static final int DISABLE_HOME = View.STATUS_BAR_DISABLE_HOME;
    public static final int DISABLE_RECENT = View.STATUS_BAR_DISABLE_RECENT;
+8 −0
Original line number Diff line number Diff line
@@ -3467,6 +3467,14 @@ public final class Settings {
        public static final String LOCK_SCREEN_OWNER_INFO_ENABLED =
            "lock_screen_owner_info_enabled";

        /**
         * This preference enables expanding the notification panel even over a securely
         * locked screen, showing only "public" notifications in this case.
         * @hide
         */
        public static final String LOCK_SCREEN_ALLOW_NOTIFICATIONS =
                "lock_screen_allow_notifications";

        /**
         * The Logging ID (a unique 64-bit value) as a hex string.
         * Used as a pseudonymous identifier for logging.
+29 −3
Original line number Diff line number Diff line
@@ -149,6 +149,18 @@ public class KeyguardViewMediator {
     */
    private static final boolean ENABLE_INSECURE_STATUS_BAR_EXPAND = true;

    /**
     * Allow the user to expand the status bar when a SECURE keyguard is engaged
     * and {@link Settings.Secure#LOCK_SCREEN_ALLOW_NOTIFICATIONS} is set
     * (private notifications will be masked).
     */
    private static final boolean ENABLE_SECURE_STATUS_BAR_EXPAND = true;

    /**
     * Default value of {@link Settings.Secure#LOCK_SCREEN_ALLOW_NOTIFICATIONS}.
     */
    private static final boolean ALLOW_NOTIFICATIONS_DEFAULT = false;

    /** The stream type that the lock sounds are tied to. */
    private int mMasterStreamType;

@@ -245,6 +257,11 @@ public class KeyguardViewMediator {
    private int mUnlockSoundId;
    private int mLockSoundStreamId;

    /**
     * Tracks value of {@link Settings.Secure#LOCK_SCREEN_ALLOW_NOTIFICATIONS}.
     */
    private boolean mAllowNotificationsWhenSecure;

    /**
     * The volume applied to the lock/unlock sounds.
     */
@@ -894,6 +911,13 @@ public class KeyguardViewMediator {
            return;
        }

        // note whether notification access should be allowed
        mAllowNotificationsWhenSecure = ENABLE_SECURE_STATUS_BAR_EXPAND
                && 0 != Settings.Secure.getInt(
                        mContext.getContentResolver(),
                        Settings.Secure.LOCK_SCREEN_ALLOW_NOTIFICATIONS,
                        ALLOW_NOTIFICATIONS_DEFAULT ? 1 : 0);

        // if the keyguard is already showing, don't bother
        if (mKeyguardViewManager.isShowing()) {
            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
@@ -1278,13 +1302,15 @@ public class KeyguardViewMediator {
                // (like recents). Temporary enable/disable (e.g. the "back" button) are
                // done in KeyguardHostView.
                flags |= StatusBarManager.DISABLE_RECENT;
                if (isSecure() || !ENABLE_INSECURE_STATUS_BAR_EXPAND) {
                if ((isSecure() && !mAllowNotificationsWhenSecure)
                        || !ENABLE_INSECURE_STATUS_BAR_EXPAND) {
                    // showing secure lockscreen; disable expanding.
                    flags |= StatusBarManager.DISABLE_EXPAND;
                }
                if (isSecure()) {
                    // showing secure lockscreen; disable ticker.
                    flags |= StatusBarManager.DISABLE_NOTIFICATION_TICKER;
                    // showing secure lockscreen; disable ticker and switch private notifications
                    // to show their public versions, if available.
                    flags |= StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS;
                }
                if (!isAssistantAvailable()) {
                    flags |= StatusBarManager.DISABLE_SEARCH;
+5 −2
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@
        android:paddingStart="8dp"
        />

    <com.android.systemui.statusbar.LatestItemView android:id="@+id/content"
    <com.android.systemui.statusbar.LatestItemView android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="@dimen/notification_divider_height"
@@ -34,10 +34,13 @@
        android:clickable="true"
        >

        <com.android.internal.widget.SizeAdaptiveLayout android:id="@+id/adaptive"
        <com.android.internal.widget.SizeAdaptiveLayout android:id="@+id/expanded"
           android:layout_width="match_parent"
           android:layout_height="wrap_content" />

        <com.android.internal.widget.SizeAdaptiveLayout android:id="@+id/expandedPublic"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </com.android.systemui.statusbar.LatestItemView>

    <View
+107 −9
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.database.ContentObserver;
@@ -130,6 +131,7 @@ public abstract class BaseStatusBar extends SystemUI implements
    protected IDreamManager mDreamManager;
    PowerManager mPowerManager;
    protected int mRowHeight;
    private boolean mPublicMode = false;

    // UI-specific methods

@@ -548,6 +550,14 @@ public abstract class BaseStatusBar extends SystemUI implements

    public abstract void resetHeadsUpDecayTimer();

    public void setPublicMode(boolean publicMode) {
        mPublicMode = publicMode;
    }

    public boolean isPublicMode() {
        return mPublicMode;
    }

    protected class H extends Handler {
        public void handleMessage(Message m) {
            Intent intent;
@@ -625,6 +635,11 @@ public abstract class BaseStatusBar extends SystemUI implements
            return false;
        }

        Log.v(TAG, "publicNotification: "
                + sbn.getNotification().publicVersion);

        Notification publicNotification = sbn.getNotification().publicVersion;

        // create the row view
        LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
                Context.LAYOUT_INFLATER_SERVICE);
@@ -642,8 +657,10 @@ public abstract class BaseStatusBar extends SystemUI implements
        // NB: the large icon is now handled entirely by the template

        // bind the click event to the content area
        ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
        ViewGroup adaptive = (ViewGroup)row.findViewById(R.id.adaptive);
        ViewGroup content = (ViewGroup)row.findViewById(R.id.container);
        SizeAdaptiveLayout expanded = (SizeAdaptiveLayout)row.findViewById(R.id.expanded);
        SizeAdaptiveLayout expandedPublic
                = (SizeAdaptiveLayout)row.findViewById(R.id.expandedPublic);

        content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);

@@ -656,12 +673,13 @@ public abstract class BaseStatusBar extends SystemUI implements
            content.setOnClickListener(null);
        }

        // set up the adaptive layout
        View contentViewLocal = null;
        View bigContentViewLocal = null;
        try {
            contentViewLocal = contentView.apply(mContext, adaptive, mOnClickHandler);
            contentViewLocal = contentView.apply(mContext, expanded, mOnClickHandler);
            if (bigContentView != null) {
                bigContentViewLocal = bigContentView.apply(mContext, adaptive, mOnClickHandler);
                bigContentViewLocal = bigContentView.apply(mContext, expanded, mOnClickHandler);
            }
        }
        catch (RuntimeException e) {
@@ -675,15 +693,70 @@ public abstract class BaseStatusBar extends SystemUI implements
                    new SizeAdaptiveLayout.LayoutParams(contentViewLocal.getLayoutParams());
            params.minHeight = minHeight;
            params.maxHeight = minHeight;
            adaptive.addView(contentViewLocal, params);
            expanded.addView(contentViewLocal, params);
        }
        if (bigContentViewLocal != null) {
            SizeAdaptiveLayout.LayoutParams params =
                    new SizeAdaptiveLayout.LayoutParams(bigContentViewLocal.getLayoutParams());
            params.minHeight = minHeight+1;
            params.maxHeight = maxHeight;
            adaptive.addView(bigContentViewLocal, params);
            expanded.addView(bigContentViewLocal, params);
        }

        PackageManager pm = mContext.getPackageManager();

        // now the public version
        View publicViewLocal = null;
        if (publicNotification != null) {
            try {
                publicViewLocal = publicNotification.contentView.apply(mContext,
                        expandedPublic, mOnClickHandler);

                if (publicViewLocal != null) {
                    SizeAdaptiveLayout.LayoutParams params =
                            new SizeAdaptiveLayout.LayoutParams(publicViewLocal.getLayoutParams());
                    params.minHeight = minHeight;
                    params.maxHeight = minHeight;
                    expandedPublic.addView(publicViewLocal, params);
                }
            }
            catch (RuntimeException e) {
                final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
                Log.e(TAG, "couldn't inflate public view for notification " + ident, e);
                publicViewLocal = null;
            }
        }

        if (publicViewLocal == null) {
            // Add a basic notification template
            publicViewLocal = LayoutInflater.from(mContext).inflate(
                    com.android.internal.R.layout.notification_template_base, expandedPublic, true);

            final TextView title = (TextView) publicViewLocal.findViewById(com.android.internal.R.id.title);
            try {
                title.setText(pm.getApplicationLabel(
                        pm.getApplicationInfo(entry.notification.getPackageName(), 0)));
            } catch (NameNotFoundException e) {
                title.setText(entry.notification.getPackageName());
            }

            final ImageView icon = (ImageView) publicViewLocal.findViewById(com.android.internal.R.id.icon);

            final StatusBarIcon ic = new StatusBarIcon(entry.notification.getPackageName(),
                    entry.notification.getUser(),
                    entry.notification.getNotification().icon,
                    entry.notification.getNotification().iconLevel,
                    entry.notification.getNotification().number,
                    entry.notification.getNotification().tickerText);

            icon.setImageDrawable(StatusBarIconView.getIcon(mContext, ic));

            final TextView text = (TextView) publicViewLocal.findViewById(com.android.internal.R.id.text);
            text.setText("Unlock your device to see this notification.");

            // TODO: fill out "time" as well
        }

        row.setDrawingCacheEnabled(true);

        applyLegacyRowBackground(sbn, content);
@@ -699,6 +772,7 @@ public abstract class BaseStatusBar extends SystemUI implements
        entry.row.setRowHeight(mRowHeight);
        entry.content = content;
        entry.expanded = contentViewLocal;
        entry.expandedPublic = publicViewLocal;
        entry.setBigContentView(bigContentViewLocal);

        return true;
@@ -904,6 +978,12 @@ public abstract class BaseStatusBar extends SystemUI implements
        final RemoteViews contentView = notification.getNotification().contentView;
        final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
        final RemoteViews bigContentView = notification.getNotification().bigContentView;
        final Notification oldPublicNotification = oldNotification.getNotification().publicVersion;
        final RemoteViews oldPublicContentView = oldPublicNotification != null
                ? oldPublicNotification.contentView : null;
        final Notification publicNotification = notification.getNotification().publicVersion;
        final RemoteViews publicContentView = publicNotification != null
                ? publicNotification.contentView : null;

        if (DEBUG) {
            Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
@@ -911,11 +991,13 @@ public abstract class BaseStatusBar extends SystemUI implements
                    + " expanded=" + oldEntry.expanded
                    + " contentView=" + oldContentView
                    + " bigContentView=" + oldBigContentView
                    + " publicView=" + oldPublicContentView
                    + " rowParent=" + oldEntry.row.getParent());
            Log.d(TAG, "new notification: when=" + notification.getNotification().when
                    + " ongoing=" + oldNotification.isOngoing()
                    + " contentView=" + contentView
                    + " bigContentView=" + bigContentView);
                    + " bigContentView=" + bigContentView
                    + " publicView=" + publicContentView);
        }

        // Can we just reapply the RemoteViews in place?  If when didn't change, the order
@@ -935,8 +1017,17 @@ public abstract class BaseStatusBar extends SystemUI implements
                    && oldBigContentView.getPackage() != null
                    && oldBigContentView.getPackage().equals(bigContentView.getPackage())
                    && oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
        boolean publicUnchanged  =
                (oldPublicContentView == null && publicContentView == null)
                || ((oldPublicContentView != null && publicContentView != null)
                        && publicContentView.getPackage() != null
                        && oldPublicContentView.getPackage() != null
                        && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
                        && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());

        ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
        boolean orderUnchanged = notification.getNotification().when== oldNotification.getNotification().when
        boolean orderUnchanged =
                   notification.getNotification().when == oldNotification.getNotification().when
                && notification.getScore() == oldNotification.getScore();
                // score now encompasses/supersedes isOngoing()

@@ -944,7 +1035,8 @@ public abstract class BaseStatusBar extends SystemUI implements
                && !TextUtils.equals(notification.getNotification().tickerText,
                        oldEntry.notification.getNotification().tickerText);
        boolean isTopAnyway = isTopNotification(rowParent, oldEntry);
        if (contentsUnchanged && bigContentsUnchanged && (orderUnchanged || isTopAnyway)) {
        if (contentsUnchanged && bigContentsUnchanged && publicUnchanged
                && (orderUnchanged || isTopAnyway)) {
            if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
            oldEntry.notification = notification;
            try {
@@ -1018,11 +1110,17 @@ public abstract class BaseStatusBar extends SystemUI implements
            StatusBarNotification notification) {
        final RemoteViews contentView = notification.getNotification().contentView;
        final RemoteViews bigContentView = notification.getNotification().bigContentView;
        final RemoteViews publicContentView
                = notification.getNotification().publicVersion.contentView;

        // Reapply the RemoteViews
        contentView.reapply(mContext, entry.expanded, mOnClickHandler);
        if (bigContentView != null && entry.getBigContentView() != null) {
            bigContentView.reapply(mContext, entry.getBigContentView(), mOnClickHandler);
        }
        if (publicContentView != null && entry.getPublicContentView() != null) {
            publicContentView.reapply(mContext, entry.getPublicContentView(), mOnClickHandler);
        }
        // update the contentIntent
        final PendingIntent contentIntent = notification.getNotification().contentIntent;
        if (contentIntent != null) {
Loading