diff options
author | Jorim Jaggi <jjaggi@google.com> | 2014-08-05 16:22:30 +0200 |
---|---|---|
committer | Jorim Jaggi <jjaggi@google.com> | 2014-08-05 16:38:02 +0200 |
commit | a2052ea218386877e6d5d2136483a62b2b31f774 (patch) | |
tree | 4b933b155795ab6abb280b9aff74438ea91074d9 | |
parent | f479792e05485a536c3fa68db9d8a71f34591b78 (diff) | |
download | frameworks_base-a2052ea218386877e6d5d2136483a62b2b31f774.zip frameworks_base-a2052ea218386877e6d5d2136483a62b2b31f774.tar.gz frameworks_base-a2052ea218386877e6d5d2136483a62b2b31f774.tar.bz2 |
Empty notification shade state.
Bug: 16483230
Change-Id: I7953a7954cae12124146f462ed8c0dc44769a38f
9 files changed, 302 insertions, 122 deletions
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 index 0000000..dd501d4 --- /dev/null +++ b/packages/SystemUI/res/layout/status_bar_no_notifications.xml @@ -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> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index c2f6aa4..af92e49 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -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) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java b/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java index f674b06..897dbf2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/DismissView.java @@ -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 index 0000000..582d165 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java @@ -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 index 0000000..62a492e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StackScrollerDecorView.java @@ -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(); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 53c4740..98bb591 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -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); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 8dfed92..ae7d500 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -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; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 943ee21..fcca5fa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -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; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java index a174952..f7a2824 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java @@ -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()); } } } |