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

Commit b6d3dc68 authored by Daniel Sandler's avatar Daniel Sandler
Browse files

Implement new lights-out mode in system bar.

When an app requests fullscreen display, "shadows" of the
visible system bar UI elements are drawn in their place. The
user can still interact with these elements by poking the
shadows; the widgets will temporarily shine through, long
enough for the user interaction.

Known issues:
 - if the notification panel is up for too long, the shadow
   will not be re-enabled on the notification area (need to
   route all hide/show requests through the
   shadowcontroller)
 - status bar hide/show animations have been temporarily
   turned off to make this work correctly; they'll be put
   back later

Bug: 3203171
parent 1bf397ff
Loading
Loading
Loading
Loading
+247 B
Loading image diff...
+100 −44
Original line number Diff line number Diff line
@@ -25,9 +25,10 @@
        android:id="@+id/bar_contents"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:animateLayoutChanges="true"
        android:animateLayoutChanges="false"
        >

        <!-- ticker: transient incoming notification information -->
        <FrameLayout
            android:id="@+id/ticker"
            android:layout_width="wrap_content"
@@ -39,6 +40,7 @@
            android:animateLayoutChanges="true"
            />

        <!-- notification icons & panel access -->
        <LinearLayout
            android:id="@+id/notificationArea"
            android:layout_width="wrap_content"
@@ -65,7 +67,7 @@
            <LinearLayout
                android:id="@+id/notificationTrigger"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_height="match_parent"
                >
                <!-- paddingLeft: 24 dips = 32dp (total space to icon) - 8dp in the icon.
                TODO: Make sure the font has a small enough leading that we don't need this
@@ -81,6 +83,11 @@
                    android:paddingLeft="24dip"
                    android:textColor="#2e2e2e"
                    />
                <LinearLayout
                    android:layout_width="48dip"
                    android:layout_height="match_parent"
                    android:orientation="horizontal"
                    >
                    <ImageView
                        android:id="@+id/battery"
                        android:layout_height="wrap_content"
@@ -96,47 +103,50 @@
                        />
                </LinearLayout>
            </LinearLayout>
        </LinearLayout>

        <!-- navigation controls -->
        <LinearLayout
            android:id="@+id/navigationArea"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_alignParentLeft="true"
            android:orientation="horizontal"
            android:animateLayoutChanges="true"
            android:animateLayoutChanges="false"
            >

            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
                android:layout_width="wrap_content"
                android:layout_width="96dip"
                android:layout_height="match_parent"
                android:paddingLeft="15dip"
                android:paddingRight="15dip"
                android:paddingLeft="18dip"
                android:paddingRight="18dip"
                android:src="@drawable/ic_sysbar_back"
                android:background="@drawable/ic_sysbar_icon_bg"
                systemui:keyCode="4"
                />
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
                android:layout_width="wrap_content"
                android:layout_width="96dip"
                android:layout_height="match_parent"
                android:paddingLeft="15dip"
                android:paddingRight="15dip"
                android:paddingLeft="18dip"
                android:paddingRight="18dip"
                android:src="@drawable/ic_sysbar_home"
                android:background="@drawable/ic_sysbar_icon_bg"
                systemui:keyCode="3"
                />
            <ImageButton android:id="@+id/recent_apps"
                android:layout_width="wrap_content"
                android:layout_width="96dip"
                android:layout_height="match_parent"
                android:src="@drawable/ic_sysbar_recent"
                android:background="@drawable/ic_sysbar_icon_bg"
                android:paddingLeft="15dip"
                android:paddingRight="15dip"
                android:paddingLeft="18dip"
                android:clickable="true"
                android:paddingRight="18dip"
                />
            <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
                android:layout_width="wrap_content"
                android:layout_width="96dip"
                android:layout_height="match_parent"
                android:paddingLeft="15dip"
                android:paddingRight="15dip"
                android:paddingLeft="18dip"
                android:paddingRight="18dip"
                android:src="@drawable/ic_sysbar_menu"
                android:background="@drawable/ic_sysbar_icon_bg"
                systemui:keyCode="82"
