Introduced the notification header

In the new grouping model we don't have a dedicated expand button
anymore but only the notification header.

Bug: 24866646
Change-Id: I2e4bc82e502fde951a71b61cd81347c44b9637f6
This commit is contained in:
Selim Cinek
2015-10-30 16:28:00 -07:00
parent 263398f017
commit eef8428510
22 changed files with 446 additions and 428 deletions

View File

@@ -1,19 +0,0 @@
<!--
~ Copyright (C) 2015 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
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetTop="2dp"
android:drawable="@drawable/ic_expand_less"/>

View File

@@ -1,18 +0,0 @@
<!--
~ Copyright (C) 2015 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
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetTop="2dp"
android:drawable="@drawable/ic_expand_more"/>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="22.0dp"
android:height="22.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:pathData="M16.600000,8.600000l-4.600000,4.599999 -4.600000,-4.599999 -1.400000,1.400000 6.000000,6.000000 6.000000,-6.000000z"
android:fillColor="#FF000000"/>
</vector>

View File

@@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 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
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/notification_bottom_decor_height">
<TextView
android:id="@+id/notification_expand_button"
android:layout_width="wrap_content"
android:layout_height="@dimen/notification_bottom_decor_height"
android:background="@null"
android:layout_gravity="top|center_horizontal"
android:gravity="center_vertical|center_horizontal"
android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
android:text="@string/notification_collapse_button_text"
android:drawableEnd="@drawable/ic_collapse_children"
android:drawablePadding="1dp"
/>
</FrameLayout>

View File

@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 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
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/notification_bottom_decor_height">
<TextView
android:id="@+id/notification_expand_button"
android:layout_width="wrap_content"
android:layout_height="@dimen/notification_bottom_decor_height"
android:background="@null"
android:layout_gravity="top|center_horizontal"
android:gravity="center_vertical|center_horizontal"
android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title"
android:text="@string/notification_expand_button_text"
android:drawableEnd="@drawable/ic_expand_children"
android:drawablePadding="1dp"
/>
<com.android.systemui.statusbar.AlphaOptimizedView
android:id="@+id/notification_expand_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="top|center"
android:background="@*android:drawable/notification_template_divider" />
</FrameLayout>

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 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
-->
<com.android.systemui.statusbar.notification.NotificationHeaderView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/notification_header_height"
android:clipChildren="false">
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="@dimen/notification_header_height"
android:layout_gravity="start"
android:gravity="center_vertical"
android:paddingStart="@dimen/notification_content_margin_start">
<ImageView
android:id="@+id/header_notification_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="4dp"
/>
<TextView
android:id="@+id/number_of_children"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@*android:style/TextAppearance.Material.Notification"
android:layout_marginEnd="2dp"
android:layout_marginStart="1dp"
/>
<TextView
android:id="@+id/app_name_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material.Notification.HeaderTitle"
android:layout_marginStart="4dp"
android:layout_marginEnd="8dp"
/>
<TextView
android:id="@+id/app_title_sub_text_divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material.Notification.HeaderTitle"
android:layout_marginEnd="8dp"
android:text="@string/notification_header_divider_symbol"/>
<TextView
android:id="@+id/title_sub_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material.Notification.HeaderTitle"
android:layout_marginEnd="8dp" />
<TextView
android:id="@+id/post_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:textAppearance="@*android:style/TextAppearance.Material.Notification.Time"
android:paddingEnd="8dp"
/>
</LinearLayout>
<ImageButton
android:id="@+id/notification_expand_button"
android:background="@null"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="end|center_vertical"
android:src="@drawable/notification_expand_more"
android:layout_marginEnd="8dp"
/>
</com.android.systemui.statusbar.notification.NotificationHeaderView>

View File

@@ -61,11 +61,11 @@
/>
<ViewStub
android:layout="@layout/notification_expand_button"
android:id="@+id/more_button_stub"
android:inflatedId="@+id/notification_more_button_container"
android:layout="@layout/notification_header"
android:id="@+id/notification_header_stub"
android:inflatedId="@+id/notification_header"
android:layout_width="match_parent"
android:layout_height="@dimen/notification_bottom_decor_height"
android:layout_height="@dimen/notification_header_height"
/>
<ViewStub

View File

@@ -333,8 +333,8 @@
<!-- radius of the corners of the material rounded rect background but negative-->
<dimen name="notification_material_rounded_rect_radius_negative">-2dp</dimen>
<!-- height of the bottom decor below the notification if present (eg. an expand action) -->
<dimen name="notification_bottom_decor_height">48dp</dimen>
<!-- height of notification header view if present -->
<dimen name="notification_header_height">32dp</dimen>
<!-- The padding between notification children -->
<dimen name="notification_children_padding">2dp</dimen>

View File

