Loading core/java/android/app/Notification.java +10 −7 Original line number Diff line number Diff line Loading @@ -6434,7 +6434,7 @@ public class Notification implements Parcelable public RemoteViews makeContentView(boolean increasedHeight) { mBuilder.mOriginalActions = mBuilder.mActions; mBuilder.mActions = new ArrayList<>(); RemoteViews remoteViews = makeBigContentView(true /* showRightIcon */); RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */); mBuilder.mActions = mBuilder.mOriginalActions; mBuilder.mOriginalActions = null; return remoteViews; Loading Loading @@ -6469,11 +6469,11 @@ public class Notification implements Parcelable */ @Override public RemoteViews makeBigContentView() { return makeBigContentView(false /* showRightIcon */); return makeMessagingView(false /* isCollapsed */); } @NonNull private RemoteViews makeBigContentView(boolean showRightIcon) { private RemoteViews makeMessagingView(boolean isCollapsed) { CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle) ? super.mBigContentTitle : mConversationTitle; Loading @@ -6484,21 +6484,24 @@ public class Notification implements Parcelable nameReplacement = conversationTitle; conversationTitle = null; } boolean hideLargeIcon = !isCollapsed || isOneToOne; RemoteViews contentView = mBuilder.applyStandardTemplateWithActions( mBuilder.getMessagingLayoutResource(), mBuilder.mParams.reset().hasProgress(false).title(conversationTitle).text(null) .hideLargeIcon(!showRightIcon || isOneToOne) .hideLargeIcon(hideLargeIcon) .headerTextSecondary(conversationTitle) .alwaysShowReply(showRightIcon)); .alwaysShowReply(isCollapsed)); addExtras(mBuilder.mN.extras); // also update the end margin if there is an image int endMargin = R.dimen.notification_content_margin_end; if (mBuilder.mN.hasLargeIcon() && showRightIcon) { if (isCollapsed) { endMargin = R.dimen.notification_content_plus_picture_margin_end; } contentView.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin); contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor", mBuilder.resolveContrastColor()); contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsCollapsed", isCollapsed); contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon", mBuilder.mN.mLargeIcon); contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement", Loading Loading @@ -6565,7 +6568,7 @@ public class Notification implements Parcelable */ @Override public RemoteViews makeHeadsUpContentView(boolean increasedHeight) { RemoteViews remoteViews = makeBigContentView(true /* showRightIcon */); RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */); remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1); return remoteViews; } Loading core/java/com/android/internal/widget/LocalImageResolver.java 0 → 100644 +76 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.internal.widget; import android.annotation.Nullable; import android.app.Notification; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import java.io.IOException; import java.io.InputStream; /** * A class to extract Bitmaps from a MessagingStyle message. */ public class LocalImageResolver { private static final int MAX_SAFE_ICON_SIZE_PX = 480; @Nullable public static Drawable resolveImage(Uri uri, Context context) throws IOException { BitmapFactory.Options onlyBoundsOptions = getBoundsOptionsForImage(uri, context); if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) { return null; } int originalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth; double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX) ? (originalSize / MAX_SAFE_ICON_SIZE_PX) : 1.0; BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio); InputStream input = context.getContentResolver().openInputStream(uri); Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions); input.close(); return new BitmapDrawable(context.getResources(), bitmap); } private static BitmapFactory.Options getBoundsOptionsForImage(Uri uri, Context context) throws IOException { InputStream input = context.getContentResolver().openInputStream(uri); BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options(); onlyBoundsOptions.inJustDecodeBounds = true; BitmapFactory.decodeStream(input, null, onlyBoundsOptions); input.close(); return onlyBoundsOptions; } private static int getPowerOfTwoForSampleRatio(double ratio) { int k = Integer.highestOneBit((int) Math.floor(ratio)); return Math.max(1, k); } } core/java/com/android/internal/widget/MessagingGroup.java +133 −28 Original line number Diff line number Diff line Loading @@ -22,9 +22,12 @@ import android.annotation.Nullable; import android.annotation.StyleRes; import android.app.Notification; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Icon; import android.text.TextUtils; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Pools; import android.view.LayoutInflater; import android.view.View; Loading Loading @@ -61,6 +64,11 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou private boolean mIsHidingAnimated; private boolean mNeedsGeneratedAvatar; private Notification.Person mSender; private boolean mAvatarsAtEnd; private ViewGroup mImageContainer; private MessagingImageMessage mIsolatedMessage; private boolean mTransformingImages; private Point mDisplaySize = new Point(); public MessagingGroup(@NonNull Context context) { super(context); Loading @@ -87,6 +95,35 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou mSenderName = findViewById(R.id.message_name); mSenderName.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR); mAvatarView = findViewById(R.id.message_icon); mImageContainer = findViewById(R.id.messaging_group_icon_container); DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); mDisplaySize.x = displayMetrics.widthPixels; mDisplaySize.y = displayMetrics.heightPixels; } public void updateClipRect() { // We want to clip to the senderName if it's available, otherwise our images will come // from a weird position Rect clipRect; if (mSenderName.getVisibility() != View.GONE && !mTransformingImages) { ViewGroup parent = (ViewGroup) mSenderName.getParent(); int top = getDistanceFromParent(mSenderName, parent) - getDistanceFromParent( mMessageContainer, parent) + mSenderName.getHeight(); clipRect = new Rect(0, top, mDisplaySize.x, mDisplaySize.y); } else { clipRect = null; } mMessageContainer.setClipBounds(clipRect); } private int getDistanceFromParent(View searchedView, ViewGroup parent) { int position = 0; View view = searchedView; while(view != parent) { position += view.getTop() + view.getTranslationY(); view = (View) view.getParent(); } return position; } public void setSender(Notification.Person sender, CharSequence nameOverride) { Loading Loading @@ -129,12 +166,14 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } public void removeMessage(MessagingMessage messagingMessage) { mMessageContainer.removeView(messagingMessage); ViewGroup messageParent = (ViewGroup) messagingMessage.getView().getParent(); messageParent.removeView(messagingMessage.getView()); Runnable recycleRunnable = () -> { mMessageContainer.removeTransientView(messagingMessage); messageParent.removeTransientView(messagingMessage.getView()); messagingMessage.recycle(); if (mMessageContainer.getChildCount() == 0 && mMessageContainer.getTransientViewCount() == 0) { && mMessageContainer.getTransientViewCount() == 0 && mImageContainer.getChildCount() == 0) { ViewParent parent = getParent(); if (parent instanceof ViewGroup) { ((ViewGroup) parent).removeView(MessagingGroup.this); Loading @@ -148,9 +187,10 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } }; if (isShown()) { mMessageContainer.addTransientView(messagingMessage, 0); performRemoveAnimation(messagingMessage, recycleRunnable); if (mMessageContainer.getChildCount() == 0) { messageParent.addTransientView(messagingMessage.getView(), 0); performRemoveAnimation(messagingMessage.getView(), recycleRunnable); if (mMessageContainer.getChildCount() == 0 && mImageContainer.getChildCount() == 0) { removeGroupAnimated(null); } } else { Loading @@ -160,12 +200,8 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } private void removeGroupAnimated(Runnable endAction) { MessagingPropertyAnimator.fadeOut(mAvatarView, null); MessagingPropertyAnimator.startLocalTranslationTo(mAvatarView, (int) (-getHeight() * 0.5f), MessagingLayout.FAST_OUT_LINEAR_IN); MessagingPropertyAnimator.fadeOut(mSenderName, null); MessagingPropertyAnimator.startLocalTranslationTo(mSenderName, (int) (-getHeight() * 0.5f), MessagingLayout.FAST_OUT_LINEAR_IN); performRemoveAnimation(mAvatarView, null); performRemoveAnimation(mSenderName, null); boolean endActionTriggered = false; for (int i = mMessageContainer.getChildCount() - 1; i >= 0; i--) { View child = mMessageContainer.getChildAt(i); Loading @@ -182,14 +218,17 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou performRemoveAnimation(child, childEndAction); endActionTriggered = true; } if (mIsolatedMessage != null) { performRemoveAnimation(mIsolatedMessage, !endActionTriggered ? endAction : null); endActionTriggered = true; } if (!endActionTriggered && endAction != null) { endAction.run(); } } public void performRemoveAnimation(View message, Runnable recycleRunnable) { MessagingPropertyAnimator.fadeOut(message, recycleRunnable); public void performRemoveAnimation(View message, Runnable endAction) { MessagingPropertyAnimator.fadeOut(message, endAction); MessagingPropertyAnimator.startLocalTranslationTo(message, (int) (-getHeight() * 0.5f), MessagingLayout.FAST_OUT_LINEAR_IN); } Loading Loading @@ -222,6 +261,9 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } } } if (mMessageContainer.getChildCount() == 0 && mIsolatedMessage != null) { return mIsolatedMessage.getMeasuredType(); } return MEASURED_NORMAL; } Loading @@ -234,6 +276,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou result += ((MessagingLinearLayout.MessagingChild) child).getConsumedLines(); } } result = mIsolatedMessage != null ? Math.max(result, 1) : result; // A group is usually taking up quite some space with the padding and the name, let's add 1 return result + 1; } Loading Loading @@ -289,26 +332,67 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou public void setMessages(List<MessagingMessage> group) { // Let's now make sure all children are added and in the correct order int textMessageIndex = 0; MessagingImageMessage isolatedMessage = null; for (int messageIndex = 0; messageIndex < group.size(); messageIndex++) { MessagingMessage message = group.get(messageIndex); message.setColor(mTextColor); if (message.getGroup() != this) { message.setMessagingGroup(this); ViewParent parent = mMessageContainer.getParent(); if (parent instanceof ViewGroup) { ((ViewGroup) parent).removeView(message); } mMessageContainer.addView(message, messageIndex); mAddedMessages.add(message); } if (messageIndex != mMessageContainer.indexOfChild(message)) { mMessageContainer.removeView(message); mMessageContainer.addView(message, messageIndex); boolean isImage = message instanceof MessagingImageMessage; if (mAvatarsAtEnd && isImage) { isolatedMessage = (MessagingImageMessage) message; } else { if (removeFromParentIfDifferent(message, mMessageContainer)) { ViewGroup.LayoutParams layoutParams = message.getView().getLayoutParams(); if (layoutParams != null && !(layoutParams instanceof MessagingLinearLayout.LayoutParams)) { message.getView().setLayoutParams( mMessageContainer.generateDefaultLayoutParams()); } mMessageContainer.addView(message.getView(), textMessageIndex); } if (isImage) { ((MessagingImageMessage) message).setIsolated(false); } // Let's sort them properly if (textMessageIndex != mMessageContainer.indexOfChild(message.getView())) { mMessageContainer.removeView(message.getView()); mMessageContainer.addView(message.getView(), textMessageIndex); } textMessageIndex++; } } message.setTextColor(mTextColor); if (isolatedMessage != null) { if (removeFromParentIfDifferent(isolatedMessage, mImageContainer)) { mImageContainer.removeAllViews(); mImageContainer.addView(isolatedMessage.getView()); } isolatedMessage.setIsolated(true); } else if (mIsolatedMessage != null) { mImageContainer.removeAllViews(); } mIsolatedMessage = isolatedMessage; mMessages = group; } /** * Remove the message from the parent if the parent isn't the one provided * @return whether the message was removed */ private boolean removeFromParentIfDifferent(MessagingMessage message, ViewGroup newParent) { ViewParent parent = message.getView().getParent(); if (parent != newParent) { if (parent instanceof ViewGroup) { ((ViewGroup) parent).removeView(message.getView()); } return true; } return false; } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); Loading @@ -317,13 +401,14 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou @Override public boolean onPreDraw() { for (MessagingMessage message : mAddedMessages) { if (!message.isShown()) { if (!message.getView().isShown()) { continue; } MessagingPropertyAnimator.fadeIn(message); MessagingPropertyAnimator.fadeIn(message.getView()); if (!mFirstLayout) { MessagingPropertyAnimator.startLocalTranslationFrom(message, message.getHeight(), MessagingLayout.LINEAR_OUT_SLOW_IN); MessagingPropertyAnimator.startLocalTranslationFrom(message.getView(), message.getView().getHeight(), MessagingLayout.LINEAR_OUT_SLOW_IN); } } mAddedMessages.clear(); Loading @@ -333,6 +418,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou }); } mFirstLayout = false; updateClipRect(); } /** Loading Loading @@ -372,6 +458,10 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou return mMessageContainer; } public MessagingImageMessage getIsolatedMessage() { return mIsolatedMessage; } public boolean needsGeneratedAvatar() { return mNeedsGeneratedAvatar; } Loading @@ -379,4 +469,19 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou public Notification.Person getSender() { return mSender; } public void setTransformingImages(boolean transformingImages) { mTransformingImages = transformingImages; } public void setDisplayAvatarsAtEnd(boolean atEnd) { if (mAvatarsAtEnd != atEnd) { mAvatarsAtEnd = atEnd; mImageContainer.setVisibility(atEnd ? View.VISIBLE : View.GONE); } } public List<MessagingMessage> getMessages() { return mMessages; } } core/java/com/android/internal/widget/MessagingImageMessage.java 0 → 100644 +273 −0 File added.Preview size limit exceeded, changes collapsed. Show changes core/java/com/android/internal/widget/MessagingLayout.java +7 −1 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ public class MessagingLayout extends FrameLayout { private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>(); private Notification.Person mUser; private CharSequence mNameReplacement; private boolean mIsCollapsed; public MessagingLayout(@NonNull Context context) { super(context); Loading Loading @@ -126,6 +127,11 @@ public class MessagingLayout extends FrameLayout { mNameReplacement = nameReplacement; } @RemotableViewMethod public void setIsCollapsed(boolean isCollapsed) { mIsCollapsed = isCollapsed; } @RemotableViewMethod public void setData(Bundle extras) { Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES); Loading Loading @@ -331,6 +337,7 @@ public class MessagingLayout extends FrameLayout { newGroup = MessagingGroup.createGroup(mMessagingLinearLayout); mAddedGroups.add(newGroup); } newGroup.setDisplayAvatarsAtEnd(mIsCollapsed); newGroup.setLayoutColor(mLayoutColor); Notification.Person sender = senders.get(groupIndex); CharSequence nameOverride = null; Loading Loading @@ -392,7 +399,6 @@ public class MessagingLayout extends FrameLayout { MessagingMessage message = findAndRemoveMatchingMessage(m); if (message == null) { message = MessagingMessage.createMessage(this, m); message.addOnLayoutChangeListener(MESSAGING_PROPERTY_ANIMATOR); } message.setIsHistoric(historic); result.add(message); Loading Loading
core/java/android/app/Notification.java +10 −7 Original line number Diff line number Diff line Loading @@ -6434,7 +6434,7 @@ public class Notification implements Parcelable public RemoteViews makeContentView(boolean increasedHeight) { mBuilder.mOriginalActions = mBuilder.mActions; mBuilder.mActions = new ArrayList<>(); RemoteViews remoteViews = makeBigContentView(true /* showRightIcon */); RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */); mBuilder.mActions = mBuilder.mOriginalActions; mBuilder.mOriginalActions = null; return remoteViews; Loading Loading @@ -6469,11 +6469,11 @@ public class Notification implements Parcelable */ @Override public RemoteViews makeBigContentView() { return makeBigContentView(false /* showRightIcon */); return makeMessagingView(false /* isCollapsed */); } @NonNull private RemoteViews makeBigContentView(boolean showRightIcon) { private RemoteViews makeMessagingView(boolean isCollapsed) { CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle) ? super.mBigContentTitle : mConversationTitle; Loading @@ -6484,21 +6484,24 @@ public class Notification implements Parcelable nameReplacement = conversationTitle; conversationTitle = null; } boolean hideLargeIcon = !isCollapsed || isOneToOne; RemoteViews contentView = mBuilder.applyStandardTemplateWithActions( mBuilder.getMessagingLayoutResource(), mBuilder.mParams.reset().hasProgress(false).title(conversationTitle).text(null) .hideLargeIcon(!showRightIcon || isOneToOne) .hideLargeIcon(hideLargeIcon) .headerTextSecondary(conversationTitle) .alwaysShowReply(showRightIcon)); .alwaysShowReply(isCollapsed)); addExtras(mBuilder.mN.extras); // also update the end margin if there is an image int endMargin = R.dimen.notification_content_margin_end; if (mBuilder.mN.hasLargeIcon() && showRightIcon) { if (isCollapsed) { endMargin = R.dimen.notification_content_plus_picture_margin_end; } contentView.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin); contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor", mBuilder.resolveContrastColor()); contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsCollapsed", isCollapsed); contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon", mBuilder.mN.mLargeIcon); contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement", Loading Loading @@ -6565,7 +6568,7 @@ public class Notification implements Parcelable */ @Override public RemoteViews makeHeadsUpContentView(boolean increasedHeight) { RemoteViews remoteViews = makeBigContentView(true /* showRightIcon */); RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */); remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1); return remoteViews; } Loading
core/java/com/android/internal/widget/LocalImageResolver.java 0 → 100644 +76 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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.internal.widget; import android.annotation.Nullable; import android.app.Notification; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import java.io.IOException; import java.io.InputStream; /** * A class to extract Bitmaps from a MessagingStyle message. */ public class LocalImageResolver { private static final int MAX_SAFE_ICON_SIZE_PX = 480; @Nullable public static Drawable resolveImage(Uri uri, Context context) throws IOException { BitmapFactory.Options onlyBoundsOptions = getBoundsOptionsForImage(uri, context); if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) { return null; } int originalSize = (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) ? onlyBoundsOptions.outHeight : onlyBoundsOptions.outWidth; double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX) ? (originalSize / MAX_SAFE_ICON_SIZE_PX) : 1.0; BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio); InputStream input = context.getContentResolver().openInputStream(uri); Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions); input.close(); return new BitmapDrawable(context.getResources(), bitmap); } private static BitmapFactory.Options getBoundsOptionsForImage(Uri uri, Context context) throws IOException { InputStream input = context.getContentResolver().openInputStream(uri); BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options(); onlyBoundsOptions.inJustDecodeBounds = true; BitmapFactory.decodeStream(input, null, onlyBoundsOptions); input.close(); return onlyBoundsOptions; } private static int getPowerOfTwoForSampleRatio(double ratio) { int k = Integer.highestOneBit((int) Math.floor(ratio)); return Math.max(1, k); } }
core/java/com/android/internal/widget/MessagingGroup.java +133 −28 Original line number Diff line number Diff line Loading @@ -22,9 +22,12 @@ import android.annotation.Nullable; import android.annotation.StyleRes; import android.app.Notification; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Icon; import android.text.TextUtils; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Pools; import android.view.LayoutInflater; import android.view.View; Loading Loading @@ -61,6 +64,11 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou private boolean mIsHidingAnimated; private boolean mNeedsGeneratedAvatar; private Notification.Person mSender; private boolean mAvatarsAtEnd; private ViewGroup mImageContainer; private MessagingImageMessage mIsolatedMessage; private boolean mTransformingImages; private Point mDisplaySize = new Point(); public MessagingGroup(@NonNull Context context) { super(context); Loading @@ -87,6 +95,35 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou mSenderName = findViewById(R.id.message_name); mSenderName.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR); mAvatarView = findViewById(R.id.message_icon); mImageContainer = findViewById(R.id.messaging_group_icon_container); DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); mDisplaySize.x = displayMetrics.widthPixels; mDisplaySize.y = displayMetrics.heightPixels; } public void updateClipRect() { // We want to clip to the senderName if it's available, otherwise our images will come // from a weird position Rect clipRect; if (mSenderName.getVisibility() != View.GONE && !mTransformingImages) { ViewGroup parent = (ViewGroup) mSenderName.getParent(); int top = getDistanceFromParent(mSenderName, parent) - getDistanceFromParent( mMessageContainer, parent) + mSenderName.getHeight(); clipRect = new Rect(0, top, mDisplaySize.x, mDisplaySize.y); } else { clipRect = null; } mMessageContainer.setClipBounds(clipRect); } private int getDistanceFromParent(View searchedView, ViewGroup parent) { int position = 0; View view = searchedView; while(view != parent) { position += view.getTop() + view.getTranslationY(); view = (View) view.getParent(); } return position; } public void setSender(Notification.Person sender, CharSequence nameOverride) { Loading Loading @@ -129,12 +166,14 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } public void removeMessage(MessagingMessage messagingMessage) { mMessageContainer.removeView(messagingMessage); ViewGroup messageParent = (ViewGroup) messagingMessage.getView().getParent(); messageParent.removeView(messagingMessage.getView()); Runnable recycleRunnable = () -> { mMessageContainer.removeTransientView(messagingMessage); messageParent.removeTransientView(messagingMessage.getView()); messagingMessage.recycle(); if (mMessageContainer.getChildCount() == 0 && mMessageContainer.getTransientViewCount() == 0) { && mMessageContainer.getTransientViewCount() == 0 && mImageContainer.getChildCount() == 0) { ViewParent parent = getParent(); if (parent instanceof ViewGroup) { ((ViewGroup) parent).removeView(MessagingGroup.this); Loading @@ -148,9 +187,10 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } }; if (isShown()) { mMessageContainer.addTransientView(messagingMessage, 0); performRemoveAnimation(messagingMessage, recycleRunnable); if (mMessageContainer.getChildCount() == 0) { messageParent.addTransientView(messagingMessage.getView(), 0); performRemoveAnimation(messagingMessage.getView(), recycleRunnable); if (mMessageContainer.getChildCount() == 0 && mImageContainer.getChildCount() == 0) { removeGroupAnimated(null); } } else { Loading @@ -160,12 +200,8 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } private void removeGroupAnimated(Runnable endAction) { MessagingPropertyAnimator.fadeOut(mAvatarView, null); MessagingPropertyAnimator.startLocalTranslationTo(mAvatarView, (int) (-getHeight() * 0.5f), MessagingLayout.FAST_OUT_LINEAR_IN); MessagingPropertyAnimator.fadeOut(mSenderName, null); MessagingPropertyAnimator.startLocalTranslationTo(mSenderName, (int) (-getHeight() * 0.5f), MessagingLayout.FAST_OUT_LINEAR_IN); performRemoveAnimation(mAvatarView, null); performRemoveAnimation(mSenderName, null); boolean endActionTriggered = false; for (int i = mMessageContainer.getChildCount() - 1; i >= 0; i--) { View child = mMessageContainer.getChildAt(i); Loading @@ -182,14 +218,17 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou performRemoveAnimation(child, childEndAction); endActionTriggered = true; } if (mIsolatedMessage != null) { performRemoveAnimation(mIsolatedMessage, !endActionTriggered ? endAction : null); endActionTriggered = true; } if (!endActionTriggered && endAction != null) { endAction.run(); } } public void performRemoveAnimation(View message, Runnable recycleRunnable) { MessagingPropertyAnimator.fadeOut(message, recycleRunnable); public void performRemoveAnimation(View message, Runnable endAction) { MessagingPropertyAnimator.fadeOut(message, endAction); MessagingPropertyAnimator.startLocalTranslationTo(message, (int) (-getHeight() * 0.5f), MessagingLayout.FAST_OUT_LINEAR_IN); } Loading Loading @@ -222,6 +261,9 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } } } if (mMessageContainer.getChildCount() == 0 && mIsolatedMessage != null) { return mIsolatedMessage.getMeasuredType(); } return MEASURED_NORMAL; } Loading @@ -234,6 +276,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou result += ((MessagingLinearLayout.MessagingChild) child).getConsumedLines(); } } result = mIsolatedMessage != null ? Math.max(result, 1) : result; // A group is usually taking up quite some space with the padding and the name, let's add 1 return result + 1; } Loading Loading @@ -289,26 +332,67 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou public void setMessages(List<MessagingMessage> group) { // Let's now make sure all children are added and in the correct order int textMessageIndex = 0; MessagingImageMessage isolatedMessage = null; for (int messageIndex = 0; messageIndex < group.size(); messageIndex++) { MessagingMessage message = group.get(messageIndex); message.setColor(mTextColor); if (message.getGroup() != this) { message.setMessagingGroup(this); ViewParent parent = mMessageContainer.getParent(); if (parent instanceof ViewGroup) { ((ViewGroup) parent).removeView(message); } mMessageContainer.addView(message, messageIndex); mAddedMessages.add(message); } if (messageIndex != mMessageContainer.indexOfChild(message)) { mMessageContainer.removeView(message); mMessageContainer.addView(message, messageIndex); boolean isImage = message instanceof MessagingImageMessage; if (mAvatarsAtEnd && isImage) { isolatedMessage = (MessagingImageMessage) message; } else { if (removeFromParentIfDifferent(message, mMessageContainer)) { ViewGroup.LayoutParams layoutParams = message.getView().getLayoutParams(); if (layoutParams != null && !(layoutParams instanceof MessagingLinearLayout.LayoutParams)) { message.getView().setLayoutParams( mMessageContainer.generateDefaultLayoutParams()); } mMessageContainer.addView(message.getView(), textMessageIndex); } if (isImage) { ((MessagingImageMessage) message).setIsolated(false); } // Let's sort them properly if (textMessageIndex != mMessageContainer.indexOfChild(message.getView())) { mMessageContainer.removeView(message.getView()); mMessageContainer.addView(message.getView(), textMessageIndex); } textMessageIndex++; } } message.setTextColor(mTextColor); if (isolatedMessage != null) { if (removeFromParentIfDifferent(isolatedMessage, mImageContainer)) { mImageContainer.removeAllViews(); mImageContainer.addView(isolatedMessage.getView()); } isolatedMessage.setIsolated(true); } else if (mIsolatedMessage != null) { mImageContainer.removeAllViews(); } mIsolatedMessage = isolatedMessage; mMessages = group; } /** * Remove the message from the parent if the parent isn't the one provided * @return whether the message was removed */ private boolean removeFromParentIfDifferent(MessagingMessage message, ViewGroup newParent) { ViewParent parent = message.getView().getParent(); if (parent != newParent) { if (parent instanceof ViewGroup) { ((ViewGroup) parent).removeView(message.getView()); } return true; } return false; } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); Loading @@ -317,13 +401,14 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou @Override public boolean onPreDraw() { for (MessagingMessage message : mAddedMessages) { if (!message.isShown()) { if (!message.getView().isShown()) { continue; } MessagingPropertyAnimator.fadeIn(message); MessagingPropertyAnimator.fadeIn(message.getView()); if (!mFirstLayout) { MessagingPropertyAnimator.startLocalTranslationFrom(message, message.getHeight(), MessagingLayout.LINEAR_OUT_SLOW_IN); MessagingPropertyAnimator.startLocalTranslationFrom(message.getView(), message.getView().getHeight(), MessagingLayout.LINEAR_OUT_SLOW_IN); } } mAddedMessages.clear(); Loading @@ -333,6 +418,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou }); } mFirstLayout = false; updateClipRect(); } /** Loading Loading @@ -372,6 +458,10 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou return mMessageContainer; } public MessagingImageMessage getIsolatedMessage() { return mIsolatedMessage; } public boolean needsGeneratedAvatar() { return mNeedsGeneratedAvatar; } Loading @@ -379,4 +469,19 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou public Notification.Person getSender() { return mSender; } public void setTransformingImages(boolean transformingImages) { mTransformingImages = transformingImages; } public void setDisplayAvatarsAtEnd(boolean atEnd) { if (mAvatarsAtEnd != atEnd) { mAvatarsAtEnd = atEnd; mImageContainer.setVisibility(atEnd ? View.VISIBLE : View.GONE); } } public List<MessagingMessage> getMessages() { return mMessages; } }
core/java/com/android/internal/widget/MessagingImageMessage.java 0 → 100644 +273 −0 File added.Preview size limit exceeded, changes collapsed. Show changes
core/java/com/android/internal/widget/MessagingLayout.java +7 −1 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ public class MessagingLayout extends FrameLayout { private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>(); private Notification.Person mUser; private CharSequence mNameReplacement; private boolean mIsCollapsed; public MessagingLayout(@NonNull Context context) { super(context); Loading Loading @@ -126,6 +127,11 @@ public class MessagingLayout extends FrameLayout { mNameReplacement = nameReplacement; } @RemotableViewMethod public void setIsCollapsed(boolean isCollapsed) { mIsCollapsed = isCollapsed; } @RemotableViewMethod public void setData(Bundle extras) { Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES); Loading Loading @@ -331,6 +337,7 @@ public class MessagingLayout extends FrameLayout { newGroup = MessagingGroup.createGroup(mMessagingLinearLayout); mAddedGroups.add(newGroup); } newGroup.setDisplayAvatarsAtEnd(mIsCollapsed); newGroup.setLayoutColor(mLayoutColor); Notification.Person sender = senders.get(groupIndex); CharSequence nameOverride = null; Loading Loading @@ -392,7 +399,6 @@ public class MessagingLayout extends FrameLayout { MessagingMessage message = findAndRemoveMatchingMessage(m); if (message == null) { message = MessagingMessage.createMessage(this, m); message.addOnLayoutChangeListener(MESSAGING_PROPERTY_ANIMATOR); } message.setIsHistoric(historic); result.add(message); Loading