@@ -144,11 +154,11 @@
                />
            <com.android.systemui.statusbar.tablet.ShirtPocket
                android:id="@+id/pocket"
                android:layout_width="71dip"
                android:layout_width="96dip"
                android:layout_height="match_parent"
                android:background="@drawable/ic_sysbar_icon_bg"
                android:paddingLeft="15dip"
                android:paddingRight="15dip"
                android:paddingLeft="18dip"
                android:paddingRight="18dip"
                android:animateLayoutChanges="true"
                android:clickable="true"
                android:descendantFocusability="blocksDescendants"
@@ -173,18 +183,64 @@
                android:visibility="invisible"
                />
        </LinearLayout>
    </RelativeLayout>

    <!-- It's curtains for you. -->
        <!-- lights out mode: "shadow" views -->
        <ImageView
        android:id="@+id/lights_out"
        android:src="@drawable/ic_sysbar_lightsout"
        android:gravity="center"
        android:background="#FF000000"
        android:layout_width="match_parent"
            android:id="@+id/notification_shadow"
            android:layout_width="176dip"
            android:layout_height="match_parent"
        android:visibility="invisible"
        android:clickable="true"
            android:paddingRight="48dip"
            android:layout_alignParentRight="true"
            android:layout_alignParentBottom="true"
            android:src="@drawable/ic_sysbar_shadow"
            android:visibility="gone"
            android:scaleType="fitXY"
            />

        <ImageView
            android:id="@+id/back_shadow"
            android:layout_width="96dip"
            android:layout_height="match_parent"
            android:paddingLeft="18dip"
            android:paddingRight="18dip"
            android:layout_alignParentLeft="true"
            android:layout_alignParentBottom="true"
            android:src="@drawable/ic_sysbar_shadow"
            android:visibility="gone"
            />
        <ImageView
            android:id="@+id/home_shadow"
            android:layout_width="96dip"
            android:layout_height="match_parent"
            android:paddingLeft="18dip"
            android:paddingRight="18dip"
            android:layout_toRightOf="@id/back_shadow"
            android:layout_alignParentBottom="true"
            android:src="@drawable/ic_sysbar_shadow"
            android:visibility="gone"
            />
        <ImageView
            android:id="@+id/recent_shadow"
            android:layout_width="96dip"
            android:layout_height="match_parent"
            android:paddingLeft="18dip"
            android:paddingRight="18dip"
            android:layout_toRightOf="@id/home_shadow"
            android:layout_alignParentBottom="true"
            android:src="@drawable/ic_sysbar_shadow"
            android:visibility="gone"
            />
        <ImageView
            android:id="@+id/menu_shadow"
            android:layout_width="96dip"
            android:layout_height="match_parent"
            android:paddingLeft="18dip"
            android:paddingRight="18dip"
            android:layout_toRightOf="@id/recent_shadow"
            android:layout_alignParentBottom="true"
            android:src="@drawable/ic_sysbar_shadow"
            android:visibility="gone"
            />
</com.android.systemui.statusbar.tablet.TabletStatusBarView>

    </RelativeLayout>