@@ -751,6 +751,9 @@
<!-- Text for overflow card on Keyguard when there is not enough space for all notifications on Keyguard. [CHAR LIMIT=1] -->
<string name="keyguard_more_overflow_text">+<xliff:g id="number_of_notifications" example="5">%d</xliff:g></string>
<!-- The divider symbol between different parts of the notification header. not translatable [CHAR LIMIT=1] -->
<string name="notification_header_divider_symbol" translatable="false">•</string>
<!-- 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>
@@ -978,12 +981,6 @@
<!-- continue action for notification educating the user about enabling notifications on the lockscreen. [CHAR LIMIT=20] -->
<string name="hidden_notifications_setup">Set up</string>
<!-- Text for the button to expand the notifications to show notification children [CHAR LIMIT=20] -->
<string name="notification_expand_button_text">See all</string>
<!-- Text for the button to expand the notifications to hide notification children [CHAR LIMIT=20] -->
<string name="notification_collapse_button_text">Hide all</string>
<!-- Accessibility string for current zen mode and selected exit condition. A template that simply concatenates existing mode string and the current condition description. [CHAR LIMIT=20] -->
<string name="zen_mode_and_condition"><xliff:g id="zen_mode" example="Priority interruptions only">%1$s</xliff:g>. <xliff:g id="exit_condition" example="For one hour">%2$s</xliff:g></string>

View File

@@ -244,6 +244,10 @@
<item name="android:textColor">#60000000</item>
</style>
<style name="TextAppearance.Material.Notification.HeaderTitle"
parent="@*android:style/TextAppearance.Material.Notification.Info">
</style>
<style name="SearchPanelCircle">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>

View File

