OSDN Git Service

Empty notification shade state.
authorJorim Jaggi <jjaggi@google.com>
Tue, 5 Aug 2014 14:22:30 +0000 (16:22 +0200)
committerJorim Jaggi <jjaggi@google.com>
Tue, 5 Aug 2014 14:38:02 +0000 (16:38 +0200)
Bug: 16483230
Change-Id: I7953a7954cae12124146f462ed8c0dc44769a38f

packages/SystemUI/res/layout/status_bar_no_notifications.xml [new file with mode: 0644]
packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java
packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java [new file with mode: 0644]
packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java [new file with mode: 0644]
packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java

diff --git a/packages/SystemUI/res/layout/status_bar_no_notifications.xml b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
new file mode 100644 (file)
index 0000000..dd501d4
--- /dev/null
@@ -0,0 +1,33 @@
+<!--
+  ~ Copyright (C) 2014 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
+  -->
+
+<!-- Extends Framelayout -->
+<com.android.systemui.statusbar.EmptyShadeView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        >
+    <TextView
+            android:id="@+id/no_notifications"
+            android:layout_width="match_parent"
+            android:layout_height="64dp"
+            android:paddingTop="12dp"
+            android:gravity="top|center_horizontal"
+            android:textColor="#ffffff"
+            android:textSize="20sp"
+            android:text="@string/empty_shade_text"/>
+</com.android.systemui.statusbar.EmptyShadeView>
index c2f6aa4..af92e49 100644 (file)
@@ -200,6 +200,7 @@ public abstract class BaseStatusBar extends SystemUI implements
 
     protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
     protected DismissView mDismissView;
+    protected EmptyShadeView mEmptyShadeView;
 
     @Override  // NotificationData.Environment
     public boolean isDeviceProvisioned() {
@@ -1405,12 +1406,11 @@ public abstract class BaseStatusBar extends SystemUI implements
         } else {
             mKeyguardIconOverflowContainer.setVisibility(View.GONE);
         }
-        // Move overflow container to second last position.
-        mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
-                mStackScroller.getChildCount() - 2);
 
-        // Now move dismissView to the last position.
-        mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1);
+        mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
+                mStackScroller.getChildCount() - 3);
+        mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 2);
+        mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 1);
     }
 
     private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