</com.android.systemui.statusbar.tablet.TabletStatusBarView>
+142 −32
Original line number Diff line number Diff line
@@ -75,12 +75,15 @@ public class TabletStatusBar extends StatusBar {
    public static final int MSG_CLOSE_NOTIFICATION_PEEK = 1003;
    public static final int MSG_OPEN_RECENTS_PANEL = 1020;
    public static final int MSG_CLOSE_RECENTS_PANEL = 1021;
    public static final int MSG_LIGHTS_ON = 1030;
    public static final int MSG_LIGHTS_OUT = 1031;
    public static final int MSG_HIDE_SHADOWS = 1030;
    public static final int MSG_SHOW_SHADOWS = 1031;
    public static final int MSG_SHOW_SHADOWS_NO_COLLAPSE = 1032;

    private static final int MAX_IMAGE_LEVEL = 10000;
    private static final boolean USE_2D_RECENTS = true;

    public static final int LIGHTS_ON_DELAY = 5000;

    int mIconSize;

    H mHandler = new H();
@@ -93,6 +96,9 @@ public class TabletStatusBar extends StatusBar {
    View mNotificationTrigger;
    NotificationIconArea mNotificationIconArea;
    View mNavigationArea;

    View mBackButton;
    View mHomeButton;
    View mMenuButton;
    View mRecentButton;

@@ -113,7 +119,10 @@ public class TabletStatusBar extends StatusBar {
    NetworkController mNetworkController;

    View mBarContents;
    View mCurtains;

    // lights out support
    View mBackShadow, mHomeShadow, mRecentShadow, mMenuShadow, mNotificationShadow;
    ShadowController mShadowController;

    NotificationIconArea.IconLayout mIconLayout;

@@ -245,14 +254,21 @@ public class TabletStatusBar extends StatusBar {
        sb.setHandler(mHandler);

        mBarContents = sb.findViewById(R.id.bar_contents);
        mCurtains = sb.findViewById(R.id.lights_out);

        mRecentButton = sb.findViewById(R.id.recent_apps);
        mRecentButton.setOnClickListener(mOnClickListener);
        // "shadows" of the status bar features, for lights-out mode
        mBackShadow = sb.findViewById(R.id.back_shadow);
        mHomeShadow = sb.findViewById(R.id.home_shadow);
        mRecentShadow = sb.findViewById(R.id.recent_shadow);
        mMenuShadow = sb.findViewById(R.id.menu_shadow);
        mNotificationShadow = sb.findViewById(R.id.notification_shadow);

        mShadowController = new ShadowController(false);

        SetLightsOnListener on = new SetLightsOnListener(true);
        mCurtains.setOnClickListener(on);
        mCurtains.setOnLongClickListener(on);
        mBackShadow.setOnTouchListener(mShadowController.makeTouchListener());
        mHomeShadow.setOnTouchListener(mShadowController.makeTouchListener());
        mRecentShadow.setOnTouchListener(mShadowController.makeTouchListener());
        mMenuShadow.setOnTouchListener(mShadowController.makeTouchListener());
        mNotificationShadow.setOnTouchListener(mShadowController.makeTouchListener());

        // the whole right-hand side of the bar
        mNotificationArea = sb.findViewById(R.id.notificationArea);
@@ -282,10 +298,15 @@ public class TabletStatusBar extends StatusBar {

        // The navigation buttons
        mNavigationArea = sb.findViewById(R.id.navigationArea);
        mBackButton = mNavigationArea.findViewById(R.id.back);
        mHomeButton = mNavigationArea.findViewById(R.id.home);
        mMenuButton = mNavigationArea.findViewById(R.id.menu);
        mRecentButton = mNavigationArea.findViewById(R.id.recent_apps);
        Slog.d(TAG, "rec=" + mRecentButton + ", listener=" + mOnClickListener);
        mRecentButton.setOnClickListener(mOnClickListener);

        // The bar contents buttons
        mInputMethodButton = (InputMethodButton) mBarContents.findViewById(R.id.imeButton);
        mInputMethodButton = (InputMethodButton) sb.findViewById(R.id.imeButton);

        // set the initial view visibility
        setAreThereNotifications();
@@ -362,6 +383,8 @@ public class TabletStatusBar extends StatusBar {
                        mNotificationPeekWindow.setVisibility(View.GONE);

                        mNotificationPanel.setVisibility(View.VISIBLE);

                        // XXX: need to synchronize with shadows here
                        mNotificationArea.setVisibility(View.GONE);
                    }
                    break;
@@ -369,6 +392,8 @@ public class TabletStatusBar extends StatusBar {
                    if (DEBUG) Slog.d(TAG, "closing notifications panel");
                    if (mNotificationPanel.getVisibility() == View.VISIBLE) {
                        mNotificationPanel.setVisibility(View.GONE);

                        // XXX: need to synchronize with shadows here
                        mNotificationArea.setVisibility(View.VISIBLE);
                    }
                    break;
@@ -380,14 +405,14 @@ public class TabletStatusBar extends StatusBar {
                    if (DEBUG) Slog.d(TAG, "closing recents panel");
                    if (mRecentsPanel != null) mRecentsPanel.setVisibility(View.GONE);
                    break;
                case MSG_LIGHTS_ON:
                    setViewVisibility(mCurtains, View.GONE, R.anim.lights_out_out);
                    setViewVisibility(mBarContents, View.VISIBLE, R.anim.status_bar_in);
                case MSG_HIDE_SHADOWS:
                    mShadowController.hideAllShadows();
                    break;
                case MSG_LIGHTS_OUT:
                case MSG_SHOW_SHADOWS:
                    animateCollapse();
                    setViewVisibility(mCurtains, View.VISIBLE, R.anim.lights_out_in);
                    setViewVisibility(mBarContents, View.GONE, R.anim.status_bar_out);
                    // fall through
                case MSG_SHOW_SHADOWS_NO_COLLAPSE:
                    mShadowController.showAllShadows();
                    break;
            }
        }
@@ -605,16 +630,16 @@ public class TabletStatusBar extends StatusBar {
    // called by StatusBar
    @Override
    public void setLightsOn(boolean on) {
        mHandler.removeMessages(MSG_LIGHTS_OUT);
        mHandler.removeMessages(MSG_LIGHTS_ON);
        mHandler.sendEmptyMessage(on ? MSG_LIGHTS_ON : MSG_LIGHTS_OUT);
        mHandler.removeMessages(MSG_SHOW_SHADOWS);
        mHandler.removeMessages(MSG_HIDE_SHADOWS);
        mHandler.sendEmptyMessage(on ? MSG_HIDE_SHADOWS : MSG_SHOW_SHADOWS);
    }

    public void setMenuKeyVisible(boolean visible) {
        if (DEBUG) {
            Slog.d(TAG, (visible?"showing":"hiding") + " the MENU button");
        }
        mMenuButton.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
        mMenuButton.setVisibility(visible ? View.VISIBLE : View.GONE);
    }

    public void setIMEButtonVisible(boolean visible) {
@@ -664,7 +689,7 @@ public class TabletStatusBar extends StatusBar {
    };

    public void onClickNotificationTrigger() {
        if (DEBUG) Slog.d(TAG, "clicked notification icons");
        if (DEBUG) Slog.d(TAG, "clicked notification icons; disabled=" + mDisabled);
        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) {
            if (!mNotificationsOn) {
                mNotificationsOn = true;
@@ -681,7 +706,7 @@ public class TabletStatusBar extends StatusBar {
    }

    public void onClickRecentButton() {
        if (DEBUG) Slog.d(TAG, "clicked recent apps");
        if (DEBUG) Slog.d(TAG, "clicked recent apps; disabled=" + mDisabled);
        if (mRecentsPanel == null) {
            Intent intent = new Intent();
            intent.setClass(mContext, RecentApplicationsActivity.class);
@@ -1006,23 +1031,108 @@ public class TabletStatusBar extends StatusBar {
        return true;
    }

    public class SetLightsOnListener implements View.OnLongClickListener,
           View.OnClickListener {
        private boolean mOn;
    public class ShadowController {
        boolean mShowShadows;
        View mTouchTarget;

        SetLightsOnListener(boolean on) {
            mOn = on;
        ShadowController(boolean showShadows) {
            mShowShadows = showShadows;
            mTouchTarget = null;
        }

        public void onClick(View v) {
            setLightsOn(mOn);
        public boolean getShadowState() {
            return mShowShadows;
        }

        public boolean onLongClick(View v) {
            setLightsOn(mOn);
        public View.OnTouchListener makeTouchListener() {
            return new View.OnTouchListener() {
                public boolean onTouch(View v, MotionEvent ev) {
                    final int action = ev.getAction();

                    if (DEBUG) Slog.d(TAG, "ShadowController: v=" + v + ", ev=" + ev);

                    // currently redirecting events?
                    if (mTouchTarget == null) {
                        if (v == mBackShadow) {
                            mTouchTarget = mBackButton;
                        } else if (v == mHomeShadow) {
                            mTouchTarget = mHomeButton;
                        } else if (v == mMenuShadow) {
                            mTouchTarget = mMenuButton;
                        } else if (v == mRecentShadow) {
                            mTouchTarget = mRecentButton;
                        } else if (v == mNotificationShadow) {
                            mTouchTarget = mNotificationArea;
                        }
                    }

                    if (mTouchTarget != null && mTouchTarget.getVisibility() != View.GONE) {
                        boolean last = false;
                        switch (action) {
                            case MotionEvent.ACTION_CANCEL:
                            case MotionEvent.ACTION_UP:
                                mHandler.removeMessages(MSG_SHOW_SHADOWS_NO_COLLAPSE);
                                if (mShowShadows) {
                                    mHandler.sendEmptyMessageDelayed(MSG_SHOW_SHADOWS_NO_COLLAPSE, 
                                            v == mNotificationShadow ? 5000 : 500);
                                }
                                last = true;
                                break;
                            case MotionEvent.ACTION_DOWN:
                                mHandler.removeMessages(MSG_SHOW_SHADOWS_NO_COLLAPSE);
                                setShadowForButton(mTouchTarget, false);
                                break;
                        }
                        mTouchTarget.dispatchTouchEvent(ev);
                        if (last) mTouchTarget = null;
                        return true;
                    }

                    return false;
                }
            };
        }

        public void showAllShadows() {
            mShowShadows = true;
            setShadowForButton(mBackButton, true);
            setShadowForButton(mHomeButton, true);
            setShadowForButton(mRecentButton, true);
            setShadowForButton(mMenuButton, true);
            setShadowForButton(mNotificationArea, true);
        }

        public void hideAllShadows() {
            mShowShadows = false;
            setShadowForButton(mBackButton, false);
            setShadowForButton(mHomeButton, false);
            setShadowForButton(mRecentButton, false);
            setShadowForButton(mMenuButton, false);
            setShadowForButton(mNotificationArea, false);
        }

        // Use View.INVISIBLE for things hidden due to shadowing, and View.GONE for things that are
        // disabled (and should not be shadowed or re-shown)
        public void setShadowForButton(View button, boolean shade) {
            View shadow = null;
            if (button == mBackButton) {
                shadow = mBackShadow;
            } else if (button == mHomeButton) {
                shadow = mHomeShadow;
            } else if (button == mMenuButton) {
                shadow = mMenuShadow;
            } else if (button == mRecentButton) {
                shadow = mRecentShadow;
            } else if (button == mNotificationArea) {
                shadow = mNotificationShadow;
            }
            if (shadow != null) {
                if (button.getVisibility() != View.GONE) {
                    shadow.setVisibility(shade ? View.VISIBLE : View.INVISIBLE);
                    button.setVisibility(shade ? View.INVISIBLE : View.VISIBLE);
                }
            }
        }
    }

    public class TouchOutsideListener implements View.OnTouchListener {
+7 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.tablet;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Slog;
import android.view.View;
import android.view.MotionEvent;
import android.widget.FrameLayout;
@@ -40,6 +41,9 @@ public class TabletStatusBarView extends FrameLayout {

    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            if (TabletStatusBar.DEBUG) {
                Slog.d(TabletStatusBar.TAG, "TabletStatusBarView intercepting touch event: " + ev);
            }
            mHandler.removeMessages(TabletStatusBar.MSG_CLOSE_NOTIFICATION_PANEL);
            mHandler.sendEmptyMessage(TabletStatusBar.MSG_CLOSE_NOTIFICATION_PANEL);
            mHandler.removeMessages(TabletStatusBar.MSG_CLOSE_RECENTS_PANEL);
@@ -48,6 +52,9 @@ public class TabletStatusBarView extends FrameLayout {
            for (int i=0; i < mPanels.length; i++) {
                if (mPanels[i] != null && mPanels[i].getVisibility() == View.VISIBLE) {
                    if (eventInside(mIgnoreChildren[i], ev)) {
                        if (TabletStatusBar.DEBUG) {
                            Slog.d(TabletStatusBar.TAG, "TabletStatusBarView eating event for view: " + mIgnoreChildren[i]);
                        }
                        return true;
                    }
                }