Round rect clipping for notification contents

Enable round rect clipping on notifications for the following kind
of notifications:
- Big picture style.
- Big media narrow style.
- Custom notifications.

Bug: 16142505
Change-Id: I157650fe470636ed624a81557c08135827eac0cb
This commit is contained in:
Jorim Jaggi
2015-05-20 20:04:08 -07:00
parent a65f1d1c4c
commit be4116a347
14 changed files with 167 additions and 9 deletions

View File

@@ -19,6 +19,7 @@
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="64dp"
android:tag="base"
>
<include layout="@layout/notification_template_icon_group"
android:layout_width="@dimen/notification_large_icon_width"

View File

@@ -19,6 +19,7 @@
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tag="big"
>
<include layout="@layout/notification_template_icon_group"
android:layout_width="@dimen/notification_large_icon_width"

View File

@@ -20,6 +20,7 @@
android:layout_width="match_parent"
android:layout_height="128dp"
android:background="#00000000"
android:tag="bigMedia"
>
<include layout="@layout/notification_template_icon_group"
android:layout_width="@dimen/notification_large_icon_width"

View File

@@ -21,6 +21,7 @@
android:layout_width="match_parent"
android:layout_height="128dp"
android:background="#00000000"
android:tag="bigMediaNarrow"
>
<ImageView android:id="@+id/icon"
android:layout_width="128dp"

View File

@@ -19,6 +19,7 @@
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:tag="bigPicture"
>
<ImageView
android:id="@+id/big_picture"

View File

@@ -19,6 +19,7 @@
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tag="bigText"
>
<include layout="@layout/notification_template_icon_group"
android:layout_width="@dimen/notification_large_icon_width"

View File

@@ -19,6 +19,7 @@
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tag="inbox"
>
<include layout="@layout/notification_template_icon_group"
android:layout_width="@dimen/notification_large_icon_width"

View File

@@ -21,6 +21,7 @@
android:layout_height="64dp"
android:orientation="horizontal"
android:background="#00000000"
android:tag="media"
>
<include layout="@layout/notification_template_icon_group"
android:layout_width="@dimen/notification_large_icon_width"

View File

@@ -42,6 +42,10 @@
<!-- Whether to use cheap, less good looking shadows for recents -->
<bool name="config_recents_fake_shadows">false</bool>
<!-- Whether to clip notification contents with a rounded rectangle. Might be expensive on
certain GPU's and thus can be turned off with only minimal visual impact. -->
<bool name="config_notifications_round_rect_clipping">true</bool>
<!-- The theme to use for RecentsActivity. -->
<item type="style" name="config_recents_activity_theme">@style/RecentsTheme.Wallpaper</item>

View File