index f674b06..897dbf2 100644 (file)
@@ -19,133 +19,21 @@ package com.android.systemui.statusbar;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.animation.Interpolator;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
-public class DismissView extends ExpandableView {
-
-    private View mClearAllIcon;
-    private boolean mIsVisible;
-    private boolean mAnimating;
-    private boolean mWillBeGone;
+public class DismissView extends StackScrollerDecorView {
 
     public DismissView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
     @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mClearAllIcon = findViewById(R.id.dismiss_text);
-        setInvisible();
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        setOutlineProvider(null);
-    }
-
-    @Override
-    public boolean isTransparent() {
-        return true;
-    }
-
-    public void performVisibilityAnimation(boolean nowVisible) {
-        animateText(nowVisible, null /* onFinishedRunnable */);
-    }
-
-    public void performVisibilityAnimation(boolean nowVisible, Runnable onFinishedRunnable) {
-        animateText(nowVisible, onFinishedRunnable);
-    }
-
-    public boolean isVisible() {
-        return mIsVisible || mAnimating;
-    }
-
-    /**
-     * Animate the text to a new visibility.
-     *
-     * @param nowVisible should it now be visible
-     * @param onFinishedRunnable A runnable which should be run when the animation is
-     *        finished.
-     */
-    private void animateText(boolean nowVisible, final Runnable onFinishedRunnable) {
-        if (nowVisible != mIsVisible) {
-            // Animate text
-            float endValue = nowVisible ? 1.0f : 0.0f;
-            Interpolator interpolator;
-            if (nowVisible) {
-                interpolator = PhoneStatusBar.ALPHA_IN;
-            } else {
-                interpolator = PhoneStatusBar.ALPHA_OUT;
-            }
-            mAnimating = true;
-            mClearAllIcon.animate()
-                    .alpha(endValue)
-                    .setInterpolator(interpolator)
-                    .setDuration(260)
-                    .withLayer()
-                    .withEndAction(new Runnable() {
-                        @Override
-                        public void run() {
-                            mAnimating = false;
-                            if (onFinishedRunnable != null) {
-                                onFinishedRunnable.run();
-                            }
-                        }
-                    });
-            mIsVisible = nowVisible;
-        } else {
-            if (onFinishedRunnable != null) {
-                onFinishedRunnable.run();
-            }
-        }
-    }
-
-    public void setInvisible() {
-        mClearAllIcon.setAlpha(0.0f);
-        mIsVisible = false;
-    }
-
-    @Override
-    public void performRemoveAnimation(long duration, float translationDirection,
-            Runnable onFinishedRunnable) {
-        // TODO: Use duration
-        performVisibilityAnimation(false);
-    }
-
-    @Override
-    public void performAddAnimation(long delay, long duration) {
-        // TODO: use delay and duration
-        performVisibilityAnimation(true);
-    }
-
-    @Override
-    public void setScrimAmount(float scrimAmount) {
-        // We don't need to scrim the dismissView
-    }
-
-    public void setOnButtonClickListener(OnClickListener onClickListener) {
-        mClearAllIcon.setOnClickListener(onClickListener);
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-
-    public void cancelAnimation() {
-        mClearAllIcon.animate().cancel();
-    }
-
-    public boolean willBeGone() {
-        return mWillBeGone;
+    protected View findContentView() {
+        return findViewById(R.id.dismiss_text);
     }
 
-    public void setWillBeGone(boolean willBeGone) {
-        mWillBeGone = willBeGone;
+    public void setOnButtonClickListener(OnClickListener listener) {
+        mContent.setOnClickListener(listener);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
new file mode 100644 (file)
index 0000000..582d165
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+
+public class EmptyShadeView extends StackScrollerDecorView {
+
+    public EmptyShadeView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected View findContentView() {
+        return findViewById(R.id.no_notifications);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java
new file mode 100644 (file)
index 0000000..62a492e
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+
+/**
+ * A common base class for all views in the notification stack scroller which don't have a
+ * background.
+ */
+public abstract class StackScrollerDecorView extends ExpandableView {
+
+    protected View mContent;
+    private boolean mIsVisible;
+    private boolean mAnimating;
+    private boolean mWillBeGone;
+
+    public StackScrollerDecorView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mContent = findContentView();
+        setInvisible();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        setOutlineProvider(null);
+    }
+
+    @Override
+    public boolean isTransparent() {
+        return true;
+    }
+
+    public void performVisibilityAnimation(boolean nowVisible) {
+        animateText(nowVisible, null /* onFinishedRunnable */);
+    }
+
+    public void performVisibilityAnimation(boolean nowVisible, Runnable onFinishedRunnable) {
+        animateText(nowVisible, onFinishedRunnable);
+    }
+
+    public boolean isVisible() {
+        return mIsVisible || mAnimating;
+    }
+
+    /**
+     * Animate the text to a new visibility.
+     *
+     * @param nowVisible should it now be visible
+     * @param onFinishedRunnable A runnable which should be run when the animation is
+     *        finished.
+     */
+    private void animateText(boolean nowVisible, final Runnable onFinishedRunnable) {
+        if (nowVisible != mIsVisible) {
+            // Animate text
+            float endValue = nowVisible ? 1.0f : 0.0f;
+            Interpolator interpolator;
+            if (nowVisible) {
+                interpolator = PhoneStatusBar.ALPHA_IN;
+            } else {
+                interpolator = PhoneStatusBar.ALPHA_OUT;
+            }
+            mAnimating = true;
+            mContent.animate()
+                    .alpha(endValue)
+                    .setInterpolator(interpolator)
+                    .setDuration(260)
+                    .withLayer()
+                    .withEndAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            mAnimating = false;
+                            if (onFinishedRunnable != null) {
+                                onFinishedRunnable.run();
+                            }
+                        }
+                    });
+            mIsVisible = nowVisible;
+        } else {
+            if (onFinishedRunnable != null) {
+                onFinishedRunnable.run();
+            }
+        }
+    }
+
+    public void setInvisible() {
+        mContent.setAlpha(0.0f);
+        mIsVisible = false;
+    }
+
+    @Override
+    public void performRemoveAnimation(long duration, float translationDirection,
+            Runnable onFinishedRunnable) {
+        // TODO: Use duration
+        performVisibilityAnimation(false);
+    }
+
+    @Override
+    public void performAddAnimation(long delay, long duration) {
+        // TODO: use delay and duration
+        performVisibilityAnimation(true);
+    }
+
+    @Override
+    public void setScrimAmount(float scrimAmount) {
+        // We don't need to scrim the dismissView
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+    public void cancelAnimation() {
+        mContent.animate().cancel();
+    }
+
+    public boolean willBeGone() {
+        return mWillBeGone;
+    }
+
+    public void setWillBeGone(boolean willBeGone) {
+        mWillBeGone = willBeGone;
+    }
+
+    protected abstract View findContentView();
+}
index 53c4740..98bb591 100644 (file)
@@ -147,6 +147,8 @@ public class NotificationPanelView extends PanelView implements
     private boolean mHeaderAnimatingIn;
     private ObjectAnimator mQsContainerAnimator;
 
+    private boolean mShadeEmpty;
+
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mSystemIconsCopy = new MirrorView(context);
@@ -879,6 +881,7 @@ public class NotificationPanelView extends PanelView implements
         mQsContainer.setVisibility(
                 mKeyguardShowing && !expandVisually ? View.INVISIBLE : View.VISIBLE);
         mScrollView.setTouchEnabled(mQsExpanded);
+        updateEmptyShadeView();
     }
 
     private void setQsExpansion(float height) {
@@ -1615,4 +1618,15 @@ public class NotificationPanelView extends PanelView implements
         }
         updateKeyguardStatusBarVisibility();
     }
+
+    public void setShadeEmpty(boolean shadeEmpty) {
+        mShadeEmpty = shadeEmpty;
+        updateEmptyShadeView();
+    }
+
+    private void updateEmptyShadeView() {
+
+        // Hide "No notifications" in QS.
+        mNotificationStackScroller.updateEmptyShadeView(mShadeEmpty && !mQsExpanded);
+    }
 }
index 8dfed92..ae7d500 100644 (file)
@@ -119,6 +119,7 @@ import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.DismissView;
 import com.android.systemui.statusbar.DragDownHelper;
+import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -696,6 +697,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
             }
         });
         mStackScroller.setDismissView(mDismissView);