@@ -138,11 +138,11 @@ public class ExpandHelper implements Gefingerpoken {
}
public void setHeight(float h) {
if (DEBUG_SCALE) Log.v(TAG, "SetHeight: setting to " + h);
mView.setContentHeight((int) h);
mView.setActualHeight((int) h);
mCurrentHeight = h;
}
public float getHeight() {
return mView.getContentHeight();
return mView.getActualHeight();
}
public int getNaturalHeight(int maximum) {
return Math.min(maximum, mView.getMaxContentHeight());

View File

@@ -891,8 +891,7 @@ public abstract class BaseStatusBar extends SystemUI implements
private void bindGuts(ExpandableNotificationRow row) {
row.inflateGuts();
final StatusBarNotification sbn = row.getStatusBarNotification();
PackageManager pmUser = getPackageManagerForUser(
sbn.getUser().getIdentifier());
PackageManager pmUser = getPackageManagerForUser(mContext, sbn.getUser().getIdentifier());
row.setTag(sbn.getPackageName());
final View guts = row.getGuts();
final String pkg = sbn.getPackageName();
@@ -1270,7 +1269,7 @@ public abstract class BaseStatusBar extends SystemUI implements
}
protected boolean inflateViews(Entry entry, ViewGroup parent) {
PackageManager pmUser = getPackageManagerForUser(
PackageManager pmUser = getPackageManagerForUser(mContext,
entry.notification.getUser().getIdentifier());
int maxHeight = mRowMaxHeight;
@@ -1502,7 +1501,7 @@ public abstract class BaseStatusBar extends SystemUI implements
row.setUserExpanded(userExpanded);
}
row.setUserLocked(userLocked);
row.setStatusBarNotification(entry.notification);
row.setEntry(entry);
applyRemoteInput(entry);
return true;
}
@@ -2113,7 +2112,7 @@ public abstract class BaseStatusBar extends SystemUI implements
// update the contentIntent
mNotificationClicker.register(entry.row, sbn);
entry.row.setStatusBarNotification(sbn);
entry.row.setEntry(entry);
entry.row.notifyContentUpdated();
entry.row.resetHeight();
@@ -2207,15 +2206,15 @@ public abstract class BaseStatusBar extends SystemUI implements
* @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
* return PackageManager for mContext
*/
protected PackageManager getPackageManagerForUser(int userId) {
Context contextForUser = mContext;
public static PackageManager getPackageManagerForUser(Context context, int userId) {
Context contextForUser = context;
// UserHandle defines special userId as negative values, e.g. USER_ALL
if (userId >= 0) {
try {
// Create a context for the correct user so if a package isn't installed
// for user 0 we can still load information about the package.
contextForUser =
mContext.createPackageContextAsUser(mContext.getPackageName(),
context.createPackageContextAsUser(context.getPackageName(),
Context.CONTEXT_RESTRICTED,
new UserHandle(userId));
} catch (NameNotFoundException e) {

View File

@@ -177,16 +177,16 @@ public class DragDownHelper implements Gefingerpoken {
overshoot *= (1 - RUBBERBAND_FACTOR_STATIC);
rubberband -= overshoot;
}
child.setContentHeight((int) (child.getMinHeight() + rubberband));
child.setActualHeight((int) (child.getMinHeight() + rubberband));
}
private void cancelExpansion(final ExpandableView child) {
if (child.getContentHeight() == child.getMinHeight()) {
if (child.getActualHeight() == child.getMinHeight()) {
mCallback.setUserLockedChild(child, false);
return;
}
ObjectAnimator anim = ObjectAnimator.ofInt(child, "contentHeight",
child.getContentHeight(), child.getMinHeight());
ObjectAnimator anim = ObjectAnimator.ofInt(child, "actualHeight",
child.getActualHeight(), child.getMinHeight());
anim.setInterpolator(mInterpolator);
anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS);
anim.addListener(new AnimatorListenerAdapter() {

View File

@@ -16,9 +16,7 @@
package com.android.systemui.statusbar;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.Notification;
import android.content.Context;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.AnimationDrawable;
@@ -39,6 +37,7 @@ import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
import com.android.systemui.statusbar.notification.NotificationHeaderView;
import com.android.systemui.statusbar.stack.StackScrollState;
import com.android.systemui.statusbar.stack.StackStateAnimator;
import com.android.systemui.statusbar.stack.StackViewState;
@@ -90,32 +89,21 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
private StatusBarNotification mStatusBarNotification;
private boolean mIsHeadsUp;
private boolean mLastChronometerRunning = true;
private View mExpandButton;
private View mExpandButtonDivider;
private ViewStub mExpandButtonStub;
private NotificationHeaderView mNotificationHeader;
private ViewStub mNotificationHeaderStub;
private ViewStub mChildrenContainerStub;
private NotificationGroupManager mGroupManager;
private View mExpandButtonContainer;
private boolean mChildrenExpanded;
private boolean mIsSummaryWithChildren;
private NotificationChildrenContainer mChildrenContainer;
private ValueAnimator mChildExpandAnimator;
private float mChildrenExpandProgress;
private float mExpandButtonStart;
private ViewStub mGutsStub;
private boolean mHasExpandAction;
private boolean mHasNotificationHeader;
private boolean mIsSystemChildExpanded;
private boolean mIsPinned;
private OnClickListener mExpandClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
mGroupManager.setGroupExpanded(mStatusBarNotification,
!mChildrenExpanded);
}
};
private FalsingManager mFalsingManager;
private boolean mJustClicked;
private NotificationData.Entry mEntry;
private boolean mChildInGroup;
public NotificationContentView getPrivateLayout() {
@@ -173,10 +161,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
}
}
public void setStatusBarNotification(StatusBarNotification statusBarNotification) {
private void setStatusBarNotification(StatusBarNotification statusBarNotification) {
mStatusBarNotification = statusBarNotification;
updateVetoButton();
updateExpandButton();
onChildrenCountChanged();
}
@@ -254,6 +241,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
return mChildrenContainer == null ? null : mChildrenContainer.getNotificationChildren();
}
public int getNumberOfNotificationChildren() {
if (mChildrenContainer == null) {
return 0;
}
return mChildrenContainer.getNotificationChildren().size();
}
/**
* Apply the order given in the list to the children.
*
@@ -304,15 +298,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
return mGuts;
}
protected int calculateContentHeightFromActualHeight(int actualHeight) {
int realActualHeight = actualHeight;
if (hasBottomDecor()) {
realActualHeight -= getBottomDecorHeight();
}
realActualHeight = Math.max(getMinHeight(), realActualHeight);
return realActualHeight;
}
/**
* Set this notification to be pinned to the top if {@link #isHeadsUp()} is true. By doing this
* the notification will be rendered on top of the screen.
@@ -382,6 +367,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
}
}
public void setEntry(NotificationData.Entry entry) {
mEntry = entry;
setStatusBarNotification(entry.notification);
}
public interface ExpansionLogger {
public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
}
@@ -445,15 +435,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
mGutsStub = null;
}
});
mExpandButtonStub = (ViewStub) findViewById(R.id.more_button_stub);
mExpandButtonStub.setOnInflateListener(new ViewStub.OnInflateListener() {
mNotificationHeaderStub = (ViewStub) findViewById(R.id.notification_header_stub);
mNotificationHeaderStub.setOnInflateListener(new ViewStub.OnInflateListener() {
@Override
public void onInflate(ViewStub stub, View inflated) {
mExpandButtonContainer = inflated;
mExpandButton = inflated.findViewById(R.id.notification_expand_button);
mExpandButtonDivider = inflated.findViewById(R.id.notification_expand_divider);
mExpandButtonContainer.setOnClickListener(mExpandClickListener);
mNotificationHeader = (NotificationHeaderView) inflated;
mNotificationHeader.setGroupManager(mGroupManager);
mNotificationHeader.bind(mEntry);
}
});
mChildrenContainerStub = (ViewStub) findViewById(R.id.child_container_stub);
@@ -462,8 +450,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
@Override
public void onInflate(ViewStub stub, View inflated) {
mChildrenContainer = (NotificationChildrenContainer) inflated;
mChildrenContainer.setCollapseClickListener(mExpandClickListener);
updateChildrenVisibility(false);
}
});
mVetoButton = findViewById(R.id.veto);
@@ -479,48 +465,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
if (mChildrenContainer == null) {
return;
}
if (mChildExpandAnimator != null) {
mChildExpandAnimator.cancel();
}
float targetProgress = mChildrenExpanded ? 1.0f : 0.0f;
if (animated) {
if (mChildrenExpanded) {
mChildrenContainer.setVisibility(VISIBLE);
}
mExpandButtonStart = mExpandButtonContainer.getTranslationY();
mChildExpandAnimator = ValueAnimator.ofFloat(mChildrenExpandProgress, targetProgress);
mChildExpandAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
setChildrenExpandProgress((float) animation.getAnimatedValue());
}
});
mChildExpandAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mChildExpandAnimator = null;
if (!mChildrenExpanded) {
mChildrenContainer.setVisibility(INVISIBLE);
}
}
});
mChildExpandAnimator.setInterpolator(mLinearInterpolator);
mChildExpandAnimator.setDuration(
StackStateAnimator.ANIMATION_DURATION_EXPAND_CLICKED);
mChildExpandAnimator.start();
} else {
setChildrenExpandProgress(targetProgress);
mChildrenContainer.setVisibility(mChildrenExpanded ? VISIBLE : INVISIBLE);
}
}
private void setChildrenExpandProgress(float progress) {
mChildrenExpandProgress = progress;
updateExpandButtonAppearance();
NotificationContentView showingLayout = getShowingLayout();
float alpha = 1.0f - mChildrenExpandProgress;
alpha = PhoneStatusBar.ALPHA_OUT.getInterpolation(alpha);
showingLayout.setAlpha(alpha);
mChildrenContainer.setVisibility(mIsSummaryWithChildren ? VISIBLE : INVISIBLE);
mPrivateLayout.setVisibility(!mIsSummaryWithChildren ? VISIBLE : INVISIBLE);
}
@Override
@@ -645,9 +591,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
public void applyExpansionToLayout() {
boolean expand = isExpanded();
if (expand && mExpandable) {
setContentHeight(mMaxExpandHeight);
setActualHeight(mMaxExpandHeight);
} else {
setContentHeight(mRowMinHeight);
setActualHeight(mRowMinHeight);
}
}
@@ -673,18 +619,18 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
} else {
maxContentHeight = getMaxExpandHeight();
}
return maxContentHeight + getBottomDecorHeight();
return maxContentHeight;
}
@Override
protected boolean hasBottomDecor() {
return BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS
&& !mIsHeadsUp && mGroupManager.hasGroupChildren(mStatusBarNotification);
private boolean isGroupExpanded() {
return mGroupManager.isGroupExpanded(mStatusBarNotification);
}
@Override
protected boolean canHaveBottomDecor() {
return BaseStatusBar.ENABLE_CHILD_NOTIFICATIONS && !mIsHeadsUp;
/**
* @return whether this view has a header on the top of the content
*/
private boolean hasNotificationHeader() {
return mIsSummaryWithChildren;
}
private void onChildrenCountChanged() {
@@ -693,6 +639,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
if (mIsSummaryWithChildren && mChildrenContainer == null) {
mChildrenContainerStub.inflate();
}
updateNotificationHeader();
updateChildrenVisibility(true);
}
@@ -812,105 +759,25 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
public void setChildrenExpanded(boolean expanded, boolean animate) {
mChildrenExpanded = expanded;
updateChildrenVisibility(animate);
}
public void updateExpandButton() {
boolean hasExpand = hasBottomDecor();
if (hasExpand != mHasExpandAction) {
if (hasExpand) {
if (mExpandButtonContainer == null) {
mExpandButtonStub.inflate();
public void updateNotificationHeader() {
boolean hasHeader = hasNotificationHeader();
if (hasHeader != mHasNotificationHeader) {
if (hasHeader) {
if (mNotificationHeader == null) {
mNotificationHeaderStub.inflate();
}
mExpandButtonContainer.setVisibility(View.VISIBLE);
updateExpandButtonAppearance();
updateExpandButtonColor();
} else if (mExpandButtonContainer != null) {
mExpandButtonContainer.setVisibility(View.GONE);
mNotificationHeader.setVisibility(View.VISIBLE);
} else if (mNotificationHeader != null) {
mNotificationHeader.setVisibility(View.GONE);
}
notifyHeightChanged(true /* needsAnimation */);
}
mHasExpandAction = hasExpand;
}
private void updateExpandButtonAppearance() {
if (mExpandButtonContainer == null) {
return;
}
float expandButtonAlpha = 0.0f;
float expandButtonTranslation = 0.0f;
float containerTranslation = 0.0f;
int minHeight = getMinHeight();
if (!mChildrenExpanded || mChildExpandAnimator != null) {
int expandActionHeight = getBottomDecorHeight();
int translationY = getActualHeight() - expandActionHeight;
if (translationY > minHeight) {
containerTranslation = translationY;
expandButtonAlpha = 1.0f;
expandButtonTranslation = 0.0f;
} else {
containerTranslation = minHeight;
float progress = expandActionHeight != 0
? (minHeight - translationY) / (float) expandActionHeight
: 1.0f;
expandButtonTranslation = -progress * expandActionHeight * 0.7f;
float alphaProgress = Math.min(progress / 0.7f, 1.0f);
alphaProgress = PhoneStatusBar.ALPHA_OUT.getInterpolation(alphaProgress);
expandButtonAlpha = 1.0f - alphaProgress;
}
}
if (mChildExpandAnimator != null || mChildrenExpanded) {
expandButtonAlpha = (1.0f - mChildrenExpandProgress)
* expandButtonAlpha;
expandButtonTranslation = (1.0f - mChildrenExpandProgress)
* expandButtonTranslation;
float newTranslation = -getBottomDecorHeight();
// We don't want to take the actual height of the view as this is already
// interpolated by a custom interpolator leading to a confusing animation. We want
// to have a stable end value to interpolate in between
float collapsedHeight = !mChildrenExpanded
? Math.max(StackStateAnimator.getFinalActualHeight(this)
- getBottomDecorHeight(), minHeight)
: mExpandButtonStart;
float translationProgress = mFastOutSlowInInterpolator.getInterpolation(
mChildrenExpandProgress);
containerTranslation = (1.0f - translationProgress) * collapsedHeight
+ translationProgress * newTranslation;
}
mExpandButton.setAlpha(expandButtonAlpha);
mExpandButtonDivider.setAlpha(expandButtonAlpha);
mExpandButton.setTranslationY(expandButtonTranslation);
mExpandButtonContainer.setTranslationY(containerTranslation);
NotificationContentView showingLayout = getShowingLayout();
float layoutTranslation =
mExpandButtonContainer.getTranslationY() - showingLayout.getContentHeight();
layoutTranslation = Math.min(layoutTranslation, 0);
if (!mChildrenExpanded && mChildExpandAnimator == null) {
// Needed for the DragDownHelper in order not to jump there, as the position
// can be negative for a short time.
layoutTranslation = 0;
}
showingLayout.setTranslationY(layoutTranslation);
if (mChildrenContainer != null) {
mChildrenContainer.setTranslationY(
mExpandButtonContainer.getTranslationY() + getBottomDecorHeight());
}
}
private void updateExpandButtonColor() {
// TODO: This needs some more baking, currently only the divider is colored according to
// the tint, but legacy black doesn't work yet perfectly for the button etc.
int color = getRippleColor();
if (color == mNormalRippleColor) {
color = 0;
}
if (mExpandButtonDivider != null) {
applyTint(mExpandButtonDivider, color);
}
if (mChildrenContainer != null) {
mChildrenContainer.setTintColor(color);
if (hasHeader) {
mNotificationHeader.bind(mEntry);
}
mHasNotificationHeader = hasHeader;
}
public static void applyTint(View v, int color) {
@@ -947,14 +814,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
@Override
public void setActualHeight(int height, boolean notifyListeners) {
super.setActualHeight(height, notifyListeners);
int contentHeight = calculateContentHeightFromActualHeight(height);
int contentHeight = Math.max(getMinHeight(), height);
mPrivateLayout.setContentHeight(contentHeight);
mPublicLayout.setContentHeight(contentHeight);
if (mGuts != null) {
mGuts.setActualHeight(height);
}
invalidate();
updateExpandButtonAppearance();
}
@Override

View File

@@ -33,7 +33,6 @@ import java.util.ArrayList;
*/
public abstract class ExpandableView extends FrameLayout {
private final int mBottomDecorHeight;
protected OnHeightChangedListener mOnHeightChangedListener;
protected int mMaxViewHeight;
private int mActualHeight;
@@ -50,12 +49,6 @@ public abstract class ExpandableView extends FrameLayout {
super(context, attrs);
mMaxViewHeight = getResources().getDimensionPixelSize(
R.dimen.notification_max_height);
mBottomDecorHeight = resolveBottomDecorHeight();
}
protected int resolveBottomDecorHeight() {
return getResources().getDimensionPixelSize(
R.dimen.notification_bottom_decor_height);
}
@Override
@@ -103,10 +96,6 @@ public abstract class ExpandableView extends FrameLayout {
}
mMatchParentViews.clear();
int width = MeasureSpec.getSize(widthMeasureSpec);
if (canHaveBottomDecor()) {
// We always account for the expandAction as well.
ownHeight += mBottomDecorHeight;
}
setMeasuredDimension(width, ownHeight);
}
@@ -120,7 +109,7 @@ public abstract class ExpandableView extends FrameLayout {
if (!mActualHeightInitialized && mActualHeight == 0) {
int initialHeight = getInitialHeight();
if (initialHeight != 0) {
setContentHeight(initialHeight);
setActualHeight(initialHeight);
}
}
updateClipping();
@@ -178,8 +167,8 @@ public abstract class ExpandableView extends FrameLayout {
}
}
public void setContentHeight(int contentHeight) {
setActualHeight(contentHeight + getBottomDecorHeight(), true);
public void setActualHeight(int actualHeight) {
setActualHeight(actualHeight, true /* notifyListeners */);
}
/**
@@ -191,31 +180,6 @@ public abstract class ExpandableView extends FrameLayout {
return mActualHeight;
}
/**
* This view may have a bottom decor which will be placed below the content. If it has one, this
* view will be layouted higher than just the content by {@link #mBottomDecorHeight}.
* @return the height of the decor if it currently has one
*/
public int getBottomDecorHeight() {
return hasBottomDecor() ? mBottomDecorHeight : 0;
}
/**
* @return whether this view may have a bottom decor at all. This will force the view to layout
* itself higher than just it's content
*/
protected boolean canHaveBottomDecor() {
return false;
}
/**
* @return whether this view has a decor view below it's content. This will make the intrinsic
* height from {@link #getIntrinsicHeight()} higher as well
*/
protected boolean hasBottomDecor() {
return false;
}
/**
* @return The maximum height of this notification.
*/
@@ -363,10 +327,6 @@ public abstract class ExpandableView extends FrameLayout {
outRect.top += getClipTopOptimization();
}
public int getContentHeight() {
return mActualHeight - getBottomDecorHeight();
}
public boolean isSummaryWithChildren() {
return false;
}

View File

@@ -70,13 +70,4 @@ public class NotificationOverflowContainer extends ActivatableNotificationView {
public NotificationOverflowIconsView getIconsView() {
return mIconsView;
}
protected int getContentHeightFromActualHeight(int actualHeight) {
int realActualHeight = actualHeight;
if (hasBottomDecor()) {
realActualHeight -= getBottomDecorHeight();
}
realActualHeight = Math.max(getMinHeight(), realActualHeight);
return realActualHeight;
}
}

View File

@@ -0,0 +1,209 @@
/*
* Copyright (C) 2015 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.notification;
import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.util.NotificationColorUtil;
import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import java.util.List;
/**
* A header for a notification view
*/
public class NotificationHeaderView extends FrameLayout {
private static final int DEFAULT_ICON_TINT_COLOR = 0xff616161;
private final NotificationColorUtil mNotificationColorUtil;
private NotificationData.Entry mNotificationEntry;
private ImageView mIconView;
private TextView mAppName;
private TextView mPostTime;
private TextView mChildCount;
private TextView mSubTextDivider;
private TextView mSubText;
private NotificationGroupManager mGroupManager;
private ImageButton mExpandButton;
public NotificationHeaderView(Context context) {
this(context, null);
}
public NotificationHeaderView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public NotificationHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public NotificationHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mNotificationColorUtil = NotificationColorUtil.getInstance(context);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mIconView = (ImageView) findViewById(R.id.header_notification_icon);
mAppName = (TextView) findViewById(R.id.app_name_text);
mSubTextDivider = (TextView) findViewById(R.id.app_title_sub_text_divider);
mSubText = (TextView) findViewById(R.id.title_sub_text);
mPostTime = (TextView) findViewById(R.id.post_time);
mChildCount = (TextView) findViewById(R.id.number_of_children);
mExpandButton = (ImageButton) findViewById(R.id.notification_expand_button);
mExpandButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mGroupManager.toggleGroupExpansion(mNotificationEntry.notification);
}
});
}
public void bind(NotificationData.Entry notificationEntry) {
mNotificationEntry = notificationEntry;
StatusBarNotification sbn = notificationEntry.notification;
int notificationColor = getNotificationColor(sbn);
bindIcon(notificationColor);
bindNumber(notificationColor);
bindAppName(sbn);
bindSubText();
bindTime(sbn);
bindExpandButton(sbn);
}
private void bindExpandButton(StatusBarNotification sbn) {
boolean summaryOfGroup = mGroupManager.isSummaryOfGroup(sbn);
mExpandButton.setVisibility(summaryOfGroup ? VISIBLE : GONE);
}
private void bindSubText() {
List<ExpandableNotificationRow> notificationChildren =
mNotificationEntry.row.getNotificationChildren();
CharSequence subText = null;
if (notificationChildren != null) {
for (int i = 0; i < notificationChildren.size(); i++) {
ExpandableNotificationRow row = notificationChildren.get(i);
CharSequence rowSubText = row.getSubText();
if (TextUtils.isEmpty(rowSubText)
|| (subText != null && !subText.equals(rowSubText))) {
// The children don't have a common subText
subText = null;
break;
} else if (subText == null) {
subText = rowSubText;
}
}
};
setSubText(subText);
}
private void setSubText(CharSequence subText) {
boolean goneInHeader = TextUtils.isEmpty(subText);
if (goneInHeader) {
mSubText.setVisibility(GONE);
mSubTextDivider.setVisibility(GONE);
} else {
mSubText.setVisibility(VISIBLE);
mSubText.setText(subText);
mSubTextDivider.setVisibility(VISIBLE);
}
List<ExpandableNotificationRow> notificationChildren =
mNotificationEntry.row.getNotificationChildren();
if (notificationChildren != null) {
for (int i = 0; i < notificationChildren.size(); i++) {
ExpandableNotificationRow row = notificationChildren.get(i);
row.setContentSubTextVisible(goneInHeader);
}
}
}
private int getNotificationColor(StatusBarNotification sbn) {
int color = sbn.getNotification().color;
if (color == Notification.COLOR_DEFAULT) {
return DEFAULT_ICON_TINT_COLOR;
}
return color;
}
private void bindNumber(int notificationColor) {
int numberOfNotificationChildren = mNotificationEntry.row.getNumberOfNotificationChildren();
boolean visible = numberOfNotificationChildren > 0;
if (visible) {
mChildCount.setText("(" + numberOfNotificationChildren + ")");
mChildCount.setTextColor(notificationColor);
mChildCount.setVisibility(VISIBLE);
} else {
mChildCount.setVisibility(GONE);
}
}
private void bindTime(StatusBarNotification sbn) {
}
private void bindIcon(int notificationColor) {
Drawable icon = mNotificationEntry.icon.getDrawable().getConstantState()
.newDrawable(getResources()).mutate();
mIconView.setImageDrawable(icon);
if (NotificationUtils.isGrayscale(mIconView, mNotificationColorUtil)) {
icon.setTint(notificationColor);
}
}
private void bindAppName(StatusBarNotification sbn) {
PackageManager pmUser = BaseStatusBar.getPackageManagerForUser(getContext(),
sbn.getUser().getIdentifier());
final String pkg = sbn.getPackageName();
String appname = pkg;
try {
final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
PackageManager.GET_UNINSTALLED_PACKAGES
| PackageManager.GET_DISABLED_COMPONENTS);
if (info != null) {
appname = String.valueOf(pmUser.getApplicationLabel(info));
}
} catch (PackageManager.NameNotFoundException e) {
// app is gone, just show package name
}
mAppName.setText(appname);
}
public void setGroupManager(NotificationGroupManager groupManager) {
mGroupManager = groupManager;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2015 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.notification;
import android.view.View;
import android.widget.ImageView;
import com.android.internal.util.NotificationColorUtil;
import com.android.systemui.R;
import com.android.systemui.statusbar.StatusBarIconView;
/**
* A util class for various reusable functions
*/
public class NotificationUtils {
public static boolean isGrayscale(ImageView v, NotificationColorUtil colorUtil) {
Object isGrayscale = v.getTag(R.id.icon_is_grayscale);
if (isGrayscale != null) {
return Boolean.TRUE.equals(isGrayscale);
}
boolean grayscale = colorUtil.isGrayscaleIcon(v.getDrawable());
v.setTag(R.id.icon_is_grayscale, grayscale);
return grayscale;
}
}

View File

@@ -106,7 +106,7 @@ public class NotificationGroupManager {
}
});
} else {
group.summary.row.updateExpandButton();
group.summary.row.updateNotificationHeader();
}
}
}
@@ -130,7 +130,7 @@ public class NotificationGroupManager {
} else {
group.children.add(added);
if (group.summary != null && group.children.size() == 1 && !group.expanded) {
group.summary.row.updateExpandButton();
group.summary.row.updateNotificationHeader();
}
}
}

View File

@@ -42,6 +42,7 @@ import com.android.systemui.R;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -401,23 +402,13 @@ public class StatusBarIconController implements Tunable {
for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
StatusBarIconView v = (StatusBarIconView) mNotificationIcons.getChildAt(i);
boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
boolean colorize = !isPreL || isGrayscale(v);
boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil);
if (colorize) {
v.setImageTintList(ColorStateList.valueOf(mIconTint));
}
}
}
private boolean isGrayscale(StatusBarIconView v) {
Object isGrayscale = v.getTag(R.id.icon_is_grayscale);
if (isGrayscale != null) {
return Boolean.TRUE.equals(isGrayscale);
}
boolean grayscale = mNotificationColorUtil.isGrayscaleIcon(v.getDrawable());
v.setTag(R.id.icon_is_grayscale, grayscale);
return grayscale;
}
public void appTransitionPending() {
mTransitionPending = true;
}

View File

@@ -21,7 +21,6 @@ import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
@@ -40,10 +39,9 @@ public class NotificationChildrenContainer extends ViewGroup {
private final int mMaxNotificationHeight;
private final List<View> mDividers = new ArrayList<>();
private final List<ExpandableNotificationRow> mChildren = new ArrayList<>();
private final View mCollapseButton;
private final View mCollapseDivider;
private final int mCollapseButtonHeight;
private final int mNotificationHeaderHeight;
private final int mNotificationAppearDistance;
private final float mHeaderTopPaddingSubstraction;
public NotificationChildrenContainer(Context context) {
this(context, null);
@@ -68,14 +66,9 @@ public class NotificationChildrenContainer extends ViewGroup {
R.dimen.notification_max_height);
mNotificationAppearDistance = getResources().getDimensionPixelSize(
R.dimen.notification_appear_distance);
LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class);
mCollapseButton = inflater.inflate(R.layout.notification_collapse_button, this,
false);
mCollapseButtonHeight = getResources().getDimensionPixelSize(
R.dimen.notification_bottom_decor_height);
addView(mCollapseButton);
mCollapseDivider = inflateDivider();
addView(mCollapseDivider);
mNotificationHeaderHeight = getResources().getDimensionPixelSize(
R.dimen.notification_header_height);
mHeaderTopPaddingSubstraction = 2 * getResources().getDisplayMetrics().density;
}
@Override
@@ -103,9 +96,6 @@ public class NotificationChildrenContainer extends ViewGroup {
firstChild = false;
}
}
mCollapseButton.layout(0, 0, getWidth(), mCollapseButtonHeight);
mCollapseDivider.layout(0, mCollapseButtonHeight - mDividerHeight, getWidth(),
mCollapseButtonHeight);
}
@Override
@@ -120,11 +110,7 @@ public class NotificationChildrenContainer extends ViewGroup {
}
int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
int collapseButtonHeightSpec = MeasureSpec.makeMeasureSpec(mCollapseButtonHeight,
MeasureSpec.EXACTLY);
mCollapseButton.measure(widthMeasureSpec, collapseButtonHeightSpec);
mCollapseDivider.measure(widthMeasureSpec, dividerHeightSpec);
int height = mCollapseButtonHeight;
int height = mNotificationHeaderHeight;
int childCount = mChildren.size();
boolean firstChild = true;
for (int i = 0; i < childCount; i++) {
@@ -247,7 +233,7 @@ public class NotificationChildrenContainer extends ViewGroup {
*/
public void getState(StackScrollState resultState, StackViewState parentState) {
int childCount = mChildren.size();
int yPosition = mCollapseButtonHeight;
int yPosition = mNotificationHeaderHeight;
boolean firstChild = true;
for (int i = 0; i < childCount; i++) {
ExpandableNotificationRow child = mChildren.get(i);
@@ -302,10 +288,6 @@ public class NotificationChildrenContainer extends ViewGroup {
}
}
public void setCollapseClickListener(OnClickListener collapseClickListener) {
mCollapseButton.setOnClickListener(collapseClickListener);
}
/**
* This is called when the children expansion has changed and positions the children properly
* for an appear animation.
@@ -376,13 +358,6 @@ public class NotificationChildrenContainer extends ViewGroup {
stateAnimator.startStackAnimations(child, viewState, state, -1, delay);
notGoneIndex++;
}
dividerState.initFrom(mCollapseButton);
dividerState.alpha = 1.0f;
stateAnimator.startViewAnimations(mCollapseButton, dividerState, baseDelay, duration);
dividerState.initFrom(mCollapseDivider);
dividerState.alpha = 1.0f;
dividerState.yTranslation = 0.0f;
stateAnimator.startViewAnimations(mCollapseDivider, dividerState, baseDelay, duration);
}
public ExpandableNotificationRow getViewAtPosition(float y) {

View File

@@ -2788,16 +2788,6 @@ public class NotificationStackScrollLayout extends ViewGroup
return touchY > mTopPadding + mStackTranslation;
}
private void updateExpandButtons() {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
row.updateExpandButton();
}
}
}
@Override
public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
boolean animated = mAnimationsEnabled && mIsExpanded;
@@ -2811,7 +2801,6 @@ public class NotificationStackScrollLayout extends ViewGroup
@Override
public void onGroupsProhibitedChanged() {
updateExpandButtons();
}
@Override