@@ -0,0 +1,36 @@
/*
* 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;
import android.content.Context;
import android.view.View;
/**
* Wraps a big media narrow notification template layout.
*/
public class NotificationBigMediaNarrowViewWrapper extends NotificationMediaViewWrapper {
protected NotificationBigMediaNarrowViewWrapper(Context ctx,
View view) {
super(ctx, view);
}
@Override
public boolean needsRoundRectClipping() {
return true;
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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;
import android.content.Context;
import android.view.View;
/**
* Wraps a notification view inflated from a big picture style template.
*/
public class NotificationBigPictureViewWrapper extends NotificationTemplateViewWrapper {
protected NotificationBigPictureViewWrapper(Context ctx, View view) {
super(ctx, view);
}
@Override
public boolean needsRoundRectClipping() {
return true;
}
}

View File

@@ -17,6 +17,7 @@
package com.android.systemui.statusbar;
import android.content.Context;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
@@ -24,6 +25,7 @@ import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
@@ -46,21 +48,26 @@ public class NotificationContentView extends FrameLayout {
private final Rect mClipBounds = new Rect();
private final int mSmallHeight;
private final int mHeadsUpHeight;
private final int mRoundRectRadius;
private final Interpolator mLinearInterpolator = new LinearInterpolator();
private final boolean mRoundRectClippingEnabled;
private View mContractedChild;
private View mExpandedChild;
private View mHeadsUpChild;
private NotificationViewWrapper mContractedWrapper;
private NotificationViewWrapper mExpandedWrapper;
private NotificationViewWrapper mHeadsUpWrapper;
private int mClipTopAmount;
private int mContentHeight;
private int mUnrestrictedContentHeight;
private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
private boolean mDark;
private final Paint mFadePaint = new Paint();
private boolean mAnimate;
private boolean mIsHeadsUp;
private ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
= new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
@@ -70,12 +77,25 @@ public class NotificationContentView extends FrameLayout {
}
};
private final ViewOutlineProvider mOutlineProvider = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, 0, view.getWidth(), mUnrestrictedContentHeight,
mRoundRectRadius);
}
};
public NotificationContentView(Context context, AttributeSet attrs) {
super(context, attrs);
mFadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
mSmallHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
mHeadsUpHeight = getResources().getDimensionPixelSize(R.dimen.notification_mid_height);
mRoundRectRadius = getResources().getDimensionPixelSize(
R.dimen.notification_material_rounded_rect_radius);
mRoundRectClippingEnabled = getResources().getBoolean(
R.bool.config_notifications_round_rect_clipping);
reset(true);
setOutlineProvider(mOutlineProvider);
}
@Override
@@ -127,6 +147,7 @@ public class NotificationContentView extends FrameLayout {
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
updateClipping();
invalidateOutline();
}
@Override
@@ -177,6 +198,7 @@ public class NotificationContentView extends FrameLayout {
mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child);
selectLayout(false /* animate */, true /* force */);
mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
updateRoundRectClipping();
}
public void setExpandedChild(View child) {
@@ -186,7 +208,9 @@ public class NotificationContentView extends FrameLayout {
}
addView(child);
mExpandedChild = child;
mExpandedWrapper = NotificationViewWrapper.wrap(getContext(), child);
selectLayout(false /* animate */, true /* force */);
updateRoundRectClipping();
}
public void setHeadsUpChild(View child) {
@@ -196,7 +220,9 @@ public class NotificationContentView extends FrameLayout {
}
addView(child);
mHeadsUpChild = child;
mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child);
selectLayout(false /* animate */, true /* force */);
updateRoundRectClipping();
}
@Override
@@ -222,10 +248,11 @@ public class NotificationContentView extends FrameLayout {
}
public void setContentHeight(int contentHeight) {
contentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight());
mContentHeight = contentHeight;
mContentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight());;
mUnrestrictedContentHeight = Math.max(contentHeight, getMinHeight());
selectLayout(mAnimate /* animate */, false /* force */);
updateClipping();
invalidateOutline();
}
public int getContentHeight() {
@@ -250,6 +277,27 @@ public class NotificationContentView extends FrameLayout {
updateClipping();
}
private void updateRoundRectClipping() {
boolean enabled = needsRoundRectClipping();
setClipToOutline(enabled);
}
private boolean needsRoundRectClipping() {
if (!mRoundRectClippingEnabled) {
return false;
}
boolean needsForContracted = mContractedChild != null
&& mContractedChild.getVisibility() == View.VISIBLE
&& mContractedWrapper.needsRoundRectClipping();
boolean needsForExpanded = mExpandedChild != null
&& mExpandedChild.getVisibility() == View.VISIBLE
&& mExpandedWrapper.needsRoundRectClipping();
boolean needsForHeadsUp = mExpandedChild != null
&& mExpandedChild.getVisibility() == View.VISIBLE
&& mExpandedWrapper.needsRoundRectClipping();
return needsForContracted || needsForExpanded || needsForHeadsUp;
}
private void updateClipping() {
mClipBounds.set(0, mClipTopAmount, getWidth(), mContentHeight);
setClipBounds(mClipBounds);
@@ -290,6 +338,7 @@ public class NotificationContentView extends FrameLayout {
mHeadsUpChild.setLayerType(LAYER_TYPE_NONE, null);
}
setLayerType(LAYER_TYPE_NONE, null);
updateRoundRectClipping();
}
private void runSwitchAnimation(int visibleType) {
@@ -315,6 +364,7 @@ public class NotificationContentView extends FrameLayout {
updateViewVisibilities(mVisibleType);
}
});
updateRoundRectClipping();
}
/**
@@ -358,6 +408,10 @@ public class NotificationContentView extends FrameLayout {
mContractedWrapper.notifyContentUpdated();
mContractedWrapper.setDark(mDark, false /* animate */, 0 /* delay */);
}
if (mExpandedChild != null) {
mExpandedWrapper.notifyContentUpdated();
}
updateRoundRectClipping();
}
public boolean isContentExpandable() {

View File

@@ -41,4 +41,9 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper {
mInvertHelper.update(dark);
}
}
@Override
public boolean needsRoundRectClipping() {
return true;
}
}

View File

@@ -25,15 +25,23 @@ import android.view.View;
*/
public abstract class NotificationViewWrapper {
private static final String TAG_BIG_MEDIA_NARROW = "bigMediaNarrow";
private static final String TAG_MEDIA = "media";
private static final String TAG_BIG_PICTURE = "bigPicture";
protected final View mView;
public static NotificationViewWrapper wrap(Context ctx, View v) {
// TODO: Figure out a better way to find out which template the view is.
if (v.findViewById(com.android.internal.R.id.media_actions) != null) {
return new NotificationMediaViewWrapper(ctx, v);
} else if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
return new NotificationTemplateViewWrapper(ctx, v);
if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
if (TAG_BIG_MEDIA_NARROW.equals(v.getTag())) {
return new NotificationBigMediaNarrowViewWrapper(ctx, v);
} else if (TAG_MEDIA.equals(v.getTag())) {
return new NotificationMediaViewWrapper(ctx, v);
} else if (TAG_BIG_PICTURE.equals(v.getTag())) {
return new NotificationBigMediaNarrowViewWrapper(ctx, v);
} else {
return new NotificationTemplateViewWrapper(ctx, v);
}
} else {
return new NotificationCustomViewWrapper(v);
}
@@ -56,4 +64,12 @@ public abstract class NotificationViewWrapper {
* Notifies this wrapper that the content of the view might have changed.
*/
public void notifyContentUpdated() {}
/**
* @return true if this template might need to be clipped with a round rect to make it look
* nice, false otherwise
*/
public boolean needsRoundRectClipping() {
return false;
}
}