+        mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
+                R.layout.status_bar_no_notifications, mStackScroller, false);
+        mStackScroller.setEmptyShadeView(mEmptyShadeView);
         mExpandedContents = mStackScroller;
 
         mScrimController = new ScrimController(mStatusBarWindow.findViewById(R.id.scrim_behind),
@@ -1420,6 +1424,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         updateRowStates();
         updateSpeedbump();
         updateClearAll();
+        updateEmptyShadeView();
 
         mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned() && mUserSetup);
         mShadeUpdates.check();
@@ -1432,6 +1437,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         mStackScroller.updateDismissView(showDismissView);
     }
 
+    private void updateEmptyShadeView() {
+        boolean showEmptyShade =
+                mState != StatusBarState.KEYGUARD &&
+                        mNotificationData.getActiveNotifications().size() == 0;
+        mNotificationPanel.setShadeEmpty(showEmptyShade);
+    }
+
     private void updateSpeedbump() {
         int speedbumpIndex = -1;
         int currentIndex = 0;
index 943ee21..fcca5fa 100644 (file)
@@ -36,6 +36,7 @@ import com.android.systemui.R;
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.statusbar.ActivatableNotificationView;
 import com.android.systemui.statusbar.DismissView;
+import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.SpeedBumpView;
@@ -145,6 +146,7 @@ public class NotificationStackScrollLayout extends ViewGroup
     private boolean mExpandedInThisMotion;
     private boolean mScrollingEnabled;
     private DismissView mDismissView;
+    private EmptyShadeView mEmptyShadeView;
     private boolean mDismissAllInProgress;
 
     /**
@@ -1222,6 +1224,9 @@ public class NotificationStackScrollLayout extends ViewGroup
         if (mDismissView.willBeGone()) {
             count--;
         }
+        if (mEmptyShadeView.willBeGone()) {
+            count--;
+        }
         return count;
     }
 
@@ -1985,6 +1990,7 @@ public class NotificationStackScrollLayout extends ViewGroup
     public void goToFullShade(long delay) {
         updateSpeedBump(true /* visibility */);
         mDismissView.setInvisible();
+        mEmptyShadeView.setInvisible();
         mGoToFullShadeNeedsAnimation = true;
         mGoToFullShadeDelay = delay;
         mNeedsAnimation =  true;
@@ -2039,6 +2045,38 @@ public class NotificationStackScrollLayout extends ViewGroup
         addView(mDismissView);
     }
 
+    public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
+        mEmptyShadeView = emptyShadeView;
+        addView(mEmptyShadeView);
+    }
+
+    public void updateEmptyShadeView(boolean visible) {
+        int oldVisibility = mEmptyShadeView.willBeGone() ? GONE : mEmptyShadeView.getVisibility();
+        int newVisibility = visible ? VISIBLE : GONE;
+        if (oldVisibility != newVisibility) {
+            if (oldVisibility == GONE) {
+                if (mEmptyShadeView.willBeGone()) {
+                    mEmptyShadeView.cancelAnimation();
+                } else {
+                    mEmptyShadeView.setInvisible();
+                    mEmptyShadeView.setVisibility(newVisibility);
+                }
+                mEmptyShadeView.setWillBeGone(false);
+                updateContentHeight();
+            } else {
+                mEmptyShadeView.setWillBeGone(true);
+                mEmptyShadeView.performVisibilityAnimation(false, new Runnable() {
+                    @Override
+                    public void run() {
+                        mEmptyShadeView.setVisibility(GONE);
+                        mEmptyShadeView.setWillBeGone(false);
+                        updateContentHeight();
+                    }
+                });
+            }
+        }
+    }
+
     public void updateDismissView(boolean visible) {
         int oldVisibility = mDismissView.willBeGone() ? GONE : mDismissView.getVisibility();
         int newVisibility = visible ? VISIBLE : GONE;
index a174952..f7a2824 100644 (file)
@@ -23,6 +23,7 @@ import android.view.ViewGroup;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.DismissView;
+import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.SpeedBumpView;
 
@@ -180,6 +181,11 @@ public class StackScrollState {
                     DismissView dismissView = (DismissView) child;
                     boolean visible = state.topOverLap < mClearAllTopPadding;
                     dismissView.performVisibilityAnimation(visible && !dismissView.willBeGone());
+                } else if (child instanceof EmptyShadeView) {
+                    EmptyShadeView emptyShadeView = (EmptyShadeView) child;
+                    boolean visible = state.topOverLap <= 0;
+                    emptyShadeView.performVisibilityAnimation(
+                            visible && !emptyShadeView.willBeGone());
                 }
             }
         }