Merge "New MediaStyle template:" into sc-dev
This commit is contained in:
@@ -20,8 +20,6 @@ import static android.annotation.Dimension.DP;
|
||||
import static android.graphics.drawable.Icon.TYPE_URI;
|
||||
import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP;
|
||||
|
||||
import static com.android.internal.util.ContrastColorUtil.satisfiesTextContrast;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import android.annotation.AttrRes;
|
||||
@@ -3701,8 +3699,6 @@ public class Notification implements Parcelable
|
||||
private int mTextColorsAreForBackground = COLOR_INVALID;
|
||||
private int mPrimaryTextColor = COLOR_INVALID;
|
||||
private int mSecondaryTextColor = COLOR_INVALID;
|
||||
private int mBackgroundColor = COLOR_INVALID;
|
||||
private int mForegroundColor = COLOR_INVALID;
|
||||
private boolean mRebuildStyledRemoteViews;
|
||||
|
||||
private boolean mTintActionButtons;
|
||||
@@ -5041,7 +5037,8 @@ public class Notification implements Parcelable
|
||||
private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p,
|
||||
TemplateBindResult result) {
|
||||
p.headerless(resId == getBaseLayoutResource()
|
||||
|| resId == getHeadsUpBaseLayoutResource());
|
||||
|| resId == getHeadsUpBaseLayoutResource()
|
||||
|| resId == R.layout.notification_template_material_media);
|
||||
RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
|
||||
|
||||
resetStandardTemplate(contentView);
|
||||
@@ -5092,7 +5089,7 @@ public class Notification implements Parcelable
|
||||
}
|
||||
|
||||
private CharSequence processTextSpans(CharSequence text) {
|
||||
if (hasForegroundColor() || mInNightMode) {
|
||||
if (mInNightMode) {
|
||||
return ContrastColorUtil.clearColorSpans(text);
|
||||
}
|
||||
return text;
|
||||
@@ -5103,10 +5100,6 @@ public class Notification implements Parcelable
|
||||
contentView.setTextColor(id, getPrimaryTextColor(p));
|
||||
}
|
||||
|
||||
private boolean hasForegroundColor() {
|
||||
return mForegroundColor != COLOR_INVALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param p the template params to inflate this with
|
||||
* @return the primary text color
|
||||
@@ -5140,75 +5133,15 @@ public class Notification implements Parcelable
|
||||
|| mSecondaryTextColor == COLOR_INVALID
|
||||
|| mTextColorsAreForBackground != backgroundColor) {
|
||||
mTextColorsAreForBackground = backgroundColor;
|
||||
if (!hasForegroundColor() || !isColorized(p)) {
|
||||
mPrimaryTextColor = ContrastColorUtil.resolvePrimaryColor(mContext,
|
||||
backgroundColor, mInNightMode);
|
||||
mSecondaryTextColor = ContrastColorUtil.resolveSecondaryColor(mContext,
|
||||
backgroundColor, mInNightMode);
|
||||
if (backgroundColor != COLOR_DEFAULT && isColorized(p)) {
|
||||
mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
|
||||
mPrimaryTextColor, backgroundColor, 4.5);
|
||||
mSecondaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
|
||||
mSecondaryTextColor, backgroundColor, 4.5);
|
||||
}
|
||||
} else {
|
||||
double backLum = ContrastColorUtil.calculateLuminance(backgroundColor);
|
||||
double textLum = ContrastColorUtil.calculateLuminance(mForegroundColor);
|
||||
double contrast = ContrastColorUtil.calculateContrast(mForegroundColor,
|
||||
backgroundColor);
|
||||
// We only respect the given colors if worst case Black or White still has
|
||||
// contrast
|
||||
boolean backgroundLight = backLum > textLum
|
||||
&& satisfiesTextContrast(backgroundColor, Color.BLACK)
|
||||
|| backLum <= textLum
|
||||
&& !satisfiesTextContrast(backgroundColor, Color.WHITE);
|
||||
if (contrast < 4.5f) {
|
||||
if (backgroundLight) {
|
||||
mSecondaryTextColor = ContrastColorUtil.findContrastColor(
|
||||
mForegroundColor,
|
||||
backgroundColor,
|
||||
true /* findFG */,
|
||||
4.5f);
|
||||
mPrimaryTextColor = ContrastColorUtil.changeColorLightness(
|
||||
mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_LIGHT);
|
||||
} else {
|
||||
mSecondaryTextColor =
|
||||
ContrastColorUtil.findContrastColorAgainstDark(
|
||||
mForegroundColor,
|
||||
backgroundColor,
|
||||
true /* findFG */,
|
||||
4.5f);
|
||||
mPrimaryTextColor = ContrastColorUtil.changeColorLightness(
|
||||
mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_DARK);
|
||||
}
|
||||
} else {
|
||||
mPrimaryTextColor = mForegroundColor;
|
||||
mSecondaryTextColor = ContrastColorUtil.changeColorLightness(
|
||||
mPrimaryTextColor, backgroundLight ? LIGHTNESS_TEXT_DIFFERENCE_LIGHT
|
||||
: LIGHTNESS_TEXT_DIFFERENCE_DARK);
|
||||
if (ContrastColorUtil.calculateContrast(mSecondaryTextColor,
|
||||
backgroundColor) < 4.5f) {
|
||||
// oh well the secondary is not good enough
|
||||
if (backgroundLight) {
|
||||
mSecondaryTextColor = ContrastColorUtil.findContrastColor(
|
||||
mSecondaryTextColor,
|
||||
backgroundColor,
|
||||
true /* findFG */,
|
||||
4.5f);
|
||||
} else {
|
||||
mSecondaryTextColor
|
||||
= ContrastColorUtil.findContrastColorAgainstDark(
|
||||
mSecondaryTextColor,
|
||||
backgroundColor,
|
||||
true /* findFG */,
|
||||
4.5f);
|
||||
}
|
||||
mPrimaryTextColor = ContrastColorUtil.changeColorLightness(
|
||||
mSecondaryTextColor, backgroundLight
|
||||
? -LIGHTNESS_TEXT_DIFFERENCE_LIGHT
|
||||
: -LIGHTNESS_TEXT_DIFFERENCE_DARK);
|
||||
}
|
||||
}
|
||||
mPrimaryTextColor = ContrastColorUtil.resolvePrimaryColor(mContext,
|
||||
backgroundColor, mInNightMode);
|
||||
mSecondaryTextColor = ContrastColorUtil.resolveSecondaryColor(mContext,
|
||||
backgroundColor, mInNightMode);
|
||||
if (backgroundColor != COLOR_DEFAULT && isColorized(p)) {
|
||||
mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
|
||||
mPrimaryTextColor, backgroundColor, 4.5);
|
||||
mSecondaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
|
||||
mSecondaryTextColor, backgroundColor, 4.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5254,11 +5187,7 @@ public class Notification implements Parcelable
|
||||
result = new TemplateBindResult();
|
||||
}
|
||||
bindLargeIcon(contentView, p, result);
|
||||
if (p.mHeaderless) {
|
||||
// views in the headerless (collapsed) state
|
||||
result.mHeadingExtraMarginSet.applyToView(contentView,
|
||||
R.id.notification_headerless_view_column);
|
||||
} else {
|
||||
if (!p.mHeaderless) {
|
||||
// views in states with a header (big states)
|
||||
result.mHeadingExtraMarginSet.applyToView(contentView, R.id.notification_header);
|
||||
result.mTitleMarginSet.applyToView(contentView, R.id.title);
|
||||
@@ -5278,6 +5207,8 @@ public class Notification implements Parcelable
|
||||
@NonNull TemplateBindResult result) {
|
||||
final Resources resources = mContext.getResources();
|
||||
final float density = resources.getDisplayMetrics().density;
|
||||
final float iconMarginDp = resources.getDimension(
|
||||
R.dimen.notification_right_icon_content_margin) / density;
|
||||
final float contentMarginDp = resources.getDimension(
|
||||
R.dimen.notification_content_margin_end) / density;
|
||||
final float expanderSizeDp = resources.getDimension(
|
||||
@@ -5299,7 +5230,7 @@ public class Notification implements Parcelable
|
||||
}
|
||||
}
|
||||
}
|
||||
final float extraMarginEndDpIfVisible = viewWidthDp + contentMarginDp;
|
||||
final float extraMarginEndDpIfVisible = viewWidthDp + iconMarginDp;
|
||||
result.setRightIconState(largeIconShown, viewWidthDp,
|
||||
extraMarginEndDpIfVisible, expanderSizeDp);
|
||||
}
|
||||
@@ -5363,7 +5294,7 @@ public class Notification implements Parcelable
|
||||
|
||||
private void bindHeaderChronometerAndTime(RemoteViews contentView,
|
||||
StandardTemplateParams p, boolean hasTextToLeft) {
|
||||
if (showsTimeOrChronometer()) {
|
||||
if (!p.mHideTime && showsTimeOrChronometer()) {
|
||||
if (hasTextToLeft) {
|
||||
contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
|
||||
setTextViewColorSecondary(contentView, R.id.time_divider, p);
|
||||
@@ -5394,6 +5325,9 @@ public class Notification implements Parcelable
|
||||
*/
|
||||
private boolean bindHeaderText(RemoteViews contentView, StandardTemplateParams p,
|
||||
boolean hasTextToLeft) {
|
||||
if (p.mHideSubText) {
|
||||
return false;
|
||||
}
|
||||
CharSequence summaryText = p.summaryText;
|
||||
if (summaryText == null && mStyle != null && mStyle.mSummaryTextSet
|
||||
&& mStyle.hasSummaryInHeader()) {
|
||||
@@ -5424,6 +5358,9 @@ public class Notification implements Parcelable
|
||||
*/
|
||||
private boolean bindHeaderTextSecondary(RemoteViews contentView, StandardTemplateParams p,
|
||||
boolean hasTextToLeft) {
|
||||
if (p.mHideSubText) {
|
||||
return false;
|
||||
}
|
||||
if (!TextUtils.isEmpty(p.headerTextSecondary)) {
|
||||
contentView.setTextViewText(R.id.header_text_secondary, processTextSpans(
|
||||
processLegacyText(p.headerTextSecondary)));
|
||||
@@ -6654,7 +6591,7 @@ public class Notification implements Parcelable
|
||||
*/
|
||||
private @ColorInt int getUnresolvedBackgroundColor(StandardTemplateParams p) {
|
||||
if (isColorized(p)) {
|
||||
return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : getRawColor(p);
|
||||
return getRawColor(p);
|
||||
} else {
|
||||
return COLOR_DEFAULT;
|
||||
}
|
||||
@@ -6681,21 +6618,6 @@ public class Notification implements Parcelable
|
||||
&& targetSdkVersion < Build.VERSION_CODES.O;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a color palette to be used as the background and textColors
|
||||
*
|
||||
* @param backgroundColor the color to be used as the background
|
||||
* @param foregroundColor the color to be used as the foreground
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void setColorPalette(@ColorInt int backgroundColor, @ColorInt int foregroundColor) {
|
||||
mBackgroundColor = backgroundColor;
|
||||
mForegroundColor = foregroundColor;
|
||||
mTextColorsAreForBackground = COLOR_INVALID;
|
||||
ensureColors(mParams.reset().fillTextsFrom(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces all styled remoteViews to be built from scratch and not use any cached
|
||||
* RemoteViews.
|
||||
@@ -6752,24 +6674,14 @@ public class Notification implements Parcelable
|
||||
if (mLargeIcon != null || largeIcon != null) {
|
||||
Resources resources = context.getResources();
|
||||
Class<? extends Style> style = getNotificationStyle();
|
||||
int maxWidth = resources.getDimensionPixelSize(isLowRam
|
||||
int maxSize = resources.getDimensionPixelSize(isLowRam
|
||||
? R.dimen.notification_right_icon_size_low_ram
|
||||
: R.dimen.notification_right_icon_size);
|
||||
int maxHeight = maxWidth;
|
||||
if (MediaStyle.class.equals(style)
|
||||
|| DecoratedMediaCustomViewStyle.class.equals(style)) {
|
||||
maxHeight = resources.getDimensionPixelSize(isLowRam
|
||||
? R.dimen.notification_media_image_max_height_low_ram
|
||||
: R.dimen.notification_media_image_max_height);
|
||||
maxWidth = resources.getDimensionPixelSize(isLowRam
|
||||
? R.dimen.notification_media_image_max_width_low_ram
|
||||
: R.dimen.notification_media_image_max_width);
|
||||
}
|
||||
if (mLargeIcon != null) {
|
||||
mLargeIcon.scaleDownIfNecessary(maxWidth, maxHeight);
|
||||
mLargeIcon.scaleDownIfNecessary(maxSize, maxSize);
|
||||
}
|
||||
if (largeIcon != null) {
|
||||
largeIcon = Icon.scaleDownIfNecessary(largeIcon, maxWidth, maxHeight);
|
||||
largeIcon = Icon.scaleDownIfNecessary(largeIcon, maxSize, maxSize);
|
||||
}
|
||||
}
|
||||
reduceImageSizesForRemoteView(contentView, context, isLowRam);
|
||||
@@ -6856,9 +6768,6 @@ public class Notification implements Parcelable
|
||||
* @hide
|
||||
*/
|
||||
public boolean isColorized() {
|
||||
if (isColorizedMedia()) {
|
||||
return true;
|
||||
}
|
||||
return extras.getBoolean(EXTRA_COLORIZED)
|
||||
&& (hasColorizedPermission() || isForegroundService());
|
||||
}
|
||||
@@ -6871,27 +6780,6 @@ public class Notification implements Parcelable
|
||||
return (flags & Notification.FLAG_CAN_COLORIZE) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this notification is colorized and it is a media notification
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public boolean isColorizedMedia() {
|
||||
Class<? extends Style> style = getNotificationStyle();
|
||||
if (MediaStyle.class.equals(style)) {
|
||||
Boolean colorized = (Boolean) extras.get(EXTRA_COLORIZED);
|
||||
if ((colorized == null || colorized) && hasMediaSession()) {
|
||||
return true;
|
||||
}
|
||||
} else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
|
||||
if (extras.getBoolean(EXTRA_COLORIZED) && hasMediaSession()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return true if this is a media notification
|
||||
*
|
||||
@@ -7178,15 +7066,6 @@ public class Notification implements Parcelable
|
||||
return mBuilder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @return true if the style positions the progress bar on the second line; false if the
|
||||
* style hides the progress bar
|
||||
*/
|
||||
protected boolean hasProgress() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @return Whether we should put the summary be put into the notification header
|
||||
@@ -9041,7 +8920,7 @@ public class Notification implements Parcelable
|
||||
*/
|
||||
@Override
|
||||
public RemoteViews makeContentView(boolean increasedHeight) {
|
||||
return makeMediaContentView();
|
||||
return makeMediaContentView(null /* customContent */);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9049,7 +8928,7 @@ public class Notification implements Parcelable
|
||||
*/
|
||||
@Override
|
||||
public RemoteViews makeBigContentView() {
|
||||
return makeMediaBigContentView();
|
||||
return makeMediaBigContentView(null /* customContent */);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9057,7 +8936,7 @@ public class Notification implements Parcelable
|
||||
*/
|
||||
@Override
|
||||
public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
|
||||
return makeMediaContentView();
|
||||
return makeMediaContentView(null /* customContent */);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@@ -9127,88 +9006,72 @@ public class Notification implements Parcelable
|
||||
container.setContentDescription(buttonId, action.title);
|
||||
}
|
||||
|
||||
private RemoteViews makeMediaContentView() {
|
||||
StandardTemplateParams p = mBuilder.mParams.reset()
|
||||
.viewType(StandardTemplateParams.VIEW_TYPE_NORMAL)
|
||||
.hideProgress(true)
|
||||
.fillTextsFrom(mBuilder);
|
||||
RemoteViews view = mBuilder.applyStandardTemplate(
|
||||
R.layout.notification_template_material_media, p,
|
||||
null /* result */);
|
||||
|
||||
/** @hide */
|
||||
protected RemoteViews makeMediaContentView(@Nullable RemoteViews customContent) {
|
||||
final int numActions = mBuilder.mActions.size();
|
||||
final int numActionsToShow = mActionsToShowInCompact == null
|
||||
? 0
|
||||
: Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
|
||||
final int numActionsToShow = Math.min(mActionsToShowInCompact == null
|
||||
? 0 : mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
|
||||
if (numActionsToShow > numActions) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"setShowActionsInCompactView: action %d out of bounds (max %d)",
|
||||
numActions, numActions - 1));
|
||||
}
|
||||
|
||||
StandardTemplateParams p = mBuilder.mParams.reset()
|
||||
.viewType(StandardTemplateParams.VIEW_TYPE_NORMAL)
|
||||
.hideTime(numActionsToShow > 1) // hide if actions wider than a large icon
|
||||
.hideSubText(numActionsToShow > 1) // hide if actions wider than a large icon
|
||||
.hideLargeIcon(numActionsToShow > 0) // large icon or actions; not both
|
||||
.hideProgress(true)
|
||||
.fillTextsFrom(mBuilder);
|
||||
TemplateBindResult result = new TemplateBindResult();
|
||||
RemoteViews template = mBuilder.applyStandardTemplate(
|
||||
R.layout.notification_template_material_media, p,
|
||||
null /* result */);
|
||||
|
||||
for (int i = 0; i < MAX_MEDIA_BUTTONS_IN_COMPACT; i++) {
|
||||
if (i < numActionsToShow) {
|
||||
final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
|
||||
bindMediaActionButton(view, MEDIA_BUTTON_IDS[i], action, p);
|
||||
bindMediaActionButton(template, MEDIA_BUTTON_IDS[i], action, p);
|
||||
} else {
|
||||
view.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
|
||||
template.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
|
||||
}
|
||||
}
|
||||
handleImage(view);
|
||||
// handle the content margin
|
||||
int endMargin = R.dimen.notification_content_margin_end;
|
||||
if (mBuilder.mN.hasLargeIcon()) {
|
||||
endMargin = R.dimen.notification_media_image_margin_end;
|
||||
}
|
||||
view.setViewLayoutMarginDimen(R.id.notification_main_column,
|
||||
RemoteViews.MARGIN_END, endMargin);
|
||||
return view;
|
||||
// Prevent a swooping expand animation when there are no actions
|
||||
boolean hasActions = numActionsToShow != 0;
|
||||
template.setViewVisibility(R.id.media_actions, hasActions ? View.VISIBLE : View.GONE);
|
||||
|
||||
// Add custom view if provided by subclass.
|
||||
buildCustomContentIntoTemplate(mBuilder.mContext, template, customContent, p, result,
|
||||
DevFlags.DECORATION_PARTIAL);
|
||||
return template;
|
||||
}
|
||||
|
||||
private RemoteViews makeMediaBigContentView() {
|
||||
/** @hide */
|
||||
protected RemoteViews makeMediaBigContentView(@Nullable RemoteViews customContent) {
|
||||
final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
|
||||
// Dont add an expanded view if there is no more content to be revealed
|
||||
int actionsInCompact = mActionsToShowInCompact == null
|
||||
? 0
|
||||
: Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
|
||||
if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
|
||||
return null;
|
||||
}
|
||||
StandardTemplateParams p = mBuilder.mParams.reset()
|
||||
.viewType(StandardTemplateParams.VIEW_TYPE_BIG)
|
||||
.hideProgress(true)
|
||||
.fillTextsFrom(mBuilder);
|
||||
RemoteViews big = mBuilder.applyStandardTemplate(
|
||||
R.layout.notification_template_material_big_media, p , null /* result */);
|
||||
TemplateBindResult result = new TemplateBindResult();
|
||||
RemoteViews template = mBuilder.applyStandardTemplate(
|
||||
R.layout.notification_template_material_big_media, p , result);
|
||||
|
||||
for (int i = 0; i < MAX_MEDIA_BUTTONS; i++) {
|
||||
if (i < actionCount) {
|
||||
bindMediaActionButton(big, MEDIA_BUTTON_IDS[i], mBuilder.mActions.get(i), p);
|
||||
bindMediaActionButton(template,
|
||||
MEDIA_BUTTON_IDS[i], mBuilder.mActions.get(i), p);
|
||||
} else {
|
||||
big.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
|
||||
template.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
|
||||
}
|
||||
}
|
||||
handleImage(big);
|
||||
return big;
|
||||
}
|
||||
|
||||
private void handleImage(RemoteViews contentView) {
|
||||
if (mBuilder.mN.hasLargeIcon()) {
|
||||
contentView.setViewLayoutMarginDimen(R.id.title, RemoteViews.MARGIN_END, 0);
|
||||
contentView.setViewLayoutMarginDimen(R.id.text, RemoteViews.MARGIN_END, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
protected boolean hasProgress() {
|
||||
return false;
|
||||
buildCustomContentIntoTemplate(mBuilder.mContext, template, customContent, p, result,
|
||||
DevFlags.DECORATION_PARTIAL);
|
||||
return template;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Helper class for generating large-format notifications that include a large image attachment.
|
||||
*
|
||||
@@ -9896,9 +9759,7 @@ public class Notification implements Parcelable
|
||||
*/
|
||||
@Override
|
||||
public RemoteViews makeContentView(boolean increasedHeight) {
|
||||
RemoteViews remoteViews = super.makeContentView(false /* increasedHeight */);
|
||||
return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
|
||||
mBuilder.mN.contentView);
|
||||
return makeMediaContentView(mBuilder.mN.contentView);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9906,24 +9767,10 @@ public class Notification implements Parcelable
|
||||
*/
|
||||
@Override
|
||||
public RemoteViews makeBigContentView() {
|
||||
RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
|
||||
RemoteViews customContent = mBuilder.mN.bigContentView != null
|
||||
? mBuilder.mN.bigContentView
|
||||
: mBuilder.mN.contentView;
|
||||
return makeBigContentViewWithCustomContent(customRemoteView);
|
||||
}
|
||||
|
||||
private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
|
||||
RemoteViews remoteViews = super.makeBigContentView();
|
||||
if (remoteViews != null) {
|
||||
return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
|
||||
customRemoteView);
|
||||
} else if (customRemoteView != mBuilder.mN.contentView){
|
||||
remoteViews = super.makeContentView(false /* increasedHeight */);
|
||||
return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
|
||||
customRemoteView);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return makeMediaBigContentView(customContent);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9931,10 +9778,10 @@ public class Notification implements Parcelable
|
||||
*/
|
||||
@Override
|
||||
public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
|
||||
RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
|
||||
RemoteViews customContent = mBuilder.mN.headsUpContentView != null
|
||||
? mBuilder.mN.headsUpContentView
|
||||
: mBuilder.mN.contentView;
|
||||
return makeBigContentViewWithCustomContent(customRemoteView);
|
||||
return makeMediaBigContentView(customContent);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9949,18 +9796,21 @@ public class Notification implements Parcelable
|
||||
return false;
|
||||
}
|
||||
|
||||
private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
|
||||
RemoteViews customContent) {
|
||||
private RemoteViews buildIntoRemoteView(RemoteViews template, RemoteViews customContent,
|
||||
boolean headerless) {
|
||||
if (customContent != null) {
|
||||
// Need to clone customContent before adding, because otherwise it can no longer be
|
||||
// parceled independently of remoteViews.
|
||||
customContent = customContent.clone();
|
||||
customContent.overrideTextColors(mBuilder.getPrimaryTextColor(mBuilder.mParams));
|
||||
remoteViews.removeAllViews(id);
|
||||
remoteViews.addView(id, customContent);
|
||||
remoteViews.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED);
|
||||
if (headerless) {
|
||||
template.removeFromParent(R.id.notification_top_line);
|
||||
}
|
||||
template.removeAllViews(R.id.notification_main_column);
|
||||
template.addView(R.id.notification_main_column, customContent);
|
||||
template.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED);
|
||||
}
|
||||
return remoteViews;
|
||||
return template;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12253,6 +12103,8 @@ public class Notification implements Parcelable
|
||||
boolean mHeaderless;
|
||||
boolean mHideAppName;
|
||||
boolean mHideTitle;
|
||||
boolean mHideSubText;
|
||||
boolean mHideTime;
|
||||
boolean mHideActions;
|
||||
boolean mHideProgress;
|
||||
boolean mHideSnoozeButton;
|
||||
@@ -12275,6 +12127,8 @@ public class Notification implements Parcelable
|
||||
mHeaderless = false;
|
||||
mHideAppName = false;
|
||||
mHideTitle = false;
|
||||
mHideSubText = false;
|
||||
mHideTime = false;
|
||||
mHideActions = false;
|
||||
mHideProgress = false;
|
||||
mHideSnoozeButton = false;
|
||||
@@ -12288,6 +12142,7 @@ public class Notification implements Parcelable
|
||||
summaryText = null;
|
||||
headerTextSecondary = null;
|
||||
maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
|
||||
hideLargeIcon = false;
|
||||
allowColorization = true;
|
||||
mReduceHighlights = false;
|
||||
return this;
|
||||
@@ -12312,6 +12167,16 @@ public class Notification implements Parcelable
|
||||
return this;
|
||||
}
|
||||
|
||||
public StandardTemplateParams hideSubText(boolean hideSubText) {
|
||||
mHideSubText = hideSubText;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StandardTemplateParams hideTime(boolean hideTime) {
|
||||
mHideTime = hideTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
final StandardTemplateParams hideActions(boolean hideActions) {
|
||||
this.mHideActions = hideActions;
|
||||
return this;
|
||||
|
||||
@@ -26,6 +26,9 @@ import android.widget.RemoteViews;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The top line of content in a notification view.
|
||||
* This includes the text views and badges but excludes the icon and the expander.
|
||||
@@ -34,17 +37,23 @@ import com.android.internal.R;
|
||||
*/
|
||||
@RemoteViews.RemoteView
|
||||
public class NotificationTopLineView extends ViewGroup {
|
||||
private final OverflowAdjuster mOverflowAdjuster = new OverflowAdjuster();
|
||||
private final int mGravityY;
|
||||
private final int mChildMinWidth;
|
||||
private final int mChildHideWidth;
|
||||
@Nullable private View mAppName;
|
||||
@Nullable private View mTitle;
|
||||
private View mHeaderText;
|
||||
private View mHeaderTextDivider;
|
||||
private View mSecondaryHeaderText;
|
||||
private View mSecondaryHeaderTextDivider;
|
||||
private OnClickListener mFeedbackListener;
|
||||
private HeaderTouchListener mTouchListener = new HeaderTouchListener();
|
||||
private View mFeedbackIcon;
|
||||
private int mHeaderTextMarginEnd;
|
||||
|
||||
private Set<View> mViewsToDisappear = new HashSet<>();
|
||||
|
||||
private int mMaxAscent;
|
||||
private int mMaxDescent;
|
||||
|
||||
@@ -66,6 +75,7 @@ public class NotificationTopLineView extends ViewGroup {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
Resources res = getResources();
|
||||
mChildMinWidth = res.getDimensionPixelSize(R.dimen.notification_header_shrink_min_width);
|
||||
mChildHideWidth = res.getDimensionPixelSize(R.dimen.notification_header_shrink_hide_width);
|
||||
|
||||
// NOTE: Implementation only supports TOP, BOTTOM, and CENTER_VERTICAL gravities,
|
||||
// with CENTER_VERTICAL being the default.
|
||||
@@ -88,7 +98,9 @@ public class NotificationTopLineView extends ViewGroup {
|
||||
mAppName = findViewById(R.id.app_name_text);
|
||||
mTitle = findViewById(R.id.title);
|
||||
mHeaderText = findViewById(R.id.header_text);
|
||||
mHeaderTextDivider = findViewById(R.id.header_text_divider);
|
||||
mSecondaryHeaderText = findViewById(R.id.header_text_secondary);
|
||||
mSecondaryHeaderTextDivider = findViewById(R.id.header_text_secondary_divider);
|
||||
mFeedbackIcon = findViewById(R.id.feedback);
|
||||
}
|
||||
|
||||
@@ -125,48 +137,37 @@ public class NotificationTopLineView extends ViewGroup {
|
||||
maxChildHeight = Math.max(maxChildHeight, childHeight);
|
||||
}
|
||||
|
||||
mViewsToDisappear.clear();
|
||||
// Ensure that there is at least enough space for the icons
|
||||
int endMargin = Math.max(mHeaderTextMarginEnd, getPaddingEnd());
|
||||
if (totalWidth > givenWidth - endMargin) {
|
||||
int overFlow = totalWidth - givenWidth + endMargin;
|
||||
|
||||
// First shrink the app name, down to a minimum size
|
||||
overFlow = shrinkViewForOverflow(heightSpec, overFlow, mAppName, mChildMinWidth);
|
||||
|
||||
// Next, shrink the header text (this usually has subText)
|
||||
// This shrinks the subtext first, but not all the way (yet!)
|
||||
overFlow = shrinkViewForOverflow(heightSpec, overFlow, mHeaderText, mChildMinWidth);
|
||||
|
||||
// Next, shrink the secondary header text (this rarely has conversationTitle)
|
||||
overFlow = shrinkViewForOverflow(heightSpec, overFlow, mSecondaryHeaderText, 0);
|
||||
|
||||
// Next, shrink the title text (this has contentTitle; only in headerless views)
|
||||
overFlow = shrinkViewForOverflow(heightSpec, overFlow, mTitle, mChildMinWidth);
|
||||
|
||||
// Finally, if there is still overflow, shrink the header down to 0 if still necessary.
|
||||
shrinkViewForOverflow(heightSpec, overFlow, mHeaderText, 0);
|
||||
mOverflowAdjuster.resetForOverflow(overFlow, heightSpec)
|
||||
// First shrink the app name, down to a minimum size
|
||||
.adjust(mAppName, null, mChildMinWidth)
|
||||
// Next, shrink the header text (this usually has subText)
|
||||
// This shrinks the subtext first, but not all the way (yet!)
|
||||
.adjust(mHeaderText, mHeaderTextDivider, mChildMinWidth)
|
||||
// Next, shrink the secondary header text (this rarely has conversationTitle)
|
||||
.adjust(mSecondaryHeaderText, mSecondaryHeaderTextDivider, 0)
|
||||
// Next, shrink the title text (this has contentTitle; only in headerless views)
|
||||
.adjust(mTitle, null, mChildMinWidth)
|
||||
// Next, shrink the header down to 0 if still necessary.
|
||||
.adjust(mHeaderText, mHeaderTextDivider, 0)
|
||||
// Finally, shrink the title to 0 if necessary (media is super cramped)
|
||||
.adjust(mTitle, null, 0)
|
||||
// Clean up
|
||||
.finish();
|
||||
}
|
||||
setMeasuredDimension(givenWidth, wrapHeight ? maxChildHeight : givenHeight);
|
||||
}
|
||||
|
||||
private int shrinkViewForOverflow(int heightSpec, int overFlow, View targetView,
|
||||
int minimumWidth) {
|
||||
if (targetView != null) {
|
||||
final int oldWidth = targetView.getMeasuredWidth();
|
||||
if (overFlow > 0 && targetView.getVisibility() != GONE && oldWidth > minimumWidth) {
|
||||
// we're still too big
|
||||
int newSize = Math.max(minimumWidth, oldWidth - overFlow);
|
||||
int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
|
||||
targetView.measure(childWidthSpec, heightSpec);
|
||||
overFlow -= oldWidth - newSize;
|
||||
}
|
||||
}
|
||||
return overFlow;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
int left = getPaddingStart();
|
||||
final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
|
||||
final int width = getWidth();
|
||||
int start = getPaddingStart();
|
||||
int childCount = getChildCount();
|
||||
int ownHeight = b - t;
|
||||
int childSpace = ownHeight - mPaddingTop - mPaddingBottom;
|
||||
@@ -182,8 +183,6 @@ public class NotificationTopLineView extends ViewGroup {
|
||||
}
|
||||
int childHeight = child.getMeasuredHeight();
|
||||
MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
|
||||
int layoutLeft;
|
||||
int layoutRight;
|
||||
|
||||
// Calculate vertical alignment of the views, accounting for the view baselines
|
||||
int childTop;
|
||||
@@ -219,19 +218,16 @@ public class NotificationTopLineView extends ViewGroup {
|
||||
default:
|
||||
childTop = mPaddingTop;
|
||||
}
|
||||
|
||||
left += params.getMarginStart();
|
||||
int right = left + child.getMeasuredWidth();
|
||||
layoutLeft = left;
|
||||
layoutRight = right;
|
||||
left = right + params.getMarginEnd();
|
||||
|
||||
if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
|
||||
int ltrLeft = layoutLeft;
|
||||
layoutLeft = getWidth() - layoutRight;
|
||||
layoutRight = getWidth() - ltrLeft;
|
||||
if (mViewsToDisappear.contains(child)) {
|
||||
child.layout(start, childTop, start, childTop + childHeight);
|
||||
} else {
|
||||
start += params.getMarginStart();
|
||||
int end = start + child.getMeasuredWidth();
|
||||
int layoutLeft = isRtl ? width - end : start;
|
||||
int layoutRight = isRtl ? width - start : end;
|
||||
start = end + params.getMarginEnd();
|
||||
child.layout(layoutLeft, childTop, layoutRight, childTop + childHeight);
|
||||
}
|
||||
child.layout(layoutLeft, childTop, layoutRight, childTop + childHeight);
|
||||
}
|
||||
updateTouchListener();
|
||||
}
|
||||
@@ -400,4 +396,83 @@ public class NotificationTopLineView extends ViewGroup {
|
||||
}
|
||||
return mTouchListener.onTouchUp(upX, upY, downX, downY);
|
||||
}
|
||||
|
||||
private final class OverflowAdjuster {
|
||||
private int mOverflow;
|
||||
private int mHeightSpec;
|
||||
private View mRegrowView;
|
||||
|
||||
OverflowAdjuster resetForOverflow(int overflow, int heightSpec) {
|
||||
mOverflow = overflow;
|
||||
mHeightSpec = heightSpec;
|
||||
mRegrowView = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shrink the targetView's width by up to overFlow, down to minimumWidth.
|
||||
* @param targetView the view to shrink the width of
|
||||
* @param targetDivider a divider view which should be set to 0 width if the targetView is
|
||||
* @param minimumWidth the minimum width allowed for the targetView
|
||||
* @return this object
|
||||
*/
|
||||
OverflowAdjuster adjust(View targetView, View targetDivider, int minimumWidth) {
|
||||
if (mOverflow <= 0 || targetView == null || targetView.getVisibility() == View.GONE) {
|
||||
return this;
|
||||
}
|
||||
final int oldWidth = targetView.getMeasuredWidth();
|
||||
if (oldWidth <= minimumWidth) {
|
||||
return this;
|
||||
}
|
||||
// we're too big
|
||||
int newSize = Math.max(minimumWidth, oldWidth - mOverflow);
|
||||
if (minimumWidth == 0 && newSize < mChildHideWidth
|
||||
&& mRegrowView != null && mRegrowView != targetView) {
|
||||
// View is so small it's better to hide it entirely (and its divider and margins)
|
||||
// so we can give that space back to another previously shrunken view.
|
||||
newSize = 0;
|
||||
}
|
||||
|
||||
int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
|
||||
targetView.measure(childWidthSpec, mHeightSpec);
|
||||
mOverflow -= oldWidth - newSize;
|
||||
|
||||
if (newSize == 0) {
|
||||
mViewsToDisappear.add(targetView);
|
||||
mOverflow -= getHorizontalMargins(targetView);
|
||||
if (targetDivider != null && targetDivider.getVisibility() != GONE) {
|
||||
mViewsToDisappear.add(targetDivider);
|
||||
int oldDividerWidth = targetDivider.getMeasuredWidth();
|
||||
int dividerWidthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.AT_MOST);
|
||||
targetDivider.measure(dividerWidthSpec, mHeightSpec);
|
||||
mOverflow -= (oldDividerWidth + getHorizontalMargins(targetDivider));
|
||||
}
|
||||
}
|
||||
if (mOverflow < 0 && mRegrowView != null) {
|
||||
// We're now under-flowing, so regrow the last view.
|
||||
final int regrowCurrentSize = mRegrowView.getMeasuredWidth();
|
||||
final int maxSize = regrowCurrentSize - mOverflow;
|
||||
int regrowWidthSpec = MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST);
|
||||
mRegrowView.measure(regrowWidthSpec, mHeightSpec);
|
||||
finish();
|
||||
return this;
|
||||
}
|
||||
|
||||
if (newSize != 0) {
|
||||
// if we shrunk this view (but did not completely hide it) store it for potential
|
||||
// re-growth if we proactively shorten a future view.
|
||||
mRegrowView = targetView;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
void finish() {
|
||||
resetForOverflow(0, 0);
|
||||
}
|
||||
|
||||
private int getHorizontalMargins(View view) {
|
||||
MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams();
|
||||
return params.getMarginStart() + params.getMarginEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,12 +129,6 @@ public final class SystemUiDeviceConfigFlags {
|
||||
|
||||
// Flags related to media notifications
|
||||
|
||||
/**
|
||||
* (boolean) If {@code true}, enables the seekbar in compact media notifications.
|
||||
*/
|
||||
public static final String COMPACT_MEDIA_SEEKBAR_ENABLED =
|
||||
"compact_media_notification_seekbar_enabled";
|
||||
|
||||
/**
|
||||
* (int) Maximum number of days to retain the salt for hashing direct share targets in logging
|
||||
*/
|
||||
|
||||
@@ -19,31 +19,19 @@ package com.android.internal.widget;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.NotificationHeaderView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A TextView that can float around an image on the end.
|
||||
* The Layout class which handles template details for the Notification.MediaStyle
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@RemoteViews.RemoteView
|
||||
public class MediaNotificationView extends FrameLayout {
|
||||
|
||||
private final int mNotificationContentMarginEnd;
|
||||
private final int mNotificationContentImageMarginEnd;
|
||||
private ImageView mRightIcon;
|
||||
private View mActions;
|
||||
private NotificationHeaderView mHeader;
|
||||
private View mMainColumn;
|
||||
private View mMediaContent;
|
||||
private int mImagePushIn;
|
||||
private ArrayList<VisibilityChangeListener> mListeners;
|
||||
|
||||
public MediaNotificationView(Context context) {
|
||||
@@ -58,120 +46,14 @@ public class MediaNotificationView extends FrameLayout {
|
||||
this(context, attrs, defStyleAttr, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
boolean hasIcon = mRightIcon.getVisibility() != GONE;
|
||||
if (!hasIcon) {
|
||||
resetHeaderIndention();
|
||||
}
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
int mode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
boolean reMeasure = false;
|
||||
mImagePushIn = 0;
|
||||
if (hasIcon && mode != MeasureSpec.UNSPECIFIED) {
|
||||
int size = MeasureSpec.getSize(widthMeasureSpec);
|
||||
size = size - mActions.getMeasuredWidth();
|
||||
ViewGroup.MarginLayoutParams layoutParams =
|
||||
(MarginLayoutParams) mRightIcon.getLayoutParams();
|
||||
int imageEndMargin = layoutParams.getMarginEnd();
|
||||
size -= imageEndMargin;
|
||||
int fullHeight = mMediaContent.getMeasuredHeight();
|
||||
if (size > fullHeight) {
|
||||
size = fullHeight;
|
||||
} else if (size < fullHeight) {
|
||||
size = Math.max(0, size);
|
||||
mImagePushIn = fullHeight - size;
|
||||
}
|
||||
if (layoutParams.width != fullHeight || layoutParams.height != fullHeight) {
|
||||
layoutParams.width = fullHeight;
|
||||
layoutParams.height = fullHeight;
|
||||
mRightIcon.setLayoutParams(layoutParams);
|
||||
reMeasure = true;
|
||||
}
|
||||
|
||||
// lets ensure that the main column doesn't run into the image
|
||||
ViewGroup.MarginLayoutParams params
|
||||
= (MarginLayoutParams) mMainColumn.getLayoutParams();
|
||||
int marginEnd = size + imageEndMargin + mNotificationContentMarginEnd;
|
||||
if (marginEnd != params.getMarginEnd()) {
|
||||
params.setMarginEnd(marginEnd);
|
||||
mMainColumn.setLayoutParams(params);
|
||||
reMeasure = true;
|
||||
}
|
||||
// TODO(b/172652345): validate all this logic (especially positioning of expand button)
|
||||
// margin for the entire header line
|
||||
int headerMarginEnd = imageEndMargin;
|
||||
// margin for the header text (not including the expand button and other icons)
|
||||
int headerExtraMarginEnd = Math.max(0,
|
||||
size + imageEndMargin - mHeader.getTopLineBaseMarginEnd());
|
||||
if (headerExtraMarginEnd != mHeader.getTopLineExtraMarginEnd()) {
|
||||
mHeader.setTopLineExtraMarginEnd(headerExtraMarginEnd);
|
||||
reMeasure = true;
|
||||
}
|
||||
params = (MarginLayoutParams) mHeader.getLayoutParams();
|
||||
if (params.getMarginEnd() != headerMarginEnd) {
|
||||
params.setMarginEnd(headerMarginEnd);
|
||||
mHeader.setLayoutParams(params);
|
||||
reMeasure = true;
|
||||
}
|
||||
if (mHeader.getPaddingEnd() != mNotificationContentImageMarginEnd) {
|
||||
mHeader.setPaddingRelative(mHeader.getPaddingStart(),
|
||||
mHeader.getPaddingTop(),
|
||||
mNotificationContentImageMarginEnd,
|
||||
mHeader.getPaddingBottom());
|
||||
reMeasure = true;
|
||||
}
|
||||
}
|
||||
if (reMeasure) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
if (mImagePushIn > 0) {
|
||||
if (this.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
|
||||
mImagePushIn *= -1;
|
||||
}
|
||||
mRightIcon.layout(mRightIcon.getLeft() + mImagePushIn, mRightIcon.getTop(),
|
||||
mRightIcon.getRight() + mImagePushIn, mRightIcon.getBottom());
|
||||
}
|
||||
}
|
||||
|
||||
private void resetHeaderIndention() {
|
||||
if (mHeader.getPaddingEnd() != mNotificationContentMarginEnd) {
|
||||
mHeader.setPaddingRelative(mHeader.getPaddingStart(),
|
||||
mHeader.getPaddingTop(),
|
||||
mNotificationContentMarginEnd,
|
||||
mHeader.getPaddingBottom());
|
||||
}
|
||||
ViewGroup.MarginLayoutParams headerParams =
|
||||
(MarginLayoutParams) mHeader.getLayoutParams();
|
||||
headerParams.setMarginEnd(0);
|
||||
if (headerParams.getMarginEnd() != 0) {
|
||||
headerParams.setMarginEnd(0);
|
||||
mHeader.setLayoutParams(headerParams);
|
||||
}
|
||||
}
|
||||
|
||||
public MediaNotificationView(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
mNotificationContentMarginEnd = context.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.notification_content_margin_end);
|
||||
mNotificationContentImageMarginEnd = context.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.notification_content_image_margin_end);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mRightIcon = findViewById(com.android.internal.R.id.right_icon);
|
||||
mActions = findViewById(com.android.internal.R.id.media_actions);
|
||||
mHeader = findViewById(com.android.internal.R.id.notification_header);
|
||||
mMainColumn = findViewById(com.android.internal.R.id.notification_main_column);
|
||||
mMediaContent = findViewById(com.android.internal.R.id.notification_media_content);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
android:paddingTop="8dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/notification_material_media_action_background"
|
||||
android:visibility="gone"
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2019 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
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/notification_media_progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_alignParentBottom="true"
|
||||
>
|
||||
<SeekBar android:id="@+id/notification_media_progress_bar"
|
||||
style="@style/Widget.ProgressBar.Horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxHeight="3dp"
|
||||
android:paddingTop="24dp"
|
||||
android:paddingBottom="24dp"
|
||||
android:clickable="true"
|
||||
android:layout_marginBottom="-24dp"
|
||||
android:layout_marginTop="-12dp"
|
||||
android:splitTrack="false"
|
||||
/>
|
||||
<FrameLayout
|
||||
android:id="@+id/notification_media_progress_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginBottom="11dp"
|
||||
>
|
||||
|
||||
<!-- width is set to "match_parent" to avoid extra layout calls -->
|
||||
<TextView android:id="@+id/notification_media_elapsed_time"
|
||||
style="@style/Widget.DeviceDefault.Notification.Text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginStart="@dimen/notification_content_margin_start"
|
||||
android:gravity="start"
|
||||
/>
|
||||
|
||||
<TextView android:id="@+id/notification_media_total_time"
|
||||
style="@style/Widget.DeviceDefault.Notification.Text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginEnd="@dimen/notification_content_margin_end"
|
||||
android:gravity="end"
|
||||
/>
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
@@ -46,20 +46,6 @@
|
||||
android:padding="@dimen/notification_icon_circle_padding"
|
||||
/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/right_icon"
|
||||
android:layout_width="@dimen/notification_right_icon_size"
|
||||
android:layout_height="@dimen/notification_right_icon_size"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
|
||||
android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
|
||||
android:layout_marginEnd="@dimen/notification_header_expand_icon_size"
|
||||
android:background="@drawable/notification_large_icon_outline"
|
||||
android:clipToOutline="true"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="centerCrop"
|
||||
/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/alternate_expand_target"
|
||||
android:layout_width="@dimen/notification_content_margin_start"
|
||||
@@ -68,95 +54,116 @@
|
||||
android:importantForAccessibility="no"
|
||||
/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/expand_button_touch_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end">
|
||||
|
||||
<include layout="@layout/notification_expand_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/notification_headerless_view_column"
|
||||
android:id="@+id/notification_headerless_view_row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginBottom="@dimen/notification_headerless_margin_twoline"
|
||||
android:layout_marginTop="@dimen/notification_headerless_margin_twoline"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/notification_content_margin_start"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
|
||||
<!-- extends ViewGroup -->
|
||||
<NotificationTopLineView
|
||||
android:id="@+id/notification_top_line"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/notification_headerless_line_height"
|
||||
android:layout_marginEnd="@dimen/notification_heading_margin_end"
|
||||
android:layout_marginStart="@dimen/notification_content_margin_start"
|
||||
android:clipChildren="false"
|
||||
android:theme="@style/Theme.DeviceDefault.Notification"
|
||||
>
|
||||
|
||||
<!--
|
||||
NOTE: The notification_top_line_views layout contains the app_name_text.
|
||||
In order to include the title view at the beginning, the Notification.Builder
|
||||
has logic to hide that view whenever this title view is to be visible.
|
||||
-->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/notification_header_separating_margin"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
|
||||
/>
|
||||
|
||||
<include layout="@layout/notification_top_line_views" />
|
||||
|
||||
</NotificationTopLineView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/notification_main_column"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/notification_headerless_view_column"
|
||||
android:layout_width="0px"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/notification_heading_margin_end"
|
||||
android:layout_marginStart="@dimen/notification_content_margin_start"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginBottom="@dimen/notification_headerless_margin_twoline"
|
||||
android:layout_marginTop="@dimen/notification_headerless_margin_twoline"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
||||
<com.android.internal.widget.NotificationVanishingFrameLayout
|
||||
android:layout_width="match_parent"
|
||||
<NotificationTopLineView
|
||||
android:id="@+id/notification_top_line"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/notification_headerless_line_height"
|
||||
android:clipChildren="false"
|
||||
android:theme="@style/Theme.DeviceDefault.Notification"
|
||||
>
|
||||
<!-- This is the simplest way to keep this text vertically centered without using
|
||||
gravity="center_vertical" which causes jumpiness in expansion animations. -->
|
||||
<include
|
||||
layout="@layout/notification_template_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/notification_text_height"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginTop="0dp"
|
||||
/>
|
||||
</com.android.internal.widget.NotificationVanishingFrameLayout>
|
||||
|
||||
<include
|
||||
layout="@layout/notification_template_progress"
|
||||
<!--
|
||||
NOTE: The notification_top_line_views layout contains the app_name_text.
|
||||
In order to include the title view at the beginning, the Notification.Builder
|
||||
has logic to hide that view whenever this title view is to be visible.
|
||||
-->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/notification_header_separating_margin"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
|
||||
/>
|
||||
|
||||
<include layout="@layout/notification_top_line_views" />
|
||||
|
||||
</NotificationTopLineView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/notification_main_column"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/notification_headerless_line_height"
|
||||
/>
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
||||
<com.android.internal.widget.NotificationVanishingFrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/notification_headerless_line_height"
|
||||
>
|
||||
<!-- This is the simplest way to keep this text vertically centered without
|
||||
gravity="center_vertical" which causes jumpiness in expansion animations. -->
|
||||
<include
|
||||
layout="@layout/notification_template_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/notification_text_height"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginTop="0dp"
|
||||
/>
|
||||
</com.android.internal.widget.NotificationVanishingFrameLayout>
|
||||
|
||||
<include
|
||||
layout="@layout/notification_template_progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/notification_headerless_line_height"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/right_icon"
|
||||
android:layout_width="@dimen/notification_right_icon_size"
|
||||
android:layout_height="@dimen/notification_right_icon_size"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
|
||||
android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
|
||||
android:layout_marginStart="@dimen/notification_right_icon_content_margin"
|
||||
android:background="@drawable/notification_large_icon_outline"
|
||||
android:clipToOutline="true"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="centerCrop"
|
||||
/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/expand_button_touch_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:minWidth="@dimen/notification_content_margin_end"
|
||||
>
|
||||
|
||||
<include layout="@layout/notification_expand_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.android.internal.widget.NotificationMaxHeightFrameLayout>
|
||||
|
||||
@@ -20,17 +20,8 @@
|
||||
android:id="@+id/status_bar_latest_event_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#00000000"
|
||||
android:tag="bigMediaNarrow"
|
||||
>
|
||||
<!-- The size will actually be determined at runtime -->
|
||||
<ImageView
|
||||
android:id="@+id/right_icon"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_gravity="top|end"
|
||||
android:scaleType="centerCrop"
|
||||
/>
|
||||
|
||||
<include
|
||||
layout="@layout/notification_template_header"
|
||||
@@ -49,46 +40,27 @@
|
||||
android:id="@+id/notification_main_column"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="46dp"
|
||||
android:layout_marginTop="@dimen/notification_content_margin_top"
|
||||
android:layout_marginStart="@dimen/notification_content_margin_start"
|
||||
android:layout_marginBottom="@dimen/notification_content_margin"
|
||||
android:layout_marginEnd="@dimen/notification_content_margin_end"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
<!-- TODO(b/172652345): fix the media style -->
|
||||
<!--<include layout="@layout/notification_template_part_line1"/>-->
|
||||
<!--<include layout="@layout/notification_template_text"/>-->
|
||||
|
||||
<TextView android:id="@+id/title"
|
||||
android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:textAlignment="viewStart"
|
||||
/>
|
||||
|
||||
<com.android.internal.widget.ImageFloatingTextView
|
||||
style="@style/Widget.DeviceDefault.Notification.Text"
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/notification_text_height"
|
||||
android:layout_gravity="top"
|
||||
android:layout_marginTop="0.5dp"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:gravity="top"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
/>
|
||||
<include layout="@layout/notification_template_part_line1"/>
|
||||
<include layout="@layout/notification_template_text"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/media_actions"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="-21dp"
|
||||
android:paddingStart="44dp"
|
||||
android:paddingEnd="44dp"
|
||||
android:paddingBottom="@dimen/media_notification_actions_padding_bottom"
|
||||
android:gravity="top"
|
||||
android:orientation="horizontal"
|
||||
android:layoutDirection="ltr"
|
||||
style="@style/NotificationMediaActionContainer"
|
||||
>
|
||||
|
||||
<include
|
||||
@@ -117,10 +89,8 @@
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/notification_media_seekbar_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<include layout="@layout/notification_template_right_icon" />
|
||||
|
||||
</com.android.internal.widget.MediaNotificationView>
|
||||
|
||||
@@ -19,101 +19,173 @@
|
||||
android:id="@+id/status_bar_latest_event_content"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#00000000"
|
||||
android:layout_height="@dimen/notification_min_height"
|
||||
android:tag="media"
|
||||
>
|
||||
<ImageView android:id="@+id/right_icon"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_gravity="top|end"
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/left_icon"
|
||||
android:layout_width="@dimen/notification_left_icon_size"
|
||||
android:layout_height="@dimen/notification_left_icon_size"
|
||||
android:layout_gravity="center_vertical|start"
|
||||
android:layout_marginStart="@dimen/notification_left_icon_start"
|
||||
android:background="@drawable/notification_large_icon_outline"
|
||||
android:clipToOutline="true"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="centerCrop"
|
||||
/>
|
||||
<include layout="@layout/notification_template_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/media_notification_header_height"
|
||||
android:visibility="gone"
|
||||
/>
|
||||
|
||||
<com.android.internal.widget.CachingIconView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="@dimen/notification_icon_circle_size"
|
||||
android:layout_height="@dimen/notification_icon_circle_size"
|
||||
android:layout_gravity="center_vertical|start"
|
||||
android:layout_marginStart="@dimen/notification_icon_circle_start"
|
||||
android:background="@drawable/notification_icon_circle"
|
||||
android:padding="@dimen/notification_icon_circle_padding"
|
||||
/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/alternate_expand_target"
|
||||
android:layout_width="@dimen/notification_content_margin_start"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
android:importantForAccessibility="no"
|
||||
/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/notification_headerless_view_row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/notification_media_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/notification_content_margin_start"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/notification_main_column"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/notification_headerless_view_column"
|
||||
android:layout_width="0px"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginStart="@dimen/notification_content_margin_start"
|
||||
android:layout_marginTop="46dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:tag="media"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginBottom="@dimen/notification_headerless_margin_twoline"
|
||||
android:layout_marginTop="@dimen/notification_headerless_margin_twoline"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
||||
<NotificationTopLineView
|
||||
android:id="@+id/notification_top_line"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/notification_headerless_line_height"
|
||||
android:clipChildren="false"
|
||||
android:theme="@style/Theme.DeviceDefault.Notification"
|
||||
>
|
||||
|
||||
<!--
|
||||
NOTE: The notification_top_line_views layout contains the app_name_text.
|
||||
In order to include the title view at the beginning, the Notification.Builder
|
||||
has logic to hide that view whenever this title view is to be visible.
|
||||
-->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/notification_header_separating_margin"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
|
||||
/>
|
||||
|
||||
<include layout="@layout/notification_top_line_views" />
|
||||
|
||||
</NotificationTopLineView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/notification_content_container"
|
||||
android:id="@+id/notification_main_column"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="fill_vertical"
|
||||
android:layout_weight="1"
|
||||
android:paddingBottom="@dimen/notification_content_margin"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
<!-- TODO(b/172652345): fix the media style -->
|
||||
<!--<include layout="@layout/notification_template_part_line1"/>-->
|
||||
<!--<include layout="@layout/notification_template_text"/>-->
|
||||
|
||||
<TextView android:id="@+id/title"
|
||||
android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
|
||||
<com.android.internal.widget.NotificationVanishingFrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:textAlignment="viewStart"
|
||||
android:layout_height="@dimen/notification_headerless_line_height"
|
||||
>
|
||||
<!-- This is the simplest way to keep this text vertically centered without
|
||||
gravity="center_vertical" which causes jumpiness in expansion animations. -->
|
||||
<include
|
||||
layout="@layout/notification_template_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/notification_text_height"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginTop="0dp"
|
||||
/>
|
||||
</com.android.internal.widget.NotificationVanishingFrameLayout>
|
||||
|
||||
<include
|
||||
layout="@layout/notification_template_progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/notification_headerless_line_height"
|
||||
/>
|
||||
|
||||
<com.android.internal.widget.ImageFloatingTextView
|
||||
style="@style/Widget.DeviceDefault.Notification.Text"
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/notification_text_height"
|
||||
android:layout_gravity="top"
|
||||
android:layout_marginTop="0.5dp"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:gravity="top"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/media_actions"
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/right_icon"
|
||||
android:layout_width="@dimen/notification_right_icon_size"
|
||||
android:layout_height="@dimen/notification_right_icon_size"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
|
||||
android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
|
||||
android:layout_marginStart="@dimen/notification_right_icon_content_margin"
|
||||
android:background="@drawable/notification_large_icon_outline"
|
||||
android:clipToOutline="true"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="centerCrop"
|
||||
/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/media_actions"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layoutDirection="ltr"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
<include
|
||||
layout="@layout/notification_material_media_action"
|
||||
android:id="@+id/action0"
|
||||
/>
|
||||
<include
|
||||
layout="@layout/notification_material_media_action"
|
||||
android:id="@+id/action1"
|
||||
/>
|
||||
<include
|
||||
layout="@layout/notification_material_media_action"
|
||||
android:id="@+id/action2"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/expand_button_touch_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:minWidth="@dimen/notification_content_margin_end"
|
||||
>
|
||||
|
||||
<include layout="@layout/notification_expand_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top|end"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layoutDirection="ltr"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
<include
|
||||
layout="@layout/notification_material_media_action"
|
||||
android:id="@+id/action0"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
/>
|
||||
<include
|
||||
layout="@layout/notification_material_media_action"
|
||||
android:id="@+id/action1"
|
||||
/>
|
||||
<include
|
||||
layout="@layout/notification_material_media_action"
|
||||
android:id="@+id/action2"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
<ViewStub android:id="@+id/notification_media_seekbar_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</com.android.internal.widget.MediaNotificationView>
|
||||
|
||||
@@ -226,9 +226,6 @@
|
||||
<!-- The margin on the end of the top-line content views (accommodates the expander) -->
|
||||
<dimen name="notification_heading_margin_end">56dp</dimen>
|
||||
|
||||
<!-- The margin for text at the end of the image view for media notifications -->
|
||||
<dimen name="notification_media_image_margin_end">72dp</dimen>
|
||||
|
||||
<!-- The height of the notification action list -->
|
||||
<dimen name="notification_action_list_height">60dp</dimen>
|
||||
|
||||
@@ -345,6 +342,9 @@
|
||||
<!-- The minimum width of the app name in the header if it shrinks -->
|
||||
<dimen name="notification_header_shrink_min_width">72dp</dimen>
|
||||
|
||||
<!-- The minimum width of optional header fields below which the view is simply hidden -->
|
||||
<dimen name="notification_header_shrink_hide_width">24sp</dimen>
|
||||
|
||||
<!-- The size of the media actions in the media notification. -->
|
||||
<dimen name="media_notification_action_button_size">48dp</dimen>
|
||||
|
||||
@@ -360,9 +360,6 @@
|
||||
<!-- The absolute height for the header in a media notification. -->
|
||||
<dimen name="media_notification_header_height">@dimen/notification_header_height</dimen>
|
||||
|
||||
<!-- The margin of the content to an image-->
|
||||
<dimen name="notification_content_image_margin_end">8dp</dimen>
|
||||
|
||||
<!-- The padding at the end of actions when the snooze and bubble buttons are gone-->
|
||||
<dimen name="snooze_and_bubble_gone_padding_end">12dp</dimen>
|
||||
|
||||
@@ -483,9 +480,6 @@
|
||||
<!-- Top padding for notification when text is large and narrow (i.e. it has 3 lines -->
|
||||
<dimen name="notification_top_pad_large_text_narrow">-4dp</dimen>
|
||||
|
||||
<!-- Padding for notification icon when drawn with circle around it -->
|
||||
<dimen name="notification_large_icon_circle_padding">11dp</dimen>
|
||||
|
||||
<!-- The margin on top of the text of the notification -->
|
||||
<dimen name="notification_text_margin_top">6dp</dimen>
|
||||
|
||||
@@ -736,12 +730,10 @@
|
||||
<dimen name="notification_big_picture_max_height">284dp</dimen>
|
||||
<!-- The maximum width of a big picture in a notification. The images will be reduced to that width in case they are bigger. This value is determined by the standard panel size -->
|
||||
<dimen name="notification_big_picture_max_width">416dp</dimen>
|
||||
<!-- The maximum height of a image in a media notification. The images will be reduced to that height in case they are bigger. This value is determined by the expanded media template-->
|
||||
<dimen name="notification_media_image_max_height">140dp</dimen>
|
||||
<!-- The maximum width of a image in a media notification. The images will be reduced to that width in case they are bigger.-->
|
||||
<dimen name="notification_media_image_max_width">280dp</dimen>
|
||||
<!-- The size of the right icon -->
|
||||
<dimen name="notification_right_icon_size">48dp</dimen>
|
||||
<!-- The margin between the right icon and the content. -->
|
||||
<dimen name="notification_right_icon_content_margin">12dp</dimen>
|
||||
<!-- The top and bottom margin of the right icon in the normal notification states -->
|
||||
<dimen name="notification_right_icon_headerless_margin">20dp</dimen>
|
||||
<!-- The top margin of the right icon in the "big" notification states -->
|
||||
@@ -762,10 +754,6 @@
|
||||
<dimen name="notification_big_picture_max_height_low_ram">208dp</dimen>
|
||||
<!-- The maximum width of a big picture in a notification. The images will be reduced to that width in case they are bigger. -->
|
||||
<dimen name="notification_big_picture_max_width_low_ram">294dp</dimen>
|
||||
<!-- The maximum height of a image in a media notification. The images will be reduced to that height in case they are bigger. -->
|
||||
<dimen name="notification_media_image_max_height_low_ram">100dp</dimen>
|
||||
<!-- The maximum width of a image in a media notification. The images will be reduced to that width in case they are bigger.-->
|
||||
<dimen name="notification_media_image_max_width_low_ram">100dp</dimen>
|
||||
<!-- The size of the right icon image when on low ram -->
|
||||
<dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size</dimen>
|
||||
<!-- The maximum size of the grayscale icon -->
|
||||
|
||||
@@ -1481,17 +1481,6 @@ please see styles_device_defaults.xml.
|
||||
<item name="android:windowExitAnimation">@anim/slide_out_down</item>
|
||||
</style>
|
||||
|
||||
<!-- The style for the container of media actions in a notification. -->
|
||||
<!-- @hide -->
|
||||
<style name="NotificationMediaActionContainer">
|
||||
<item name="layout_width">wrap_content</item>
|
||||
<item name="layout_height">wrap_content</item>
|
||||
<item name="layout_marginTop">-21dp</item>
|
||||
<item name="paddingStart">8dp</item>
|
||||
<item name="paddingBottom">@dimen/media_notification_actions_padding_bottom</item>
|
||||
<item name="gravity">top</item>
|
||||
</style>
|
||||
|
||||
<!-- The style for normal action button on notification -->
|
||||
<style name="NotificationAction" parent="Widget.Material.Light.Button.Borderless.Small">
|
||||
<item name="textColor">@color/notification_action_button_text_color</item>
|
||||
|
||||
@@ -200,12 +200,7 @@
|
||||
<java-symbol type="id" name="action2" />
|
||||
<java-symbol type="id" name="action3" />
|
||||
<java-symbol type="id" name="action4" />
|
||||
<java-symbol type="id" name="notification_media_seekbar_container" />
|
||||
<java-symbol type="id" name="notification_media_content" />
|
||||
<java-symbol type="id" name="notification_media_progress" />
|
||||
<java-symbol type="id" name="notification_media_progress_bar" />
|
||||
<java-symbol type="id" name="notification_media_elapsed_time" />
|
||||
<java-symbol type="id" name="notification_media_total_time" />
|
||||
<java-symbol type="id" name="big_picture" />
|
||||
<java-symbol type="id" name="big_text" />
|
||||
<java-symbol type="id" name="chronometer" />
|
||||
@@ -525,7 +520,6 @@
|
||||
<java-symbol type="dimen" name="notification_top_pad_narrow" />
|
||||
<java-symbol type="dimen" name="notification_top_pad_large_text" />
|
||||
<java-symbol type="dimen" name="notification_top_pad_large_text_narrow" />
|
||||
<java-symbol type="dimen" name="notification_large_icon_circle_padding" />
|
||||
<java-symbol type="dimen" name="notification_badge_size" />
|
||||
<java-symbol type="dimen" name="immersive_mode_cling_width" />
|
||||
<java-symbol type="dimen" name="accessibility_magnification_indicator_width" />
|
||||
@@ -1564,7 +1558,6 @@
|
||||
<java-symbol type="layout" name="immersive_mode_cling" />
|
||||
<java-symbol type="layout" name="user_switching_dialog" />
|
||||
<java-symbol type="layout" name="common_tab_settings" />
|
||||
<java-symbol type="layout" name="notification_material_media_seekbar" />
|
||||
<java-symbol type="layout" name="resolver_list_per_profile" />
|
||||
<java-symbol type="layout" name="chooser_list_per_profile" />
|
||||
<java-symbol type="layout" name="resolver_empty_states" />
|
||||
@@ -2921,6 +2914,7 @@
|
||||
<java-symbol type="drawable" name="ic_expand_bundle" />
|
||||
<java-symbol type="drawable" name="ic_collapse_bundle" />
|
||||
<java-symbol type="dimen" name="notification_header_shrink_min_width" />
|
||||
<java-symbol type="dimen" name="notification_header_shrink_hide_width" />
|
||||
<java-symbol type="dimen" name="notification_content_margin_start" />
|
||||
<java-symbol type="dimen" name="notification_content_margin_end" />
|
||||
<java-symbol type="dimen" name="notification_heading_margin_end" />
|
||||
@@ -3010,7 +3004,6 @@
|
||||
<java-symbol type="string" name="new_sms_notification_content" />
|
||||
|
||||
<java-symbol type="dimen" name="media_notification_expanded_image_margin_bottom" />
|
||||
<java-symbol type="dimen" name="notification_content_image_margin_end" />
|
||||
|
||||
<java-symbol type="bool" name="config_strongAuthRequiredOnBoot" />
|
||||
|
||||
@@ -3019,8 +3012,6 @@
|
||||
|
||||
<java-symbol type="id" name="aerr_wait" />
|
||||
|
||||
<java-symbol type="id" name="notification_content_container" />
|
||||
|
||||
<java-symbol type="plurals" name="duration_minutes_shortest" />
|
||||
<java-symbol type="plurals" name="duration_hours_shortest" />
|
||||
<java-symbol type="plurals" name="duration_days_shortest" />
|
||||
@@ -3138,7 +3129,6 @@
|
||||
|
||||
<java-symbol type="bool" name="config_supportPreRebootSecurityLogs" />
|
||||
|
||||
<java-symbol type="dimen" name="notification_media_image_margin_end" />
|
||||
<java-symbol type="id" name="notification_action_list_margin_target" />
|
||||
<java-symbol type="dimen" name="notification_action_disabled_alpha" />
|
||||
<java-symbol type="id" name="tag_margin_end_when_icon_visible" />
|
||||
@@ -3461,17 +3451,14 @@
|
||||
|
||||
<java-symbol type="dimen" name="notification_big_picture_max_height"/>
|
||||
<java-symbol type="dimen" name="notification_big_picture_max_width"/>
|
||||
<java-symbol type="dimen" name="notification_media_image_max_width"/>
|
||||
<java-symbol type="dimen" name="notification_media_image_max_height"/>
|
||||
<java-symbol type="dimen" name="notification_right_icon_size"/>
|
||||
<java-symbol type="dimen" name="notification_right_icon_content_margin"/>
|
||||
<java-symbol type="dimen" name="notification_actions_icon_drawable_size"/>
|
||||
<java-symbol type="dimen" name="notification_custom_view_max_image_height"/>
|
||||
<java-symbol type="dimen" name="notification_custom_view_max_image_width"/>
|
||||
|
||||
<java-symbol type="dimen" name="notification_big_picture_max_height_low_ram"/>
|
||||
<java-symbol type="dimen" name="notification_big_picture_max_width_low_ram"/>
|
||||
<java-symbol type="dimen" name="notification_media_image_max_width_low_ram"/>
|
||||
<java-symbol type="dimen" name="notification_media_image_max_height_low_ram"/>
|
||||
<java-symbol type="dimen" name="notification_right_icon_size_low_ram"/>
|
||||
<java-symbol type="dimen" name="notification_grayscale_icon_max_size"/>
|
||||
<java-symbol type="dimen" name="notification_custom_view_max_image_height_low_ram"/>
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package android.app;
|
||||
|
||||
import static com.android.internal.util.ContrastColorUtil.satisfiesTextContrast;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
@@ -98,23 +96,6 @@ public class NotificationTest {
|
||||
assertFalse(n.isColorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testColorSatisfiedWhenBgDarkTextDarker() {
|
||||
Notification.Builder builder = getMediaNotification();
|
||||
Notification n = builder.build();
|
||||
|
||||
assertTrue(n.isColorized());
|
||||
|
||||
// An initial guess where the foreground color is actually darker than an already dark bg
|
||||
int backgroundColor = 0xff585868;
|
||||
int initialForegroundColor = 0xff505868;
|
||||
builder.setColorPalette(backgroundColor, initialForegroundColor);
|
||||
int primaryTextColor = builder.getPrimaryTextColor(builder.mParams);
|
||||
assertTrue(satisfiesTextContrast(primaryTextColor, backgroundColor));
|
||||
int secondaryTextColor = builder.getSecondaryTextColor(builder.mParams);
|
||||
assertTrue(satisfiesTextContrast(secondaryTextColor, backgroundColor));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasCompletedProgress_noProgress() {
|
||||
Notification n = new Notification.Builder(mContext).build();
|
||||
|
||||
@@ -38,8 +38,6 @@ import android.media.session.PlaybackState;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Trace;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.provider.DeviceConfig.Properties;
|
||||
import android.service.notification.NotificationListenerService;
|
||||
import android.service.notification.NotificationStats;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
@@ -48,7 +46,6 @@ import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
|
||||
import com.android.internal.statusbar.NotificationVisibility;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.Dumpable;
|
||||
@@ -147,23 +144,6 @@ public class NotificationMediaManager implements Dumpable {
|
||||
private ImageView mBackdropFront;
|
||||
private ImageView mBackdropBack;
|
||||
|
||||
private boolean mShowCompactMediaSeekbar;
|
||||
private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener =
|
||||
new DeviceConfig.OnPropertiesChangedListener() {
|
||||
@Override
|
||||
public void onPropertiesChanged(Properties properties) {
|
||||
for (String name : properties.getKeyset()) {
|
||||
if (SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED.equals(name)) {
|
||||
String value = properties.getString(name, null);
|
||||
if (DEBUG_MEDIA) {
|
||||
Log.v(TAG, "DEBUG_MEDIA: compact media seekbar flag updated: " + value);
|
||||
}
|
||||
mShowCompactMediaSeekbar = "true".equals(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final MediaController.Callback mMediaListener = new MediaController.Callback() {
|
||||
@Override
|
||||
public void onPlaybackStateChanged(PlaybackState state) {
|
||||
@@ -231,14 +211,6 @@ public class NotificationMediaManager implements Dumpable {
|
||||
setupNotifPipeline();
|
||||
mUsingNotifPipeline = true;
|
||||
}
|
||||
|
||||
mShowCompactMediaSeekbar = "true".equals(
|
||||
DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||
SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED));
|
||||
|
||||
deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||
mContext.getMainExecutor(),
|
||||
mPropertiesChangedListener);
|
||||
}
|
||||
|
||||
private void setupNotifPipeline() {
|
||||
@@ -405,10 +377,6 @@ public class NotificationMediaManager implements Dumpable {
|
||||
return mMediaMetadata;
|
||||
}
|
||||
|
||||
public boolean getShowCompactMediaSeekbar() {
|
||||
return mShowCompactMediaSeekbar;
|
||||
}
|
||||
|
||||
public Icon getMediaIcon() {
|
||||
if (mMediaNotificationKey == null) {
|
||||
return null;
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorMatrix;
|
||||
import android.graphics.ColorMatrixColorFilter;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
/**
|
||||
* A utility class to colorize bitmaps with a color gradient and a special blending mode
|
||||
*/
|
||||
public class ImageGradientColorizer {
|
||||
public Bitmap colorize(Drawable drawable, int backgroundColor, boolean isRtl) {
|
||||
int width = drawable.getIntrinsicWidth();
|
||||
int height = drawable.getIntrinsicHeight();
|
||||
int size = Math.min(width, height);
|
||||
int widthInset = (width - size) / 2;
|
||||
int heightInset = (height - size) / 2;
|
||||
drawable = drawable.mutate();
|
||||
drawable.setBounds(- widthInset, - heightInset, width - widthInset, height - heightInset);
|
||||
Bitmap newBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(newBitmap);
|
||||
|
||||
// Values to calculate the luminance of a color
|
||||
float lr = 0.2126f;
|
||||
float lg = 0.7152f;
|
||||
float lb = 0.0722f;
|
||||
|
||||
// Extract the red, green, blue components of the color extraction color in
|
||||
// float and int form
|
||||
int tri = Color.red(backgroundColor);
|
||||
int tgi = Color.green(backgroundColor);
|
||||
int tbi = Color.blue(backgroundColor);
|
||||
|
||||
float tr = tri / 255f;
|
||||
float tg = tgi / 255f;
|
||||
float tb = tbi / 255f;
|
||||
|
||||
// Calculate the luminance of the color extraction color
|
||||
float cLum = (tr * lr + tg * lg + tb * lb) * 255;
|
||||
|
||||
ColorMatrix m = new ColorMatrix(new float[] {
|
||||
lr, lg, lb, 0, tri - cLum,
|
||||
lr, lg, lb, 0, tgi - cLum,
|
||||
lr, lg, lb, 0, tbi - cLum,
|
||||
0, 0, 0, 1, 0,
|
||||
});
|
||||
|
||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
LinearGradient linearGradient = new LinearGradient(0, 0, size, 0,
|
||||
new int[] {0, Color.argb(0.5f, 1, 1, 1), Color.BLACK},
|
||||
new float[] {0.0f, 0.4f, 1.0f}, Shader.TileMode.CLAMP);
|
||||
paint.setShader(linearGradient);
|
||||
Bitmap fadeIn = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
|
||||
Canvas fadeInCanvas = new Canvas(fadeIn);
|
||||
drawable.clearColorFilter();
|
||||
drawable.draw(fadeInCanvas);
|
||||
|
||||
if (isRtl) {
|
||||
// Let's flip the gradient
|
||||
fadeInCanvas.translate(size, 0);
|
||||
fadeInCanvas.scale(-1, 1);
|
||||
}
|
||||
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
|
||||
fadeInCanvas.drawPaint(paint);
|
||||
|
||||
Paint coloredPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
coloredPaint.setColorFilter(new ColorMatrixColorFilter(m));
|
||||
coloredPaint.setAlpha((int) (0.5f * 255));
|
||||
canvas.drawBitmap(fadeIn, 0, 0, coloredPaint);
|
||||
|
||||
linearGradient = new LinearGradient(0, 0, size, 0,
|
||||
new int[] {0, Color.argb(0.5f, 1, 1, 1), Color.BLACK},
|
||||
new float[] {0.0f, 0.6f, 1.0f}, Shader.TileMode.CLAMP);
|
||||
paint.setShader(linearGradient);
|
||||
fadeInCanvas.drawPaint(paint);
|
||||
canvas.drawBitmap(fadeIn, 0, 0, null);
|
||||
|
||||
return newBitmap;
|
||||
}
|
||||
}
|
||||
@@ -16,50 +16,21 @@
|
||||
|
||||
package com.android.systemui.statusbar.notification;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.util.LayoutDirection;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.palette.graphics.Palette;
|
||||
|
||||
import com.android.internal.util.ContrastColorUtil;
|
||||
import com.android.settingslib.Utils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A class the processes media notifications and extracts the right text and background colors.
|
||||
* A gutted class that now contains only a color extraction utility used by the
|
||||
* MediaArtworkProcessor, which has otherwise supplanted this.
|
||||
*
|
||||
* TODO(b/182926117): move this into MediaArtworkProcessor.kt
|
||||
*/
|
||||
public class MediaNotificationProcessor {
|
||||
|
||||
/**
|
||||
* The fraction below which we select the vibrant instead of the light/dark vibrant color
|
||||
*/
|
||||
private static final float POPULATION_FRACTION_FOR_MORE_VIBRANT = 1.0f;
|
||||
|
||||
/**
|
||||
* Minimum saturation that a muted color must have if there exists if deciding between two
|
||||
* colors
|
||||
*/
|
||||
private static final float MIN_SATURATION_WHEN_DECIDING = 0.19f;
|
||||
|
||||
/**
|
||||
* Minimum fraction that any color must have to be picked up as a text color
|
||||
*/
|
||||
private static final double MINIMUM_IMAGE_FRACTION = 0.002;
|
||||
|
||||
/**
|
||||
* The population fraction to select the dominant color as the text color over a the colored
|
||||
* ones.
|
||||
*/
|
||||
private static final float POPULATION_FRACTION_FOR_DOMINANT = 0.01f;
|
||||
|
||||
/**
|
||||
* The population fraction to select a white or black color as the background over a color.
|
||||
*/
|
||||
@@ -67,186 +38,8 @@ public class MediaNotificationProcessor {
|
||||
private static final float BLACK_MAX_LIGHTNESS = 0.08f;
|
||||
private static final float WHITE_MIN_LIGHTNESS = 0.90f;
|
||||
private static final int RESIZE_BITMAP_AREA = 150 * 150;
|
||||
private final ImageGradientColorizer mColorizer;
|
||||
private final Context mContext;
|
||||
private final Palette.Filter mBlackWhiteFilter = (rgb, hsl) -> !isWhiteOrBlack(hsl);
|
||||
|
||||
/**
|
||||
* The context of the notification. This is the app context of the package posting the
|
||||
* notification.
|
||||
*/
|
||||
private final Context mPackageContext;
|
||||
|
||||
public MediaNotificationProcessor(Context context, Context packageContext) {
|
||||
this(context, packageContext, new ImageGradientColorizer());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
MediaNotificationProcessor(Context context, Context packageContext,
|
||||
ImageGradientColorizer colorizer) {
|
||||
mContext = context;
|
||||
mPackageContext = packageContext;
|
||||
mColorizer = colorizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a builder of a media notification and calculates the appropriate colors that should
|
||||
* be used.
|
||||
*
|
||||
* @param notification the notification that is being processed
|
||||
* @param builder the recovered builder for the notification. this will be modified
|
||||
*/
|
||||
public void processNotification(Notification notification, Notification.Builder builder) {
|
||||
Icon largeIcon = notification.getLargeIcon();
|
||||
Bitmap bitmap = null;
|
||||
Drawable drawable = null;
|
||||
if (largeIcon != null) {
|
||||
// We're transforming the builder, let's make sure all baked in RemoteViews are
|
||||
// rebuilt!
|
||||
builder.setRebuildStyledRemoteViews(true);
|
||||
drawable = largeIcon.loadDrawable(mPackageContext);
|
||||
int backgroundColor = 0;
|
||||
if (notification.isColorizedMedia()) {
|
||||
int width = drawable.getIntrinsicWidth();
|
||||
int height = drawable.getIntrinsicHeight();
|
||||
int area = width * height;
|
||||
if (area > RESIZE_BITMAP_AREA) {
|
||||
double factor = Math.sqrt((float) RESIZE_BITMAP_AREA / area);
|
||||
width = (int) (factor * width);
|
||||
height = (int) (factor * height);
|
||||
}
|
||||
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, width, height);
|
||||
drawable.draw(canvas);
|
||||
|
||||
Palette.Builder paletteBuilder = generateArtworkPaletteBuilder(bitmap);
|
||||
Palette palette = paletteBuilder.generate();
|
||||
Palette.Swatch backgroundSwatch = findBackgroundSwatch(palette);
|
||||
backgroundColor = backgroundSwatch.getRgb();
|
||||
// we want most of the full region again, slightly shifted to the right
|
||||
float textColorStartWidthFraction = 0.4f;
|
||||
paletteBuilder.setRegion((int) (bitmap.getWidth() * textColorStartWidthFraction), 0,
|
||||
bitmap.getWidth(),
|
||||
bitmap.getHeight());
|
||||
// We're not filtering on white or black
|
||||
if (!isWhiteOrBlack(backgroundSwatch.getHsl())) {
|
||||
final float backgroundHue = backgroundSwatch.getHsl()[0];
|
||||
paletteBuilder.addFilter((rgb, hsl) -> {
|
||||
// at least 10 degrees hue difference
|
||||
float diff = Math.abs(hsl[0] - backgroundHue);
|
||||
return diff > 10 && diff < 350;
|
||||
});
|
||||
}
|
||||
paletteBuilder.addFilter(mBlackWhiteFilter);
|
||||
palette = paletteBuilder.generate();
|
||||
int foregroundColor = selectForegroundColor(backgroundColor, palette);
|
||||
builder.setColorPalette(backgroundColor, foregroundColor);
|
||||
} else {
|
||||
backgroundColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground)
|
||||
.getDefaultColor();
|
||||
}
|
||||
Bitmap colorized = mColorizer.colorize(drawable, backgroundColor,
|
||||
mContext.getResources().getConfiguration().getLayoutDirection() ==
|
||||
LayoutDirection.RTL);
|
||||
builder.setLargeIcon(Icon.createWithBitmap(colorized));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a foreground color depending on whether the background color is dark or light
|
||||
* @param backgroundColor Background color to coordinate with
|
||||
* @param palette Artwork palette, should be obtained from {@link generateArtworkPaletteBuilder}
|
||||
* @return foreground color
|
||||
*/
|
||||
public static int selectForegroundColor(int backgroundColor, Palette palette) {
|
||||
if (ContrastColorUtil.isColorLight(backgroundColor)) {
|
||||
return selectForegroundColorForSwatches(palette.getDarkVibrantSwatch(),
|
||||
palette.getVibrantSwatch(),
|
||||
palette.getDarkMutedSwatch(),
|
||||
palette.getMutedSwatch(),
|
||||
palette.getDominantSwatch(),
|
||||
Color.BLACK);
|
||||
} else {
|
||||
return selectForegroundColorForSwatches(palette.getLightVibrantSwatch(),
|
||||
palette.getVibrantSwatch(),
|
||||
palette.getLightMutedSwatch(),
|
||||
palette.getMutedSwatch(),
|
||||
palette.getDominantSwatch(),
|
||||
Color.WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
private static int selectForegroundColorForSwatches(Palette.Swatch moreVibrant,
|
||||
Palette.Swatch vibrant, Palette.Swatch moreMutedSwatch, Palette.Swatch mutedSwatch,
|
||||
Palette.Swatch dominantSwatch, int fallbackColor) {
|
||||
Palette.Swatch coloredCandidate = selectVibrantCandidate(moreVibrant, vibrant);
|
||||
if (coloredCandidate == null) {
|
||||
coloredCandidate = selectMutedCandidate(mutedSwatch, moreMutedSwatch);
|
||||
}
|
||||
if (coloredCandidate != null) {
|
||||
if (dominantSwatch == coloredCandidate) {
|
||||
return coloredCandidate.getRgb();
|
||||
} else if ((float) coloredCandidate.getPopulation() / dominantSwatch.getPopulation()
|
||||
< POPULATION_FRACTION_FOR_DOMINANT
|
||||
&& dominantSwatch.getHsl()[1] > MIN_SATURATION_WHEN_DECIDING) {
|
||||
return dominantSwatch.getRgb();
|
||||
} else {
|
||||
return coloredCandidate.getRgb();
|
||||
}
|
||||
} else if (hasEnoughPopulation(dominantSwatch)) {
|
||||
return dominantSwatch.getRgb();
|
||||
} else {
|
||||
return fallbackColor;
|
||||
}
|
||||
}
|
||||
|
||||
private static Palette.Swatch selectMutedCandidate(Palette.Swatch first,
|
||||
Palette.Swatch second) {
|
||||
boolean firstValid = hasEnoughPopulation(first);
|
||||
boolean secondValid = hasEnoughPopulation(second);
|
||||
if (firstValid && secondValid) {
|
||||
float firstSaturation = first.getHsl()[1];
|
||||
float secondSaturation = second.getHsl()[1];
|
||||
float populationFraction = first.getPopulation() / (float) second.getPopulation();
|
||||
if (firstSaturation * populationFraction > secondSaturation) {
|
||||
return first;
|
||||
} else {
|
||||
return second;
|
||||
}
|
||||
} else if (firstValid) {
|
||||
return first;
|
||||
} else if (secondValid) {
|
||||
return second;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Palette.Swatch selectVibrantCandidate(Palette.Swatch first,
|
||||
Palette.Swatch second) {
|
||||
boolean firstValid = hasEnoughPopulation(first);
|
||||
boolean secondValid = hasEnoughPopulation(second);
|
||||
if (firstValid && secondValid) {
|
||||
int firstPopulation = first.getPopulation();
|
||||
int secondPopulation = second.getPopulation();
|
||||
if (firstPopulation / (float) secondPopulation
|
||||
< POPULATION_FRACTION_FOR_MORE_VIBRANT) {
|
||||
return second;
|
||||
} else {
|
||||
return first;
|
||||
}
|
||||
} else if (firstValid) {
|
||||
return first;
|
||||
} else if (secondValid) {
|
||||
return second;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean hasEnoughPopulation(Palette.Swatch swatch) {
|
||||
// We want a fraction that is at least 1% of the image
|
||||
return swatch != null
|
||||
&& (swatch.getPopulation() / (float) RESIZE_BITMAP_AREA > MINIMUM_IMAGE_FRACTION);
|
||||
private MediaNotificationProcessor() {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,7 +72,7 @@ public class MediaNotificationProcessor {
|
||||
List<Palette.Swatch> swatches = palette.getSwatches();
|
||||
float highestNonWhitePopulation = -1;
|
||||
Palette.Swatch second = null;
|
||||
for (Palette.Swatch swatch: swatches) {
|
||||
for (Palette.Swatch swatch : swatches) {
|
||||
if (swatch != dominantSwatch
|
||||
&& swatch.getPopulation() > highestNonWhitePopulation
|
||||
&& !isWhiteOrBlack(swatch.getHsl())) {
|
||||
|
||||
@@ -668,7 +668,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
&& expandedView.findViewById(com.android.internal.R.id.media_actions) != null;
|
||||
boolean isMessagingLayout = contractedView instanceof MessagingLayout;
|
||||
boolean isCallLayout = contractedView instanceof CallLayout;
|
||||
boolean showCompactMediaSeekbar = mMediaManager.getShowCompactMediaSeekbar();
|
||||
|
||||
if (customView && beforeS && !mIsSummaryWithChildren) {
|
||||
if (beforeN) {
|
||||
@@ -678,12 +677,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
} else {
|
||||
smallHeight = mMaxSmallHeightBeforeS;
|
||||
}
|
||||
} else if (isMediaLayout) {
|
||||
// TODO(b/172652345): MediaStyle notifications currently look broken when we enforce
|
||||
// the standard notification height, so we have to afford them more vertical space to
|
||||
// make sure we don't crop them terribly. We actually need to revisit this and give
|
||||
// them a headerless design, then remove this hack.
|
||||
smallHeight = showCompactMediaSeekbar ? mMaxSmallHeightMedia : mMaxSmallHeightBeforeS;
|
||||
} else if (isMessagingLayout) {
|
||||
// TODO(b/173204301): MessagingStyle notifications currently look broken when we enforce
|
||||
// the standard notification height, so we have to afford them more vertical space to
|
||||
|
||||
@@ -40,13 +40,11 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.widget.ImageMessageConsumer;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.dagger.qualifiers.Background;
|
||||
import com.android.systemui.media.MediaDataManagerKt;
|
||||
import com.android.systemui.media.MediaFeatureFlag;
|
||||
import com.android.systemui.statusbar.InflationTask;
|
||||
import com.android.systemui.statusbar.NotificationRemoteInputManager;
|
||||
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
|
||||
import com.android.systemui.statusbar.notification.InflationException;
|
||||
import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
|
||||
import com.android.systemui.statusbar.phone.StatusBar;
|
||||
@@ -799,13 +797,6 @@ public class NotificationContentInflater implements NotificationRowContentBinder
|
||||
// For all of our templates, we want it to be RTL
|
||||
packageContext = new RtlEnabledContext(packageContext);
|
||||
}
|
||||
Notification notification = sbn.getNotification();
|
||||
if (notification.isMediaNotification() && !(mIsMediaInQS
|
||||
&& MediaDataManagerKt.isMediaNotification(sbn))) {
|
||||
MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
|
||||
packageContext);
|
||||
processor.processNotification(notification, recoveredBuilder);
|
||||
}
|
||||
if (mEntry.getRanking().isConversation()) {
|
||||
mConversationProcessor.processNotification(mEntry, recoveredBuilder);
|
||||
}
|
||||
|
||||
@@ -16,341 +16,26 @@
|
||||
|
||||
package com.android.systemui.statusbar.notification.row.wrapper;
|
||||
|
||||
import static com.android.systemui.Dependency.MAIN_HANDLER;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.media.MediaMetadata;
|
||||
import android.media.session.MediaController;
|
||||
import android.media.session.MediaSession;
|
||||
import android.media.session.PlaybackState;
|
||||
import android.metrics.LogMaker;
|
||||
import android.os.Handler;
|
||||
import android.text.format.DateUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewStub;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.internal.widget.MediaNotificationView;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.statusbar.NotificationMediaManager;
|
||||
import com.android.systemui.statusbar.TransformableView;
|
||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* Wraps a notification containing a media template
|
||||
*/
|
||||
public class NotificationMediaTemplateViewWrapper extends NotificationTemplateViewWrapper {
|
||||
|
||||
private static final long PROGRESS_UPDATE_INTERVAL = 1000; // 1s
|
||||
private static final String COMPACT_MEDIA_TAG = "media";
|
||||
private final Handler mHandler = Dependency.get(MAIN_HANDLER);
|
||||
private Timer mSeekBarTimer;
|
||||
private View mActions;
|
||||
private SeekBar mSeekBar;
|
||||
private TextView mSeekBarElapsedTime;
|
||||
private TextView mSeekBarTotalTime;
|
||||
private long mDuration = 0;
|
||||
private MediaController mMediaController;
|
||||
private MediaMetadata mMediaMetadata;
|
||||
private NotificationMediaManager mMediaManager;
|
||||
private View mSeekBarView;
|
||||
private Context mContext;
|
||||
private MetricsLogger mMetricsLogger;
|
||||
private boolean mIsViewVisible;
|
||||
|
||||
@VisibleForTesting
|
||||
protected SeekBar.OnSeekBarChangeListener mSeekListener =
|
||||
new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
if (mMediaController != null) {
|
||||
mMediaController.getTransportControls().seekTo(mSeekBar.getProgress());
|
||||
mMetricsLogger.write(newLog(MetricsEvent.TYPE_UPDATE));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private MediaNotificationView.VisibilityChangeListener mVisibilityListener =
|
||||
new MediaNotificationView.VisibilityChangeListener() {
|
||||
@Override
|
||||
public void onAggregatedVisibilityChanged(boolean isVisible) {
|
||||
mIsViewVisible = isVisible;
|
||||
if (isVisible && mMediaController != null) {
|
||||
// Restart timer if we're currently playing and didn't already have one going
|
||||
PlaybackState state = mMediaController.getPlaybackState();
|
||||
if (state != null && state.getState() == PlaybackState.STATE_PLAYING
|
||||
&& mSeekBarTimer == null && mSeekBarView != null
|
||||
&& mSeekBarView.getVisibility() != View.GONE) {
|
||||
startTimer();
|
||||
}
|
||||
} else {
|
||||
clearTimer();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private View.OnAttachStateChangeListener mAttachStateListener =
|
||||
new View.OnAttachStateChangeListener() {
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View v) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View v) {
|
||||
mIsViewVisible = false;
|
||||
}
|
||||
};
|
||||
|
||||
private MediaController.Callback mMediaCallback = new MediaController.Callback() {
|
||||
@Override
|
||||
public void onSessionDestroyed() {
|
||||
clearTimer();
|
||||
mMediaController.unregisterCallback(this);
|
||||
if (mView instanceof MediaNotificationView) {
|
||||
((MediaNotificationView) mView).removeVisibilityListener(mVisibilityListener);
|
||||
mView.removeOnAttachStateChangeListener(mAttachStateListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackStateChanged(@Nullable PlaybackState state) {
|
||||
if (state == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.getState() != PlaybackState.STATE_PLAYING) {
|
||||
// Update the UI once, in case playback info changed while we were paused
|
||||
updatePlaybackUi(state);
|
||||
clearTimer();
|
||||
} else if (mSeekBarTimer == null && mSeekBarView != null
|
||||
&& mSeekBarView.getVisibility() != View.GONE) {
|
||||
startTimer();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMetadataChanged(@Nullable MediaMetadata metadata) {
|
||||
if (mMediaMetadata == null || !mMediaMetadata.equals(metadata)) {
|
||||
mMediaMetadata = metadata;
|
||||
updateDuration();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protected NotificationMediaTemplateViewWrapper(Context ctx, View view,
|
||||
ExpandableNotificationRow row) {
|
||||
super(ctx, view, row);
|
||||
mContext = ctx;
|
||||
mMediaManager = Dependency.get(NotificationMediaManager.class);
|
||||
mMetricsLogger = Dependency.get(MetricsLogger.class);
|
||||
}
|
||||
|
||||
private void resolveViews() {
|
||||
mActions = mView.findViewById(com.android.internal.R.id.media_actions);
|
||||
mIsViewVisible = mView.isShown();
|
||||
|
||||
final MediaSession.Token token = mRow.getEntry().getSbn().getNotification().extras
|
||||
.getParcelable(Notification.EXTRA_MEDIA_SESSION);
|
||||
|
||||
boolean showCompactSeekbar = mMediaManager.getShowCompactMediaSeekbar();
|
||||
if (token == null || (COMPACT_MEDIA_TAG.equals(mView.getTag()) && !showCompactSeekbar)) {
|
||||
if (mSeekBarView != null) {
|
||||
mSeekBarView.setVisibility(View.GONE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for existing media controller and clean up / create as necessary
|
||||
boolean shouldUpdateListeners = false;
|
||||
if (mMediaController == null || !mMediaController.getSessionToken().equals(token)) {
|
||||
if (mMediaController != null) {
|
||||
mMediaController.unregisterCallback(mMediaCallback);
|
||||
}
|
||||
mMediaController = new MediaController(mContext, token);
|
||||
shouldUpdateListeners = true;
|
||||
}
|
||||
|
||||
mMediaMetadata = mMediaController.getMetadata();
|
||||
if (mMediaMetadata != null) {
|
||||
long duration = mMediaMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
|
||||
if (duration <= 0) {
|
||||
// Don't include the seekbar if this is a livestream
|
||||
if (mSeekBarView != null && mSeekBarView.getVisibility() != View.GONE) {
|
||||
mSeekBarView.setVisibility(View.GONE);
|
||||
mMetricsLogger.write(newLog(MetricsEvent.TYPE_CLOSE));
|
||||
clearTimer();
|
||||
} else if (mSeekBarView == null && shouldUpdateListeners) {
|
||||
// Only log if the controller changed, otherwise we would log multiple times for
|
||||
// the same notification when user pauses/resumes
|
||||
mMetricsLogger.write(newLog(MetricsEvent.TYPE_CLOSE));
|
||||
}
|
||||
return;
|
||||
} else if (mSeekBarView != null && mSeekBarView.getVisibility() == View.GONE) {
|
||||
// Otherwise, make sure the seekbar is visible
|
||||
mSeekBarView.setVisibility(View.VISIBLE);
|
||||
mMetricsLogger.write(newLog(MetricsEvent.TYPE_OPEN));
|
||||
updateDuration();
|
||||
startTimer();
|
||||
}
|
||||
}
|
||||
|
||||
// Inflate the seekbar template
|
||||
ViewStub stub = mView.findViewById(R.id.notification_media_seekbar_container);
|
||||
if (stub instanceof ViewStub) {
|
||||
LayoutInflater layoutInflater = LayoutInflater.from(stub.getContext());
|
||||
stub.setLayoutInflater(layoutInflater);
|
||||
stub.setLayoutResource(R.layout.notification_material_media_seekbar);
|
||||
mSeekBarView = stub.inflate();
|
||||
mMetricsLogger.write(newLog(MetricsEvent.TYPE_OPEN));
|
||||
|
||||
mSeekBar = mSeekBarView.findViewById(R.id.notification_media_progress_bar);
|
||||
mSeekBar.setOnSeekBarChangeListener(mSeekListener);
|
||||
|
||||
mSeekBarElapsedTime = mSeekBarView.findViewById(R.id.notification_media_elapsed_time);
|
||||
mSeekBarTotalTime = mSeekBarView.findViewById(R.id.notification_media_total_time);
|
||||
|
||||
shouldUpdateListeners = true;
|
||||
}
|
||||
|
||||
if (shouldUpdateListeners) {
|
||||
if (mView instanceof MediaNotificationView) {
|
||||
MediaNotificationView mediaView = (MediaNotificationView) mView;
|
||||
mediaView.addVisibilityListener(mVisibilityListener);
|
||||
mView.addOnAttachStateChangeListener(mAttachStateListener);
|
||||
}
|
||||
|
||||
if (mSeekBarTimer == null) {
|
||||
if (mMediaController != null && canSeekMedia(mMediaController.getPlaybackState())) {
|
||||
// Log initial state, since it will not be updated
|
||||
mMetricsLogger.write(newLog(MetricsEvent.TYPE_DETAIL, 1));
|
||||
} else {
|
||||
setScrubberVisible(false);
|
||||
}
|
||||
updateDuration();
|
||||
startTimer();
|
||||
mMediaController.registerCallback(mMediaCallback);
|
||||
}
|
||||
}
|
||||
updateSeekBarTint(mSeekBarView);
|
||||
}
|
||||
|
||||
private void startTimer() {
|
||||
clearTimer();
|
||||
if (mIsViewVisible) {
|
||||
mSeekBarTimer = new Timer(true /* isDaemon */);
|
||||
mSeekBarTimer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
mHandler.post(mOnUpdateTimerTick);
|
||||
}
|
||||
}, 0, PROGRESS_UPDATE_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearTimer() {
|
||||
if (mSeekBarTimer != null) {
|
||||
mSeekBarTimer.cancel();
|
||||
mSeekBarTimer.purge();
|
||||
mSeekBarTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoved() {
|
||||
clearTimer();
|
||||
if (mMediaController != null) {
|
||||
mMediaController.unregisterCallback(mMediaCallback);
|
||||
}
|
||||
if (mView instanceof MediaNotificationView) {
|
||||
((MediaNotificationView) mView).removeVisibilityListener(mVisibilityListener);
|
||||
mView.removeOnAttachStateChangeListener(mAttachStateListener);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canSeekMedia(@Nullable PlaybackState state) {
|
||||
if (state == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long actions = state.getActions();
|
||||
return ((actions & PlaybackState.ACTION_SEEK_TO) != 0);
|
||||
}
|
||||
|
||||
private void setScrubberVisible(boolean isVisible) {
|
||||
if (mSeekBar == null || mSeekBar.isEnabled() == isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
mSeekBar.getThumb().setAlpha(isVisible ? 255 : 0);
|
||||
mSeekBar.setEnabled(isVisible);
|
||||
mMetricsLogger.write(newLog(MetricsEvent.TYPE_DETAIL, isVisible ? 1 : 0));
|
||||
}
|
||||
|
||||
private void updateDuration() {
|
||||
if (mMediaMetadata != null && mSeekBar != null) {
|
||||
long duration = mMediaMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
|
||||
if (mDuration != duration) {
|
||||
mDuration = duration;
|
||||
mSeekBar.setMax((int) mDuration);
|
||||
mSeekBarTotalTime.setText(millisecondsToTimeString(duration));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final Runnable mOnUpdateTimerTick = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mMediaController != null && mSeekBar != null) {
|
||||
PlaybackState playbackState = mMediaController.getPlaybackState();
|
||||
if (playbackState != null) {
|
||||
updatePlaybackUi(playbackState);
|
||||
} else {
|
||||
clearTimer();
|
||||
}
|
||||
} else {
|
||||
clearTimer();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void updatePlaybackUi(PlaybackState state) {
|
||||
if (mSeekBar == null || mSeekBarElapsedTime == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
long position = state.getPosition();
|
||||
mSeekBar.setProgress((int) position);
|
||||
|
||||
mSeekBarElapsedTime.setText(millisecondsToTimeString(position));
|
||||
|
||||
// Update scrubber in case available actions have changed
|
||||
setScrubberVisible(canSeekMedia(state));
|
||||
}
|
||||
|
||||
private String millisecondsToTimeString(long milliseconds) {
|
||||
long seconds = milliseconds / 1000;
|
||||
String text = DateUtils.formatElapsedTime(seconds);
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -361,28 +46,6 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi
|
||||
super.onContentUpdated(row);
|
||||
}
|
||||
|
||||
private void updateSeekBarTint(View seekBarContainer) {
|
||||
if (seekBarContainer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.getNotificationHeader() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int tintColor = getOriginalIconColor();
|
||||
mSeekBarElapsedTime.setTextColor(tintColor);
|
||||
mSeekBarTotalTime.setTextColor(tintColor);
|
||||
mSeekBarTotalTime.setShadowLayer(1.5f, 1.5f, 1.5f, mBackgroundColor);
|
||||
|
||||
ColorStateList tintList = ColorStateList.valueOf(tintColor);
|
||||
mSeekBar.setThumbTintList(tintList);
|
||||
tintList = tintList.withAlpha(192); // 75%
|
||||
mSeekBar.setProgressTintList(tintList);
|
||||
tintList = tintList.withAlpha(128); // 50%
|
||||
mSeekBar.setProgressBackgroundTintList(tintList);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateTransformedTypes() {
|
||||
// This also clears the existing types
|
||||
@@ -393,37 +56,8 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDimmable() {
|
||||
return getCustomBackgroundColor() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an initialized LogMaker for logging changes to the seekbar
|
||||
* @return new LogMaker
|
||||
*/
|
||||
private LogMaker newLog(int event) {
|
||||
String packageName = mRow.getEntry().getSbn().getPackageName();
|
||||
|
||||
return new LogMaker(MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR)
|
||||
.setType(event)
|
||||
.setPackageName(packageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an initialized LogMaker for logging changes with subtypes
|
||||
* @return new LogMaker
|
||||
*/
|
||||
private LogMaker newLog(int event, int subtype) {
|
||||
String packageName = mRow.getEntry().getSbn().getPackageName();
|
||||
return new LogMaker(MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR)
|
||||
.setType(event)
|
||||
.setSubtype(subtype)
|
||||
.setPackageName(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,31 +18,18 @@ package com.android.systemui.statusbar.notification;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Notification;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import androidx.palette.graphics.Palette;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.tests.R;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -58,17 +45,8 @@ public class MediaNotificationProcessorTest extends SysuiTestCase {
|
||||
*/
|
||||
private static final int COLOR_TOLERANCE = 8;
|
||||
|
||||
private MediaNotificationProcessor mProcessor;
|
||||
private Bitmap mBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
|
||||
private ImageGradientColorizer mColorizer;
|
||||
@Nullable private Bitmap mArtwork;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mColorizer = spy(new TestableColorizer(mBitmap));
|
||||
mProcessor = new MediaNotificationProcessor(getContext(), getContext(), mColorizer);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if (mArtwork != null) {
|
||||
@@ -77,53 +55,6 @@ public class MediaNotificationProcessorTest extends SysuiTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testColorizedWithLargeIcon() {
|
||||
Notification.Builder builder = new Notification.Builder(getContext()).setSmallIcon(
|
||||
R.drawable.ic_person)
|
||||
.setContentTitle("Title")
|
||||
.setLargeIcon(mBitmap)
|
||||
.setContentText("Text");
|
||||
Notification notification = builder.build();
|
||||
mProcessor.processNotification(notification, builder);
|
||||
verify(mColorizer).colorize(any(), anyInt(), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotColorizedWithoutLargeIcon() {
|
||||
Notification.Builder builder = new Notification.Builder(getContext()).setSmallIcon(
|
||||
R.drawable.ic_person)
|
||||
.setContentTitle("Title")
|
||||
.setContentText("Text");
|
||||
Notification notification = builder.build();
|
||||
mProcessor.processNotification(notification, builder);
|
||||
verifyZeroInteractions(mColorizer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoteViewsReset() {
|
||||
Notification.Builder builder = new Notification.Builder(getContext()).setSmallIcon(
|
||||
R.drawable.ic_person)
|
||||
.setContentTitle("Title")
|
||||
.setStyle(new Notification.MediaStyle())
|
||||
.setLargeIcon(mBitmap)
|
||||
.setContentText("Text");
|
||||
Notification notification = builder.build();
|
||||
RemoteViews remoteViews = new RemoteViews(getContext().getPackageName(),
|
||||
R.layout.custom_view_dark);
|
||||
notification.contentView = remoteViews;
|
||||
notification.bigContentView = remoteViews;
|
||||
notification.headsUpContentView = remoteViews;
|
||||
mProcessor.processNotification(notification, builder);
|
||||
verify(mColorizer).colorize(any(), anyInt(), anyBoolean());
|
||||
RemoteViews contentView = builder.createContentView();
|
||||
assertNotSame(contentView, remoteViews);
|
||||
contentView = builder.createBigContentView();
|
||||
assertNotSame(contentView, remoteViews);
|
||||
contentView = builder.createHeadsUpContentView();
|
||||
assertNotSame(contentView, remoteViews);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findBackgroundSwatch_white() {
|
||||
// Given artwork that is completely white.
|
||||
@@ -153,17 +84,4 @@ public class MediaNotificationProcessorTest extends SysuiTestCase {
|
||||
assertThat((float) Color.green(expected)).isWithin(COLOR_TOLERANCE).of(Color.green(actual));
|
||||
assertThat((float) Color.blue(expected)).isWithin(COLOR_TOLERANCE).of(Color.blue(actual));
|
||||
}
|
||||
|
||||
public static class TestableColorizer extends ImageGradientColorizer {
|
||||
private final Bitmap mBitmap;
|
||||
|
||||
private TestableColorizer(Bitmap bitmap) {
|
||||
mBitmap = bitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap colorize(Drawable drawable, int backgroundColor, boolean isRtl) {
|
||||
return mBitmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.row.wrapper;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.media.MediaMetadata;
|
||||
import android.media.session.MediaSession;
|
||||
import android.media.session.PlaybackState;
|
||||
import android.provider.Settings;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.SeekBar;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWithLooper
|
||||
public class NotificationMediaTemplateViewWrapperTest extends SysuiTestCase {
|
||||
|
||||
private ExpandableNotificationRow mRow;
|
||||
private Notification mNotif;
|
||||
private View mView;
|
||||
private NotificationMediaTemplateViewWrapper mWrapper;
|
||||
|
||||
@Mock
|
||||
private MetricsLogger mMetricsLogger;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
allowTestableLooperAsMainThread();
|
||||
|
||||
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
|
||||
|
||||
// These tests are for regular media style notifications, not controls in quick settings
|
||||
Settings.System.putInt(mContext.getContentResolver(), "qs_media_player", 0);
|
||||
}
|
||||
|
||||
private void makeTestNotification(long duration, boolean allowSeeking) throws Exception {
|
||||
Notification.Builder builder = new Notification.Builder(mContext)
|
||||
.setSmallIcon(R.drawable.ic_person)
|
||||
.setContentTitle("Title")
|
||||
.setContentText("Text");
|
||||
|
||||
MediaMetadata metadata = new MediaMetadata.Builder()
|
||||
.putLong(MediaMetadata.METADATA_KEY_DURATION, duration)
|
||||
.build();
|
||||
MediaSession session = new MediaSession(mContext, "TEST_CHANNEL");
|
||||
session.setMetadata(metadata);
|
||||
|
||||
PlaybackState playbackState = new PlaybackState.Builder()
|
||||
.setActions(allowSeeking ? PlaybackState.ACTION_SEEK_TO : 0)
|
||||
.build();
|
||||
|
||||
session.setPlaybackState(playbackState);
|
||||
|
||||
builder.setStyle(new Notification.MediaStyle()
|
||||
.setMediaSession(session.getSessionToken())
|
||||
);
|
||||
|
||||
mNotif = builder.build();
|
||||
assertTrue(mNotif.hasMediaSession());
|
||||
|
||||
NotificationTestHelper helper = new NotificationTestHelper(
|
||||
mContext,
|
||||
mDependency,
|
||||
TestableLooper.get(this));
|
||||
mRow = helper.createRow(mNotif);
|
||||
|
||||
RemoteViews views = new RemoteViews(mContext.getPackageName(),
|
||||
com.android.internal.R.layout.notification_template_material_big_media);
|
||||
mView = views.apply(mContext, null);
|
||||
mWrapper = new NotificationMediaTemplateViewWrapper(mContext,
|
||||
mView, mRow);
|
||||
mWrapper.onContentUpdated(mRow);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogging_NoSeekbar() throws Exception {
|
||||
// Media sessions with duration <= 0 should not include a seekbar
|
||||
makeTestNotification(0, false);
|
||||
|
||||
verify(mMetricsLogger).write(argThat(logMaker ->
|
||||
logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
|
||||
&& logMaker.getType() == MetricsEvent.TYPE_CLOSE
|
||||
));
|
||||
|
||||
verify(mMetricsLogger, times(0)).write(argThat(logMaker ->
|
||||
logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
|
||||
&& logMaker.getType() == MetricsEvent.TYPE_OPEN
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogging_HasSeekbarNoScrubber() throws Exception {
|
||||
// Media sessions that do not support seeking should have a seekbar, but no scrubber
|
||||
makeTestNotification(1000, false);
|
||||
|
||||
verify(mMetricsLogger).write(argThat(logMaker ->
|
||||
logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
|
||||
&& logMaker.getType() == MetricsEvent.TYPE_OPEN
|
||||
));
|
||||
|
||||
// Ensure the callback runs at least once
|
||||
mWrapper.mOnUpdateTimerTick.run();
|
||||
|
||||
verify(mMetricsLogger).write(argThat(logMaker ->
|
||||
logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
|
||||
&& logMaker.getType() == MetricsEvent.TYPE_DETAIL
|
||||
&& logMaker.getSubtype() == 0
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogging_HasSeekbarAndScrubber() throws Exception {
|
||||
makeTestNotification(1000, true);
|
||||
|
||||
verify(mMetricsLogger).write(argThat(logMaker ->
|
||||
logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
|
||||
&& logMaker.getType() == MetricsEvent.TYPE_OPEN
|
||||
));
|
||||
|
||||
verify(mMetricsLogger).write(argThat(logMaker ->
|
||||
logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
|
||||
&& logMaker.getType() == MetricsEvent.TYPE_DETAIL
|
||||
&& logMaker.getSubtype() == 1
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogging_UpdateSeekbar() throws Exception {
|
||||
makeTestNotification(1000, true);
|
||||
|
||||
SeekBar seekbar = mView.findViewById(
|
||||
com.android.internal.R.id.notification_media_progress_bar);
|
||||
assertTrue(seekbar != null);
|
||||
|
||||
mWrapper.mSeekListener.onStopTrackingTouch(seekbar);
|
||||
|
||||
verify(mMetricsLogger).write(argThat(logMaker ->
|
||||
logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
|
||||
&& logMaker.getType() == MetricsEvent.TYPE_UPDATE));
|
||||
}
|
||||
}
|
||||
@@ -31,9 +31,7 @@ import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.session.MediaSession;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
@@ -73,7 +71,6 @@ public class NotificationComparatorTest extends UiServiceTestCase {
|
||||
private NotificationRecord mRecordMinCallNonInterruptive;
|
||||
private NotificationRecord mRecordMinCall;
|
||||
private NotificationRecord mRecordHighCall;
|
||||
private NotificationRecord mRecordDefaultMedia;
|
||||
private NotificationRecord mRecordEmail;
|
||||
private NotificationRecord mRecordInlineReply;
|
||||
private NotificationRecord mRecordSms;
|
||||
@@ -139,15 +136,6 @@ public class NotificationComparatorTest extends UiServiceTestCase {
|
||||
new UserHandle(userId), "", 1999), getDefaultChannel());
|
||||
mRecordHighCall.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
|
||||
|
||||
Notification n3 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
|
||||
.setStyle(new Notification.MediaStyle()
|
||||
.setMediaSession(new MediaSession.Token(Process.myUid(), null)))
|
||||
.build();
|
||||
mRecordDefaultMedia = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
|
||||
pkg2, 1, "media", uid2, uid2, n3, new UserHandle(userId),
|
||||
"", 1499), getDefaultChannel());
|
||||
mRecordDefaultMedia.setSystemImportance(NotificationManager.IMPORTANCE_DEFAULT);
|
||||
|
||||
Notification n4 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
|
||||
.setStyle(new Notification.MessagingStyle("sender!")).build();
|
||||
mRecordInlineReply = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
|
||||
@@ -218,7 +206,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
|
||||
.setStyle(new Notification.MediaStyle())
|
||||
.build();
|
||||
mNoMediaSessionMedia = new NotificationRecord(mContext, new StatusBarNotification(
|
||||
pkg2, pkg2, 1, "cheater", uid2, uid2, n12, new UserHandle(userId),
|
||||
pkg2, pkg2, 1, "media", uid2, uid2, n12, new UserHandle(userId),
|
||||
"", 9258), getDefaultChannel());
|
||||
mNoMediaSessionMedia.setSystemImportance(NotificationManager.IMPORTANCE_DEFAULT);
|
||||
|
||||
@@ -247,7 +235,6 @@ public class NotificationComparatorTest extends UiServiceTestCase {
|
||||
final List<NotificationRecord> expected = new ArrayList<>();
|
||||
expected.add(mRecordColorizedCall);
|
||||
expected.add(mRecordColorized);
|
||||
expected.add(mRecordDefaultMedia);
|
||||
expected.add(mRecordHighCall);
|
||||
expected.add(mRecordInlineReply);
|
||||
if (mRecordSms != null) {
|
||||
|
||||
Reference in New Issue
Block a user