Implemented visual speed-bump for notifications.
The separation between the important and the less important notifications has now a visual representation. Bug: 14607473 Change-Id: I8baa0a08924ec041be2884a2834139477313ab40
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
<!--
|
||||
~ 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.SpeedBumpView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
android:visibility="gone"
|
||||
>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/speed_bump_height_collapsed"
|
||||
android:layout_gravity="top"
|
||||
android:orientation="horizontal">
|
||||
<View
|
||||
android:id="@+id/speedbump_line_left"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_weight="1"
|
||||
android:background="#6fdddddd"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
<com.android.systemui.statusbar.SpeedBumpDotsLayout
|
||||
android:id="@+id/speed_bump_dots_layout"
|
||||
android:layout_width="34dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="0"/>
|
||||
<View
|
||||
android:id="@+id/speedbump_line_right"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_weight="1"
|
||||
android:background="#6fdddddd"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:id="@+id/speed_bump_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top|center_horizontal"
|
||||
android:fontFamily="sans-serif-condensed"
|
||||
android:textSize="15sp"
|
||||
android:singleLine="true"
|
||||
android:textColor="#eeeeee"
|
||||
android:visibility="invisible"
|
||||
android:text="@string/speed_bump_explanation"
|
||||
android:paddingTop="4dp" />
|
||||
</com.android.systemui.statusbar.SpeedBumpView>
|
||||
@@ -58,6 +58,18 @@
|
||||
<!-- Tint color for the content on the notification overflow card. -->
|
||||
<color name="keyguard_overflow_content_color">#ff666666</color>
|
||||
|
||||
<!-- The color of the red speed bump dot -->
|
||||
<color name="speed_bump_dot_red">#ffd50000</color>
|
||||
|
||||
<!-- The color of the blue speed bump dot -->
|
||||
<color name="speed_bump_dot_blue">#ff2962ff</color>
|
||||
|
||||
<!-- The color of the yellow speed bump dot -->
|
||||
<color name="speed_bump_dot_yellow">#ffffd600</color>
|
||||
|
||||
<!-- The color of the green speed bump dot -->
|
||||
<color name="speed_bump_dot_green">#ff00c853</color>
|
||||
|
||||
<!-- The default recents task bar background color. -->
|
||||
<color name="recents_task_bar_default_background_color">#e6444444</color>
|
||||
<!-- The default recents task bar text color. -->
|
||||
|
||||
@@ -260,6 +260,15 @@
|
||||
<!-- The padding between the individual notification cards. -->
|
||||
<dimen name="notification_padding">4dp</dimen>
|
||||
|
||||
<!-- The height of the collapsed speed bump view. -->
|
||||
<dimen name="speed_bump_height_collapsed">24dp</dimen>
|
||||
|
||||
<!-- The padding inset the explanation text needs compared to the collapsed height -->
|
||||
<dimen name="speed_bump_text_padding_inset">10dp</dimen>
|
||||
|
||||
<!-- The height of the speed bump dots. -->
|
||||
<dimen name="speed_bump_dots_height">5dp</dimen>
|
||||
|
||||
<!-- The total height of the stack in its collapsed size (i.e. when quick settings is open) -->
|
||||
<dimen name="collapsed_stack_height">94dp</dimen>
|
||||
|
||||
|
||||
@@ -553,6 +553,9 @@
|
||||
<item quantity="other">%d more</item>
|
||||
</plurals>
|
||||
|
||||
<!-- An explanation for the visual speed bump in the notifications, which will appear when you click on it. [CHAR LIMIT=50] -->
|
||||
<string name="speed_bump_explanation">Less urgent notifications below</string>
|
||||
|
||||
<!-- Shows to explain the double tap interaction with notifications: After tapping a notification on Keyguard, this will explain users to tap again to launch a notification. [CHAR LIMIT=60] -->
|
||||
<string name="notification_tap_again">Tap again to open</string>
|
||||
|
||||
|
||||
@@ -877,6 +877,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
entry.row = row;
|
||||
entry.row.setHeightRange(mRowMinHeight, mRowMaxHeight);
|
||||
entry.row.setOnActivatedListener(this);
|
||||
entry.row.setIsBelowSpeedBump(isBelowSpeedBump(entry.notification));
|
||||
entry.expanded = contentViewLocal;
|
||||
entry.expandedPublic = publicViewLocal;
|
||||
entry.setBigContentView(bigContentViewLocal);
|
||||
@@ -1039,8 +1040,8 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "addNotificationViews: added at " + pos);
|
||||
}
|
||||
updateRowStates();
|
||||
updateNotificationIcons();
|
||||
updateRowStates();
|
||||
}
|
||||
|
||||
private void addNotificationViews(IBinder key, StatusBarNotification notification) {
|
||||
@@ -1060,6 +1061,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
|
||||
int n = mNotificationData.size();
|
||||
int visibleNotifications = 0;
|
||||
int speedBumpIndex = -1;
|
||||
boolean onKeyguard = mState == StatusBarState.KEYGUARD;
|
||||
for (int i = n-1; i >= 0; i--) {
|
||||
NotificationData.Entry entry = mNotificationData.get(i);
|
||||
@@ -1087,6 +1089,10 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
entry.row.setVisibility(View.VISIBLE);
|
||||
visibleNotifications++;
|
||||
}
|
||||
if (entry.row.getVisibility() != View.GONE && speedBumpIndex == -1
|
||||
&& entry.row.isBelowSpeedBump() ) {
|
||||
speedBumpIndex = n - 1 - i;
|
||||
}
|
||||
}
|
||||
|
||||
if (onKeyguard && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0) {
|
||||
@@ -1094,6 +1100,8 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
} else {
|
||||
mKeyguardIconOverflowContainer.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
mStackScroller.updateSpeedBumpIndex(speedBumpIndex);
|
||||
}
|
||||
|
||||
private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
|
||||
@@ -1309,9 +1317,19 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
} else {
|
||||
entry.row.setOnClickListener(null);
|
||||
}
|
||||
boolean wasBelow = entry.row.isBelowSpeedBump();
|
||||
boolean nowBelow = isBelowSpeedBump(notification);
|
||||
if (wasBelow != nowBelow) {
|
||||
entry.row.setIsBelowSpeedBump(nowBelow);
|
||||
}
|
||||
entry.row.notifyContentUpdated();
|
||||
}
|
||||
|
||||
private boolean isBelowSpeedBump(StatusBarNotification notification) {
|
||||
return notification.getNotification().priority ==
|
||||
Notification.PRIORITY_MIN;
|
||||
}
|
||||
|
||||
protected void notifyHeadsUpScreenOn(boolean screenOn) {
|
||||
if (!screenOn && mInterruptingNotificationEntry != null) {
|
||||
mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP);
|
||||
|
||||
@@ -52,6 +52,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
private NotificationContentView mPublicLayout;
|
||||
private NotificationContentView mPrivateLayout;
|
||||
private int mMaxExpandHeight;
|
||||
private boolean mIsBelowSpeedBump;
|
||||
|
||||
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
@@ -244,6 +245,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
mPrivateLayout.setClipTopAmount(clipTopAmount);
|
||||
}
|
||||
|
||||
public boolean isBelowSpeedBump() {
|
||||
return mIsBelowSpeedBump;
|
||||
}
|
||||
|
||||
public void setIsBelowSpeedBump(boolean isBelow) {
|
||||
this.mIsBelowSpeedBump = isBelow;
|
||||
}
|
||||
|
||||
public void notifyContentUpdated() {
|
||||
mPrivateLayout.notifyContentUpdated();
|
||||
}
|
||||
|
||||
@@ -40,11 +40,15 @@ public abstract class ExpandableView extends FrameLayout {
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
if (!mActualHeightInitialized && mActualHeight == 0) {
|
||||
mActualHeight = getHeight();
|
||||
mActualHeight = getInitialHeight();
|
||||
}
|
||||
mActualHeightInitialized = true;
|
||||
}
|
||||
|
||||
protected int getInitialHeight() {
|
||||
return getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
if (filterMotionEvent(ev)) {
|
||||
@@ -146,6 +150,10 @@ public abstract class ExpandableView extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isTransparent() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener notifying when {@link #getActualHeight} changes.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.graphics.Canvas;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* An single dot of the {@link com.android.systemui.statusbar.SpeedBumpDotsLayout}
|
||||
*/
|
||||
public class SpeedBumpDotView extends View {
|
||||
|
||||
private final Paint mPaint = new Paint();
|
||||
|
||||
public SpeedBumpDotView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mPaint.setAntiAlias(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
float radius = getWidth() / 2.0f;
|
||||
canvas.drawCircle(radius, radius, radius, mPaint);
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
mPaint.setColor(color);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.view.View;
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* The Algorithm of the {@link com.android.systemui.statusbar.SpeedBumpDotsLayout} which can be
|
||||
* queried for {@link * com.android.systemui.statusbar.SpeedBumpDotsState}
|
||||
*/
|
||||
public class SpeedBumpDotsAlgorithm {
|
||||
|
||||
private final float mDotRadius;
|
||||
|
||||
public SpeedBumpDotsAlgorithm(Context context) {
|
||||
mDotRadius = context.getResources().getDimensionPixelSize(R.dimen.speed_bump_dots_height)
|
||||
/ 2.0f;
|
||||
}
|
||||
|
||||
public void getState(SpeedBumpDotsState resultState) {
|
||||
|
||||
// First reset the current state and ensure that every View has a ViewState
|
||||
resultState.resetViewStates();
|
||||
|
||||
SpeedBumpDotsLayout hostView = resultState.getHostView();
|
||||
boolean currentlyVisible = hostView.isCurrentlyVisible();
|
||||
resultState.setActiveState(currentlyVisible
|
||||
? SpeedBumpDotsState.SHOWN
|
||||
: SpeedBumpDotsState.HIDDEN);
|
||||
int hostWidth = hostView.getWidth();
|
||||
float layoutWidth = hostWidth - 2 * mDotRadius;
|
||||
int childCount = hostView.getChildCount();
|
||||
float paddingBetween = layoutWidth / (childCount - 1);
|
||||
float centerY = hostView.getHeight() / 2.0f;
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View child = hostView.getChildAt(i);
|
||||
SpeedBumpDotsState.ViewState viewState = resultState.getViewStateForView(child);
|
||||
if (currentlyVisible) {
|
||||
float xTranslation = i * paddingBetween;
|
||||
viewState.xTranslation = xTranslation;
|
||||
viewState.yTranslation = calculateYTranslation(hostView, centerY, xTranslation,
|
||||
layoutWidth);
|
||||
} else {
|
||||
viewState.xTranslation = layoutWidth / 2;
|
||||
viewState.yTranslation = centerY - mDotRadius;
|
||||
}
|
||||
viewState.alpha = currentlyVisible ? 1.0f : 0.0f;
|
||||
viewState.scale = currentlyVisible ? 1.0f : 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
private float calculateYTranslation(SpeedBumpDotsLayout hostView, float centerY,
|
||||
float xTranslation, float layoutWidth) {
|
||||
float t = hostView.getAnimationProgress();
|
||||
if (t == 0.0f || t == 1.0f) {
|
||||
return centerY - mDotRadius;
|
||||
}
|
||||
float damping = (0.5f -Math.abs(0.5f - t)) * 1.3f;
|
||||
float partialOffset = xTranslation / layoutWidth;
|
||||
float indentFactor = (float) (Math.sin((t + partialOffset * 1.5f) * - Math.PI) * damping);
|
||||
return (1.0f - indentFactor) * centerY - mDotRadius;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.animation.TimeAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* A layout with a certain number of dots which are integrated in the
|
||||
* {@link com.android.systemui.statusbar.SpeedBumpView}
|
||||
*/
|
||||
public class SpeedBumpDotsLayout extends ViewGroup {
|
||||
|
||||
private static final float DOT_CLICK_ANIMATION_LENGTH = 300;
|
||||
private final int mDotSize;
|
||||
private final SpeedBumpDotsAlgorithm mAlgorithm = new SpeedBumpDotsAlgorithm(getContext());
|
||||
private final SpeedBumpDotsState mCurrentState = new SpeedBumpDotsState(this);
|
||||
private boolean mIsCurrentlyVisible = true;
|
||||
private final ValueAnimator mClickAnimator;
|
||||
private float mAnimationProgress;
|
||||
private ValueAnimator.AnimatorUpdateListener mClickUpdateListener
|
||||
= new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
mAnimationProgress = animation.getAnimatedFraction();
|
||||
updateChildren();
|
||||
}
|
||||
};
|
||||
|
||||
public SpeedBumpDotsLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mDotSize = getResources().getDimensionPixelSize(R.dimen.speed_bump_dots_height);
|
||||
createDots(context, attrs);
|
||||
mClickAnimator = TimeAnimator.ofFloat(0, DOT_CLICK_ANIMATION_LENGTH);
|
||||
mClickAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||
mClickAnimator.addUpdateListener(mClickUpdateListener);
|
||||
}
|
||||
|
||||
private void createDots(Context context, AttributeSet attrs) {
|
||||
SpeedBumpDotView blueDot = new SpeedBumpDotView(context, attrs);
|
||||
blueDot.setColor(getResources().getColor(R.color.speed_bump_dot_blue));
|
||||
addView(blueDot);
|
||||
|
||||
SpeedBumpDotView redDot = new SpeedBumpDotView(context, attrs);
|
||||
redDot.setColor(getResources().getColor(R.color.speed_bump_dot_red));
|
||||
addView(redDot);
|
||||
|
||||
SpeedBumpDotView yellowDot = new SpeedBumpDotView(context, attrs);
|
||||
yellowDot.setColor(getResources().getColor(R.color.speed_bump_dot_yellow));
|
||||
addView(yellowDot);
|
||||
|
||||
SpeedBumpDotView greenDot = new SpeedBumpDotView(context, attrs);
|
||||
greenDot.setColor(getResources().getColor(R.color.speed_bump_dot_green));
|
||||
addView(greenDot);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
int childWidthSpec = MeasureSpec.makeMeasureSpec(mDotSize,
|
||||
MeasureSpec.getMode(widthMeasureSpec));
|
||||
int childHeightSpec = MeasureSpec.makeMeasureSpec(mDotSize,
|
||||
MeasureSpec.getMode(heightMeasureSpec));
|
||||
measureChildren(childWidthSpec, childHeightSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View child = getChildAt(i);
|
||||
child.layout(0, 0, mDotSize, mDotSize);
|
||||
}
|
||||
if (changed) {
|
||||
updateChildren();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateChildren() {
|
||||
mAlgorithm.getState(mCurrentState);
|
||||
mCurrentState.apply();
|
||||
}
|
||||
|
||||
public void performVisibilityAnimation(boolean visible) {
|
||||
if (mClickAnimator.isRunning()) {
|
||||
mClickAnimator.cancel();
|
||||
}
|
||||
mIsCurrentlyVisible = visible;
|
||||
mAlgorithm.getState(mCurrentState);
|
||||
mCurrentState.animateToState();
|
||||
}
|
||||
|
||||
public void setInvisible() {
|
||||
mIsCurrentlyVisible = false;
|
||||
mAlgorithm.getState(mCurrentState);
|
||||
mCurrentState.apply();
|
||||
}
|
||||
|
||||
public boolean isCurrentlyVisible() {
|
||||
return mIsCurrentlyVisible;
|
||||
}
|
||||
|
||||
public void performDotClickAnimation() {
|
||||
if (mClickAnimator.isRunning()) {
|
||||
// don't perform an animation if it's running already
|
||||
return;
|
||||
}
|
||||
mClickAnimator.start();
|
||||
}
|
||||
|
||||
|
||||
public float getAnimationProgress() {
|
||||
return mAnimationProgress;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.view.View;
|
||||
import android.view.ViewPropertyAnimator;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A state of a {@link com.android.systemui.statusbar.SpeedBumpDotsLayout}
|
||||
*/
|
||||
public class SpeedBumpDotsState {
|
||||
|
||||
public static final int HIDDEN = 1;
|
||||
public static final int SHOWN = 2;
|
||||
private static final int VISIBILITY_ANIMATION_DELAY_PER_ELEMENT = 80;
|
||||
|
||||
private final SpeedBumpDotsLayout mHostView;
|
||||
private final HashMap<View, ViewState> mStateMap = new HashMap<View, ViewState>();
|
||||
private final Interpolator mFastOutSlowInInterpolator;
|
||||
private int mActiveState = 0;
|
||||
|
||||
public SpeedBumpDotsState(SpeedBumpDotsLayout hostLayout) {
|
||||
mHostView = hostLayout;
|
||||
mFastOutSlowInInterpolator = AnimationUtils
|
||||
.loadInterpolator(hostLayout.getContext(),
|
||||
android.R.interpolator.fast_out_slow_in);
|
||||
}
|
||||
|
||||
public SpeedBumpDotsLayout getHostView() {
|
||||
return mHostView;
|
||||
}
|
||||
|
||||
public void resetViewStates() {
|
||||
int numChildren = mHostView.getChildCount();
|
||||
for (int i = 0; i < numChildren; i++) {
|
||||
View child = mHostView.getChildAt(i);
|
||||
ViewState viewState = mStateMap.get(child);
|
||||
if (viewState == null) {
|
||||
viewState = new ViewState();
|
||||
mStateMap.put(child, viewState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ViewState getViewStateForView(View requestedView) {
|
||||
return mStateMap.get(requestedView);
|
||||
}
|
||||
|
||||
public void apply() {
|
||||
int childCount = mHostView.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View child = mHostView.getChildAt(i);
|
||||
ViewState viewState = mStateMap.get(child);
|
||||
float translationX = child.getTranslationX();
|
||||
float translationY = child.getTranslationY();
|
||||
float scale = child.getScaleX();
|
||||
float alpha = child.getAlpha();
|
||||
if (translationX != viewState.xTranslation) {
|
||||
child.setTranslationX(viewState.xTranslation);
|
||||
}
|
||||
if (translationY != viewState.yTranslation) {
|
||||
child.setTranslationY(viewState.yTranslation);
|
||||
}
|
||||
if (scale != viewState.scale) {
|
||||
child.setScaleX(viewState.scale);
|
||||
child.setScaleY(viewState.scale);
|
||||
}
|
||||
if (alpha != viewState.alpha) {
|
||||
child.setAlpha(viewState.alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void animateToState() {
|
||||
int childCount = mHostView.getChildCount();
|
||||
int middleIndex = (childCount - 1) / 2;
|
||||
long delayPerElement = VISIBILITY_ANIMATION_DELAY_PER_ELEMENT;
|
||||
boolean isAppearing = getActiveState() == SHOWN;
|
||||
boolean isDisappearing = getActiveState() == HIDDEN;
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
int delayIndex;
|
||||
if (i <= middleIndex) {
|
||||
delayIndex = i * 2;
|
||||
} else {
|
||||
int distToMiddle = i - middleIndex;
|
||||
delayIndex = (childCount - 1) - (distToMiddle - 1) * 2;
|
||||
}
|
||||
long startDelay = 0;
|
||||
if (isAppearing || isDisappearing) {
|
||||
if (isDisappearing) {
|
||||
delayIndex = childCount - 1 - delayIndex;
|
||||
}
|
||||
startDelay = delayIndex * delayPerElement;
|
||||
}
|
||||
View child = mHostView.getChildAt(i);
|
||||
ViewState viewState = mStateMap.get(child);
|
||||
child.animate().setInterpolator(mFastOutSlowInInterpolator)
|
||||
.setStartDelay(startDelay)
|
||||
.alpha(viewState.alpha).withLayer()
|
||||
.translationX(viewState.xTranslation)
|
||||
.translationY(viewState.yTranslation)
|
||||
.scaleX(viewState.scale).scaleY(viewState.scale);
|
||||
}
|
||||
}
|
||||
|
||||
public int getActiveState() {
|
||||
return mActiveState;
|
||||
}
|
||||
|
||||
public void setActiveState(int mActiveState) {
|
||||
this.mActiveState = mActiveState;
|
||||
}
|
||||
|
||||
public static class ViewState {
|
||||
float xTranslation;
|
||||
float yTranslation;
|
||||
float alpha;
|
||||
float scale;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* 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.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Outline;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.widget.TextView;
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* The view representing the separation between important and less important notifications
|
||||
*/
|
||||
public class SpeedBumpView extends ExpandableView implements View.OnClickListener {
|
||||
|
||||
private final int mCollapsedHeight;
|
||||
private final int mDotsHeight;
|
||||
private final int mTextPaddingInset;
|
||||
private SpeedBumpDotsLayout mDots;
|
||||
private View mLineLeft;
|
||||
private View mLineRight;
|
||||
private boolean mIsExpanded;
|
||||
private boolean mDividerVisible = true;
|
||||
private ValueAnimator mCurrentAnimator;
|
||||
private final Interpolator mFastOutSlowInInterpolator;
|
||||
private float mCenterX;
|
||||
private TextView mExplanationText;
|
||||
private boolean mExplanationTextVisible = false;
|
||||
private AnimatorListenerAdapter mHideExplanationListener = new AnimatorListenerAdapter() {
|
||||
private boolean mCancelled;
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (!mCancelled) {
|
||||
mExplanationText.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mCancelled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mCancelled = false;
|
||||
}
|
||||
};
|
||||
private Animator.AnimatorListener mAnimationFinishedListener = new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mCurrentAnimator = null;
|
||||
}
|
||||
};
|
||||
|
||||
public SpeedBumpView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mCollapsedHeight = getResources()
|
||||
.getDimensionPixelSize(R.dimen.speed_bump_height_collapsed);
|
||||
mTextPaddingInset = getResources().getDimensionPixelSize(
|
||||
R.dimen.speed_bump_text_padding_inset);
|
||||
mDotsHeight = getResources().getDimensionPixelSize(R.dimen.speed_bump_dots_height);
|
||||
setOnClickListener(this);
|
||||
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
|
||||
android.R.interpolator.fast_out_slow_in);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mDots = (SpeedBumpDotsLayout) findViewById(R.id.speed_bump_dots_layout);
|
||||
mLineLeft = findViewById(R.id.speedbump_line_left);
|
||||
mLineRight = findViewById(R.id.speedbump_line_right);
|
||||
mExplanationText = (TextView) findViewById(R.id.speed_bump_text);
|
||||
resetExplanationText();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getInitialHeight() {
|
||||
return mCollapsedHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIntrinsicHeight() {
|
||||
return getActualHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
Outline outline = new Outline();
|
||||
mCenterX = getWidth() / 2;
|
||||
float centerY = getHeight() / 2;
|
||||
// TODO: hide outline better
|
||||
// Temporary workaround to hide outline on a transparent view
|
||||
int outlineLeft = (int) (mCenterX - getResources().getDisplayMetrics().densityDpi * 8);
|
||||
int outlineTop = (int) (centerY - mDotsHeight / 2);
|
||||
outline.setOval(outlineLeft, outlineTop, outlineLeft + mDotsHeight,
|
||||
outlineTop + mDotsHeight);
|
||||
setOutline(outline);
|
||||
mLineLeft.setPivotX(mLineLeft.getWidth());
|
||||
mLineLeft.setPivotY(mLineLeft.getHeight() / 2);
|
||||
mLineRight.setPivotX(0);
|
||||
mLineRight.setPivotY(mLineRight.getHeight() / 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
measureChildren(widthMeasureSpec, heightMeasureSpec);
|
||||
int height = mCollapsedHeight + mExplanationText.getMeasuredHeight() - mTextPaddingInset;
|
||||
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mCurrentAnimator != null) {
|
||||
return;
|
||||
}
|
||||
int startValue = mIsExpanded ? getMaxHeight() : mCollapsedHeight;
|
||||
int endValue = mIsExpanded ? mCollapsedHeight : getMaxHeight();
|
||||
mCurrentAnimator = ValueAnimator.ofInt(startValue, endValue);
|
||||
mCurrentAnimator.setInterpolator(mFastOutSlowInInterpolator);
|
||||
mCurrentAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
setActualHeight((int) animation.getAnimatedValue());
|
||||
}
|
||||
});
|
||||
mCurrentAnimator.addListener(mAnimationFinishedListener);
|
||||
mCurrentAnimator.start();
|
||||
mIsExpanded = !mIsExpanded;
|
||||
mDots.performDotClickAnimation();
|
||||
animateExplanationTextInternal(mIsExpanded);
|
||||
}
|
||||
|
||||
private void animateExplanationTextInternal(boolean visible) {
|
||||
if (mExplanationTextVisible != visible) {
|
||||
float translationY = 0.0f;
|
||||
float scale = 0.5f;
|
||||
float alpha = 0.0f;
|
||||
boolean needsHideListener = true;
|
||||
if (visible) {
|
||||
mExplanationText.setVisibility(VISIBLE);
|
||||
translationY = mDots.getBottom() - mTextPaddingInset;
|
||||
scale = 1.0f;
|
||||
alpha = 1.0f;
|
||||
needsHideListener = false;
|
||||
}
|
||||
mExplanationText.animate().setInterpolator(mFastOutSlowInInterpolator)
|
||||
.alpha(alpha)
|
||||
.scaleX(scale)
|
||||
.scaleY(scale)
|
||||
.translationY(translationY)
|
||||
.setListener(needsHideListener ? mHideExplanationListener : null)
|
||||
.withLayer();
|
||||
mExplanationTextVisible = visible;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTransparent() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void performVisibilityAnimation(boolean nowVisible) {
|
||||
animateDivider(nowVisible);
|
||||
|
||||
// Animate explanation Text
|
||||
if (mIsExpanded) {
|
||||
animateExplanationTextInternal(nowVisible);
|
||||
}
|
||||
}
|
||||
|
||||
public void animateDivider(boolean nowVisible) {
|
||||
if (nowVisible != mDividerVisible) {
|
||||
// Animate dividers
|
||||
float endValue = nowVisible ? 1.0f : 0.0f;
|
||||
float endTranslationXLeft = nowVisible ? 0.0f : mCenterX - mLineLeft.getRight();
|
||||
float endTranslationXRight = nowVisible ? 0.0f : mCenterX - mLineRight.getLeft();
|
||||
mLineLeft.animate()
|
||||
.alpha(endValue)
|
||||
.withLayer()
|
||||
.scaleX(endValue)
|
||||
.scaleY(endValue)
|
||||
.translationX(endTranslationXLeft)
|
||||
.setInterpolator(mFastOutSlowInInterpolator);
|
||||
mLineRight.animate()
|
||||
.alpha(endValue)
|
||||
.withLayer()
|
||||
.scaleX(endValue)
|
||||
.scaleY(endValue)
|
||||
.translationX(endTranslationXRight)
|
||||
.setInterpolator(mFastOutSlowInInterpolator);
|
||||
|
||||
// Animate dots
|
||||
mDots.performVisibilityAnimation(nowVisible);
|
||||
mDividerVisible = nowVisible;
|
||||
}
|
||||
}
|
||||
|
||||
public void setInvisible() {
|
||||
float endTranslationXLeft = mCenterX - mLineLeft.getRight();
|
||||
float endTranslationXRight = mCenterX - mLineRight.getLeft();
|
||||
mLineLeft.setAlpha(0.0f);
|
||||
mLineLeft.setScaleX(0.0f);
|
||||
mLineLeft.setScaleY(0.0f);
|
||||
mLineLeft.setTranslationX(endTranslationXLeft);
|
||||
mLineRight.setAlpha(0.0f);
|
||||
mLineRight.setScaleX(0.0f);
|
||||
mLineRight.setScaleY(0.0f);
|
||||
mLineRight.setTranslationX(endTranslationXRight);
|
||||
mDots.setInvisible();
|
||||
resetExplanationText();
|
||||
|
||||
mDividerVisible = false;
|
||||
}
|
||||
|
||||
public void collapse() {
|
||||
if (mIsExpanded) {
|
||||
setActualHeight(mCollapsedHeight);
|
||||
mIsExpanded = false;
|
||||
}
|
||||
resetExplanationText();
|
||||
}
|
||||
|
||||
public void animateExplanationText(boolean nowVisible) {
|
||||
if (mIsExpanded) {
|
||||
animateExplanationTextInternal(nowVisible);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetExplanationText() {
|
||||
mExplanationText.setTranslationY(0);
|
||||
mExplanationText.setVisibility(INVISIBLE);
|
||||
mExplanationText.setAlpha(0.0f);
|
||||
mExplanationText.setScaleX(0.5f);
|
||||
mExplanationText.setScaleY(0.5f);
|
||||
mExplanationTextVisible = false;
|
||||
}
|
||||
|
||||
public boolean isExpanded() {
|
||||
return mIsExpanded;
|
||||
}
|
||||
}
|
||||
@@ -143,8 +143,9 @@ public class NotificationPanelView extends PanelView implements
|
||||
}
|
||||
}
|
||||
|
||||
public void animateNextTopPaddingChange() {
|
||||
public void animateToFullShade() {
|
||||
mAnimateNextTopPaddingChange = true;
|
||||
mNotificationStackScroller.goToFullShade();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
|
||||
@@ -106,6 +106,7 @@ import com.android.systemui.statusbar.NotificationData;
|
||||
import com.android.systemui.statusbar.NotificationData.Entry;
|
||||
import com.android.systemui.statusbar.NotificationOverflowContainer;
|
||||
import com.android.systemui.statusbar.SignalClusterView;
|
||||
import com.android.systemui.statusbar.SpeedBumpView;
|
||||
import com.android.systemui.statusbar.StatusBarIconView;
|
||||
import com.android.systemui.statusbar.StatusBarState;
|
||||
import com.android.systemui.statusbar.policy.BatteryController;
|
||||
@@ -634,6 +635,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
|
||||
mStackScroller.addView(mKeyguardIconOverflowContainer);
|
||||
|
||||
SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate(
|
||||
R.layout.status_bar_notification_speed_bump, mStackScroller, false);
|
||||
mStackScroller.setSpeedBumpView(speedBump);
|
||||
|
||||
mExpandedContents = mStackScroller;
|
||||
|
||||
mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
|
||||
@@ -1150,7 +1155,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
ArrayList<View> toRemove = new ArrayList<View>();
|
||||
for (int i=0; i< mStackScroller.getChildCount(); i++) {
|
||||
View child = mStackScroller.getChildAt(i);
|
||||
if (!toShow.contains(child) && child != mKeyguardIconOverflowContainer) {
|
||||
if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
|
||||
toRemove.add(child);
|
||||
}
|
||||
}
|
||||
@@ -2722,7 +2727,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
setBarState(StatusBarState.SHADE);
|
||||
if (mLeaveOpenOnKeyguardHide) {
|
||||
mLeaveOpenOnKeyguardHide = false;
|
||||
mNotificationPanel.animateNextTopPaddingChange();
|
||||
mNotificationPanel.animateToFullShade();
|
||||
} else {
|
||||
instantCollapseNotificationPanel();
|
||||
}
|
||||
@@ -2894,7 +2899,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
mLeaveOpenOnKeyguardHide = true;
|
||||
showBouncer();
|
||||
} else {
|
||||
mNotificationPanel.animateNextTopPaddingChange();
|
||||
mNotificationPanel.animateToFullShade();
|
||||
setBarState(StatusBarState.SHADE_LOCKED);
|
||||
updateKeyguardState();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ public class AmbientState {
|
||||
private View mActivatedChild;
|
||||
private float mOverScrollTopAmount;
|
||||
private float mOverScrollBottomAmount;
|
||||
private int mSpeedBumpIndex = -1;
|
||||
|
||||
public int getScrollY() {
|
||||
return mScrollY;
|
||||
@@ -86,4 +87,12 @@ public class AmbientState {
|
||||
public float getOverScrollAmount(boolean top) {
|
||||
return top ? mOverScrollTopAmount : mOverScrollBottomAmount;
|
||||
}
|
||||
|
||||
public int getSpeedBumpIndex() {
|
||||
return mSpeedBumpIndex;
|
||||
}
|
||||
|
||||
public void setSpeedBumpIndex(int speedBumpIndex) {
|
||||
mSpeedBumpIndex = speedBumpIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import com.android.systemui.R;
|
||||
import com.android.systemui.SwipeHelper;
|
||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.ExpandableView;
|
||||
import com.android.systemui.statusbar.SpeedBumpView;
|
||||
import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
|
||||
import com.android.systemui.statusbar.policy.ScrollAdapter;
|
||||
|
||||
@@ -126,6 +127,8 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
private boolean mActivateNeedsAnimation;
|
||||
private boolean mIsExpanded = true;
|
||||
private boolean mChildrenUpdateRequested;
|
||||
private SpeedBumpView mSpeedBumpView;
|
||||
private boolean mIsExpansionChanging;
|
||||
private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
|
||||
= new ViewTreeObserver.OnPreDrawListener() {
|
||||
@Override
|
||||
@@ -244,6 +247,22 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
requestChildrenUpdate();
|
||||
}
|
||||
|
||||
public void updateSpeedBumpIndex(int newIndex) {
|
||||
int currentIndex = indexOfChild(mSpeedBumpView);
|
||||
|
||||
// If we are currently layouted before the new speed bump index, we have to decrease it.
|
||||
boolean validIndex = newIndex > 0;
|
||||
if (newIndex > getChildCount() - 1) {
|
||||
validIndex = false;
|
||||
newIndex = -1;
|
||||
}
|
||||
if (validIndex && currentIndex != newIndex) {
|
||||
changeViewPosition(mSpeedBumpView, newIndex);
|
||||
}
|
||||
updateSpeedBump(validIndex);
|
||||
mAmbientState.setSpeedBumpIndex(newIndex);
|
||||
}
|
||||
|
||||
public void setChildLocationsChangedListener(OnChildLocationsChangedListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
@@ -1044,6 +1063,10 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
mCurrentStackScrollState.removeViewStateForView(child);
|
||||
mStackScrollAlgorithm.notifyChildrenChanged(this);
|
||||
updateScrollStateForRemovedChild(child);
|
||||
generateRemoveAnimation(child);
|
||||
}
|
||||
|
||||
private void generateRemoveAnimation(View child) {
|
||||
if (mIsExpanded) {
|
||||
|
||||
if (!mChildrenToAddAnimated.contains(child)) {
|
||||
@@ -1120,7 +1143,9 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
*/
|
||||
public void changeViewPosition(View child, int newIndex) {
|
||||
if (child != null && child.getParent() == this) {
|
||||
// TODO: handle this
|
||||
removeView(child);
|
||||
addView(child, newIndex);
|
||||
// TODO: handle events
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1362,10 +1387,12 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
}
|
||||
|
||||
public void onExpansionStarted() {
|
||||
mIsExpansionChanging = true;
|
||||
mStackScrollAlgorithm.onExpansionStarted(mCurrentStackScrollState);
|
||||
}
|
||||
|
||||
public void onExpansionStopped() {
|
||||
mIsExpansionChanging = false;
|
||||
mStackScrollAlgorithm.onExpansionStopped();
|
||||
}
|
||||
|
||||
@@ -1374,6 +1401,7 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
mStackScrollAlgorithm.setIsExpanded(isExpanded);
|
||||
if (!isExpanded) {
|
||||
mOwnScrollY = 0;
|
||||
mSpeedBumpView.collapse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1432,6 +1460,34 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
}
|
||||
}
|
||||
|
||||
public void setSpeedBumpView(SpeedBumpView speedBumpView) {
|
||||
mSpeedBumpView = speedBumpView;
|
||||
addView(speedBumpView);
|
||||
}
|
||||
|
||||
private void updateSpeedBump(boolean visible) {
|
||||
int newVisibility = visible ? VISIBLE : GONE;
|
||||
int oldVisibility = mSpeedBumpView.getVisibility();
|
||||
if (newVisibility != oldVisibility) {
|
||||
mSpeedBumpView.setVisibility(newVisibility);
|
||||
if (visible) {
|
||||
mSpeedBumpView.collapse();
|
||||
// Make invisible to ensure that the appear animation is played.
|
||||
mSpeedBumpView.setInvisible();
|
||||
if (!mIsExpansionChanging) {
|
||||
generateAddAnimation(mSpeedBumpView);
|
||||
}
|
||||
} else {
|
||||
mSpeedBumpView.performVisibilityAnimation(false);
|
||||
generateRemoveAnimation(mSpeedBumpView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void goToFullShade() {
|
||||
updateSpeedBump(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener that is notified when some child locations might have changed.
|
||||
*/
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.android.systemui.statusbar.ExpandableView;
|
||||
import com.android.systemui.statusbar.SpeedBumpView;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -167,13 +168,50 @@ public class StackScrollState {
|
||||
clipHeight,
|
||||
(int) (newHeight - (previousNotificationStart - newYTranslation)));
|
||||
|
||||
previousNotificationStart = newYTranslation + child.getClipTopAmount();
|
||||
previousNotificationEnd = newNotificationEnd;
|
||||
previousNotificationIsSwiped = child.getTranslationX() != 0;
|
||||
if (!child.isTransparent()) {
|
||||
// Only update the previous values if we are not transparent,
|
||||
// otherwise we would clip to a transparent view.
|
||||
previousNotificationStart = newYTranslation + child.getClipTopAmount();
|
||||
previousNotificationEnd = newNotificationEnd;
|
||||
previousNotificationIsSwiped = child.getTranslationX() != 0;
|
||||
}
|
||||
|
||||
if(child instanceof SpeedBumpView) {
|
||||
performSpeedBumpAnimation(i, (SpeedBumpView) child, newNotificationEnd,
|
||||
newYTranslation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, float speedBumpEnd,
|
||||
float speedBumpStart) {
|
||||
View nextChild = getNextChildNotGone(i);
|
||||
if (nextChild != null) {
|
||||
ViewState nextState = getViewStateForView(nextChild);
|
||||
boolean startIsAboveNext = nextState.yTranslation > speedBumpStart;
|
||||
speedBump.animateDivider(startIsAboveNext);
|
||||
|
||||
// handle expanded case
|
||||
if (speedBump.isExpanded()) {
|
||||
boolean endIsAboveNext = nextState.yTranslation > speedBumpEnd;
|
||||
speedBump.animateExplanationText(endIsAboveNext);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private View getNextChildNotGone(int childIndex) {
|
||||
int childCount = mHostView.getChildCount();
|
||||
for (int i = childIndex + 1; i < childCount; i++) {
|
||||
View child = mHostView.getChildAt(i);
|
||||
if (child.getVisibility() != View.GONE) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the shadow outline and the clipping for a view.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user