Merge "Implemented support for inline images"
This commit is contained in:
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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",
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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) {
|
||||
@@ -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);
|
||||
@@ -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 {
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -222,6 +261,9 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mMessageContainer.getChildCount() == 0 && mIsolatedMessage != null) {
|
||||
return mIsolatedMessage.getMeasuredType();
|
||||
}
|
||||
return MEASURED_NORMAL;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
@@ -333,6 +418,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
|
||||
});
|
||||
}
|
||||
mFirstLayout = false;
|
||||
updateClipRect();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -372,6 +458,10 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
|
||||
return mMessageContainer;
|
||||
}
|
||||
|
||||
public MessagingImageMessage getIsolatedMessage() {
|
||||
return mIsolatedMessage;
|
||||
}
|
||||
|
||||
public boolean needsGeneratedAvatar() {
|
||||
return mNeedsGeneratedAvatar;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
273
core/java/com/android/internal/widget/MessagingImageMessage.java
Normal file
273
core/java/com/android/internal/widget/MessagingImageMessage.java
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* 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.AttrRes;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.StyleRes;
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Pools;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A message of a {@link MessagingLayout} that is an image.
|
||||
*/
|
||||
@RemoteViews.RemoteView
|
||||
public class MessagingImageMessage extends ImageView implements MessagingMessage {
|
||||
private static final String TAG = "MessagingImageMessage";
|
||||
private static Pools.SimplePool<MessagingImageMessage> sInstancePool
|
||||
= new Pools.SynchronizedPool<>(10);
|
||||
private final MessagingMessageState mState = new MessagingMessageState(this);
|
||||
private final int mMinImageHeight;
|
||||
private final Path mPath = new Path();
|
||||
private final int mImageRounding;
|
||||
private final int mMaxImageHeight;
|
||||
private final int mIsolatedSize;
|
||||
private final int mExtraSpacing;
|
||||
private Drawable mDrawable;
|
||||
private float mAspectRatio;
|
||||
private int mActualWidth;
|
||||
private int mActualHeight;
|
||||
private boolean mIsIsolated;
|
||||
|
||||
public MessagingImageMessage(@NonNull Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public MessagingImageMessage(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public MessagingImageMessage(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||
@AttrRes int defStyleAttr) {
|
||||
this(context, attrs, defStyleAttr, 0);
|
||||
}
|
||||
|
||||
public MessagingImageMessage(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||
@AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
mMinImageHeight = context.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.messaging_image_min_size);
|
||||
mMaxImageHeight = context.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.messaging_image_max_height);
|
||||
mImageRounding = context.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.messaging_image_rounding);
|
||||
mExtraSpacing = context.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.messaging_image_extra_spacing);
|
||||
setMaxHeight(mMaxImageHeight);
|
||||
mIsolatedSize = getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessagingMessageState getState() {
|
||||
return mState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setMessage(Notification.MessagingStyle.Message message) {
|
||||
MessagingMessage.super.setMessage(message);
|
||||
Drawable drawable;
|
||||
try {
|
||||
drawable = LocalImageResolver.resolveImage(message.getDataUri(), getContext());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
int intrinsicHeight = mDrawable.getIntrinsicHeight();
|
||||
if (intrinsicHeight == 0) {
|
||||
Log.w(TAG, "Drawable with 0 intrinsic height was returned");
|
||||
return false;
|
||||
}
|
||||
mDrawable = drawable;
|
||||
mAspectRatio = ((float) mDrawable.getIntrinsicWidth()) / intrinsicHeight;
|
||||
setImageDrawable(drawable);
|
||||
setContentDescription(message.getText());
|
||||
return true;
|
||||
}
|
||||
|
||||
static MessagingMessage createMessage(MessagingLayout layout,
|
||||
Notification.MessagingStyle.Message m) {
|
||||
MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout();
|
||||
MessagingImageMessage createdMessage = sInstancePool.acquire();
|
||||
if (createdMessage == null) {
|
||||
createdMessage = (MessagingImageMessage) LayoutInflater.from(
|
||||
layout.getContext()).inflate(
|
||||
R.layout.notification_template_messaging_image_message,
|
||||
messagingLinearLayout,
|
||||
false);
|
||||
createdMessage.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR);
|
||||
}
|
||||
boolean created = createdMessage.setMessage(m);
|
||||
if (!created) {
|
||||
createdMessage.recycle();
|
||||
return MessagingTextMessage.createMessage(layout, m);
|
||||
}
|
||||
return createdMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
canvas.save();
|
||||
canvas.clipPath(getRoundedRectPath());
|
||||
int width = (int) Math.max(getActualWidth(), getActualHeight() * mAspectRatio);
|
||||
int height = (int) (width / mAspectRatio);
|
||||
int left = (int) ((getActualWidth() - width) / 2.0f);
|
||||
mDrawable.setBounds(left, 0, left + width, height);
|
||||
mDrawable.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
public Path getRoundedRectPath() {
|
||||
int left = 0;
|
||||
int right = getActualWidth();
|
||||
int top = 0;
|
||||
int bottom = getActualHeight();
|
||||
mPath.reset();
|
||||
int width = right - left;
|
||||
float roundnessX = mImageRounding;
|
||||
float roundnessY = mImageRounding;
|
||||
roundnessX = Math.min(width / 2, roundnessX);
|
||||
roundnessY = Math.min((bottom - top) / 2, roundnessY);
|
||||
mPath.moveTo(left, top + roundnessY);
|
||||
mPath.quadTo(left, top, left + roundnessX, top);
|
||||
mPath.lineTo(right - roundnessX, top);
|
||||
mPath.quadTo(right, top, right, top + roundnessY);
|
||||
mPath.lineTo(right, bottom - roundnessY);
|
||||
mPath.quadTo(right, bottom, right - roundnessX, bottom);
|
||||
mPath.lineTo(left + roundnessX, bottom);
|
||||
mPath.quadTo(left, bottom, left, bottom - roundnessY);
|
||||
mPath.close();
|
||||
return mPath;
|
||||
}
|
||||
|
||||
public void recycle() {
|
||||
MessagingMessage.super.recycle();
|
||||
setAlpha(1.0f);
|
||||
setTranslationY(0);
|
||||
setImageBitmap(null);
|
||||
mDrawable = null;
|
||||
sInstancePool.release(this);
|
||||
}
|
||||
|
||||
public static void dropCache() {
|
||||
sInstancePool = new Pools.SynchronizedPool<>(10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMeasuredType() {
|
||||
int measuredHeight = getMeasuredHeight();
|
||||
int minImageHeight;
|
||||
if (mIsIsolated) {
|
||||
minImageHeight = mIsolatedSize;
|
||||
} else {
|
||||
minImageHeight = mMinImageHeight;
|
||||
}
|
||||
boolean measuredTooSmall = measuredHeight < minImageHeight
|
||||
&& measuredHeight != mDrawable.getIntrinsicHeight();
|
||||
if (measuredTooSmall) {
|
||||
return MEASURED_TOO_SMALL;
|
||||
} else {
|
||||
if (!mIsIsolated && measuredHeight != mDrawable.getIntrinsicHeight()) {
|
||||
return MEASURED_SHORTENED;
|
||||
} else {
|
||||
return MEASURED_NORMAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxDisplayedLines(int lines) {
|
||||
// Nothing to do, this should be handled automatically.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
if (mIsIsolated) {
|
||||
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
|
||||
MeasureSpec.getSize(heightMeasureSpec));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
// TODO: ensure that this isn't called when transforming
|
||||
setActualWidth(getStaticWidth());
|
||||
setActualHeight(getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConsumedLines() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
public void setActualWidth(int actualWidth) {
|
||||
mActualWidth = actualWidth;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public int getActualWidth() {
|
||||
return mActualWidth;
|
||||
}
|
||||
|
||||
public void setActualHeight(int actualHeight) {
|
||||
mActualHeight = actualHeight;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public int getActualHeight() {
|
||||
return mActualHeight;
|
||||
}
|
||||
|
||||
public int getStaticWidth() {
|
||||
if (mIsIsolated) {
|
||||
return getWidth();
|
||||
}
|
||||
return (int) (getHeight() * mAspectRatio);
|
||||
}
|
||||
|
||||
public void setIsolated(boolean isolated) {
|
||||
if (mIsIsolated != isolated) {
|
||||
mIsIsolated = isolated;
|
||||
// update the layout params not to have margins
|
||||
ViewGroup.MarginLayoutParams layoutParams =
|
||||
(ViewGroup.MarginLayoutParams) getLayoutParams();
|
||||
layoutParams.topMargin = isolated ? 0 : mExtraSpacing;
|
||||
setLayoutParams(layoutParams);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExtraSpacing() {
|
||||
return mExtraSpacing;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
|
||||
@@ -75,7 +75,6 @@ public class MessagingLinearLayout extends ViewGroup {
|
||||
targetHeight = Integer.MAX_VALUE;
|
||||
break;
|
||||
}
|
||||
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
|
||||
|
||||
// Now that we know which views to take, fix up the indents and see what width we get.
|
||||
int measuredWidth = mPaddingLeft + mPaddingRight;
|
||||
@@ -90,7 +89,6 @@ public class MessagingLinearLayout extends ViewGroup {
|
||||
totalHeight = mPaddingTop + mPaddingBottom;
|
||||
boolean first = true;
|
||||
int linesRemaining = mMaxDisplayedLines;
|
||||
|
||||
// Starting from the bottom: we measure every view as if it were the only one. If it still
|
||||
// fits, we take it, otherwise we stop there.
|
||||
for (int i = count - 1; i >= 0 && totalHeight < targetHeight; i--) {
|
||||
@@ -100,11 +98,13 @@ public class MessagingLinearLayout extends ViewGroup {
|
||||
final View child = getChildAt(i);
|
||||
LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
|
||||
MessagingChild messagingChild = null;
|
||||
int spacing = mSpacing;
|
||||
if (child instanceof MessagingChild) {
|
||||
messagingChild = (MessagingChild) child;
|
||||
messagingChild.setMaxDisplayedLines(linesRemaining);
|
||||
spacing += messagingChild.getExtraSpacing();
|
||||
}
|
||||
int spacing = first ? 0 : mSpacing;
|
||||
spacing = first ? 0 : spacing;
|
||||
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, totalHeight
|
||||
- mPaddingTop - mPaddingBottom + spacing);
|
||||
|
||||
@@ -254,6 +254,9 @@ public class MessagingLinearLayout extends ViewGroup {
|
||||
void setMaxDisplayedLines(int lines);
|
||||
void hideAnimated();
|
||||
boolean isHidingAnimated();
|
||||
default int getExtraSpacing() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static class LayoutParams extends MarginLayoutParams {
|
||||
|
||||
@@ -16,182 +16,125 @@
|
||||
|
||||
package com.android.internal.widget;
|
||||
|
||||
import android.annotation.AttrRes;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.StyleRes;
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.text.Layout;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Pools;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.android.internal.R;
|
||||
import android.view.View;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A message of a {@link MessagingLayout}.
|
||||
*/
|
||||
@RemoteViews.RemoteView
|
||||
public class MessagingMessage extends ImageFloatingTextView implements
|
||||
MessagingLinearLayout.MessagingChild {
|
||||
public interface MessagingMessage extends MessagingLinearLayout.MessagingChild {
|
||||
|
||||
private static Pools.SimplePool<MessagingMessage> sInstancePool
|
||||
= new Pools.SynchronizedPool<>(10);
|
||||
private Notification.MessagingStyle.Message mMessage;
|
||||
private MessagingGroup mGroup;
|
||||
private boolean mIsHistoric;
|
||||
private boolean mIsHidingAnimated;
|
||||
/**
|
||||
* Prefix for supported image MIME types
|
||||
**/
|
||||
String IMAGE_MIME_TYPE_PREFIX = "image/";
|
||||
|
||||
public MessagingMessage(@NonNull Context context) {
|
||||
super(context);
|
||||
static MessagingMessage createMessage(MessagingLayout layout,
|
||||
Notification.MessagingStyle.Message m) {
|
||||
if (hasImage(m)) {
|
||||
return MessagingImageMessage.createMessage(layout, m);
|
||||
} else {
|
||||
return MessagingTextMessage.createMessage(layout, m);
|
||||
}
|
||||
}
|
||||
|
||||
public MessagingMessage(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
static void dropCache() {
|
||||
MessagingTextMessage.dropCache();
|
||||
MessagingImageMessage.dropCache();
|
||||
}
|
||||
|
||||
public MessagingMessage(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||
@AttrRes int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
static boolean hasImage(Notification.MessagingStyle.Message m) {
|
||||
return m.getDataUri() != null
|
||||
&& m.getDataMimeType() != null
|
||||
&& m.getDataMimeType().startsWith(IMAGE_MIME_TYPE_PREFIX);
|
||||
}
|
||||
|
||||
public MessagingMessage(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||
@AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
/**
|
||||
* Set a message for this view.
|
||||
* @return true if setting the message worked
|
||||
*/
|
||||
default boolean setMessage(Notification.MessagingStyle.Message message) {
|
||||
getState().setMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setMessage(Notification.MessagingStyle.Message message) {
|
||||
mMessage = message;
|
||||
setText(message.getText());
|
||||
default Notification.MessagingStyle.Message getMessage() {
|
||||
return getState().getMessage();
|
||||
}
|
||||
|
||||
public Notification.MessagingStyle.Message getMessage() {
|
||||
return mMessage;
|
||||
}
|
||||
|
||||
boolean sameAs(Notification.MessagingStyle.Message message) {
|
||||
if (!Objects.equals(message.getText(), mMessage.getText())) {
|
||||
default boolean sameAs(Notification.MessagingStyle.Message message) {
|
||||
Notification.MessagingStyle.Message ownMessage = getMessage();
|
||||
if (!Objects.equals(message.getText(), ownMessage.getText())) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(message.getSender(), mMessage.getSender())) {
|
||||
if (!Objects.equals(message.getSender(), ownMessage.getSender())) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(message.getTimestamp(), mMessage.getTimestamp())) {
|
||||
if (!Objects.equals(message.getTimestamp(), ownMessage.getTimestamp())) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(message.getDataMimeType(), ownMessage.getDataMimeType())) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(message.getDataUri(), ownMessage.getDataUri())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean sameAs(MessagingMessage message) {
|
||||
default boolean sameAs(MessagingMessage message) {
|
||||
return sameAs(message.getMessage());
|
||||
}
|
||||
|
||||
static MessagingMessage createMessage(MessagingLayout layout,
|
||||
Notification.MessagingStyle.Message m) {
|
||||
MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout();
|
||||
MessagingMessage createdMessage = sInstancePool.acquire();
|
||||
if (createdMessage == null) {
|
||||
createdMessage = (MessagingMessage) LayoutInflater.from(layout.getContext()).inflate(
|
||||
R.layout.notification_template_messaging_message, messagingLinearLayout,
|
||||
false);
|
||||
}
|
||||
createdMessage.setMessage(m);
|
||||
return createdMessage;
|
||||
default void removeMessage() {
|
||||
getGroup().removeMessage(this);
|
||||
}
|
||||
|
||||
public void removeMessage() {
|
||||
mGroup.removeMessage(this);
|
||||
default void setMessagingGroup(MessagingGroup group) {
|
||||
getState().setGroup(group);
|
||||
}
|
||||
|
||||
public void recycle() {
|
||||
mGroup = null;
|
||||
mMessage = null;
|
||||
setAlpha(1.0f);
|
||||
setTranslationY(0);
|
||||
sInstancePool.release(this);
|
||||
default void setIsHistoric(boolean isHistoric) {
|
||||
getState().setIsHistoric(isHistoric);
|
||||
}
|
||||
|
||||
public void setMessagingGroup(MessagingGroup group) {
|
||||
mGroup = group;
|
||||
default MessagingGroup getGroup() {
|
||||
return getState().getGroup();
|
||||
}
|
||||
|
||||
public static void dropCache() {
|
||||
sInstancePool = new Pools.SynchronizedPool<>(10);
|
||||
}
|
||||
|
||||
public void setIsHistoric(boolean isHistoric) {
|
||||
mIsHistoric = isHistoric;
|
||||
}
|
||||
|
||||
public MessagingGroup getGroup() {
|
||||
return mGroup;
|
||||
default void setIsHidingAnimated(boolean isHiding) {
|
||||
getState().setIsHidingAnimated(isHiding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMeasuredType() {
|
||||
boolean measuredTooSmall = getMeasuredHeight()
|
||||
< getLayoutHeight() + getPaddingTop() + getPaddingBottom();
|
||||
if (measuredTooSmall) {
|
||||
return MEASURED_TOO_SMALL;
|
||||
} else {
|
||||
Layout layout = getLayout();
|
||||
if (layout == null) {
|
||||
return MEASURED_TOO_SMALL;
|
||||
}
|
||||
if (layout.getEllipsisCount(layout.getLineCount() - 1) > 0) {
|
||||
return MEASURED_SHORTENED;
|
||||
} else {
|
||||
return MEASURED_NORMAL;
|
||||
}
|
||||
}
|
||||
default boolean isHidingAnimated() {
|
||||
return getState().isHidingAnimated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideAnimated() {
|
||||
default void hideAnimated() {
|
||||
setIsHidingAnimated(true);
|
||||
mGroup.performRemoveAnimation(this, () -> setIsHidingAnimated(false));
|
||||
getGroup().performRemoveAnimation(getState().getHostView(),
|
||||
() -> setIsHidingAnimated(false));
|
||||
}
|
||||
|
||||
private void setIsHidingAnimated(boolean isHiding) {
|
||||
ViewParent parent = getParent();
|
||||
mIsHidingAnimated = isHiding;
|
||||
invalidate();
|
||||
if (parent instanceof ViewGroup) {
|
||||
((ViewGroup) parent).invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidingAnimated() {
|
||||
return mIsHidingAnimated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxDisplayedLines(int lines) {
|
||||
setMaxLines(lines);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConsumedLines() {
|
||||
return getLineCount();
|
||||
}
|
||||
|
||||
public int getLayoutHeight() {
|
||||
Layout layout = getLayout();
|
||||
if (layout == null) {
|
||||
return 0;
|
||||
}
|
||||
return layout.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOverlappingRendering() {
|
||||
default boolean hasOverlappingRendering() {
|
||||
return false;
|
||||
}
|
||||
|
||||
default void recycle() {
|
||||
getState().reset();
|
||||
}
|
||||
|
||||
default View getView() {
|
||||
return (View) this;
|
||||
}
|
||||
|
||||
default void setColor(int textColor) {}
|
||||
|
||||
MessagingMessageState getState();
|
||||
|
||||
void setVisibility(int visibility);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.app.Notification;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
|
||||
/**
|
||||
* Shared state and implementation for MessagingMessages. Used to share common implementations.
|
||||
*/
|
||||
public class MessagingMessageState {
|
||||
private final View mHostView;
|
||||
private Notification.MessagingStyle.Message mMessage;
|
||||
private MessagingGroup mGroup;
|
||||
private boolean mIsHistoric;
|
||||
private boolean mIsHidingAnimated;
|
||||
|
||||
MessagingMessageState(View hostView) {
|
||||
mHostView = hostView;
|
||||
}
|
||||
|
||||
public void setMessage(Notification.MessagingStyle.Message message) {
|
||||
mMessage = message;
|
||||
}
|
||||
|
||||
public Notification.MessagingStyle.Message getMessage() {
|
||||
return mMessage;
|
||||
}
|
||||
|
||||
public void setGroup(MessagingGroup group) {
|
||||
mGroup = group;
|
||||
}
|
||||
|
||||
public MessagingGroup getGroup() {
|
||||
return mGroup;
|
||||
}
|
||||
|
||||
public void setIsHistoric(boolean isHistoric) {
|
||||
mIsHistoric = isHistoric;
|
||||
}
|
||||
|
||||
public void setIsHidingAnimated(boolean isHiding) {
|
||||
ViewParent parent = mHostView.getParent();
|
||||
mIsHidingAnimated = isHiding;
|
||||
mHostView.invalidate();
|
||||
if (parent instanceof ViewGroup) {
|
||||
((ViewGroup) parent).invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isHidingAnimated() {
|
||||
return mIsHidingAnimated;
|
||||
}
|
||||
|
||||
public View getHostView() {
|
||||
return mHostView;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
mIsHidingAnimated = false;
|
||||
mIsHistoric = false;
|
||||
mGroup = null;
|
||||
mMessage = null;
|
||||
}
|
||||
}
|
||||
145
core/java/com/android/internal/widget/MessagingTextMessage.java
Normal file
145
core/java/com/android/internal/widget/MessagingTextMessage.java
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.AttrRes;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.StyleRes;
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.text.Layout;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Pools;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A message of a {@link MessagingLayout}.
|
||||
*/
|
||||
@RemoteViews.RemoteView
|
||||
public class MessagingTextMessage extends ImageFloatingTextView implements MessagingMessage {
|
||||
|
||||
private static Pools.SimplePool<MessagingTextMessage> sInstancePool
|
||||
= new Pools.SynchronizedPool<>(20);
|
||||
private final MessagingMessageState mState = new MessagingMessageState(this);
|
||||
|
||||
public MessagingTextMessage(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public MessagingTextMessage(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public MessagingTextMessage(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||
@AttrRes int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public MessagingTextMessage(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||
@AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessagingMessageState getState() {
|
||||
return mState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setMessage(Notification.MessagingStyle.Message message) {
|
||||
MessagingMessage.super.setMessage(message);
|
||||
setText(message.getText());
|
||||
return true;
|
||||
}
|
||||
|
||||
static MessagingMessage createMessage(MessagingLayout layout,
|
||||
Notification.MessagingStyle.Message m) {
|
||||
MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout();
|
||||
MessagingTextMessage createdMessage = sInstancePool.acquire();
|
||||
if (createdMessage == null) {
|
||||
createdMessage = (MessagingTextMessage) LayoutInflater.from(
|
||||
layout.getContext()).inflate(
|
||||
R.layout.notification_template_messaging_text_message,
|
||||
messagingLinearLayout,
|
||||
false);
|
||||
createdMessage.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR);
|
||||
}
|
||||
createdMessage.setMessage(m);
|
||||
return createdMessage;
|
||||
}
|
||||
|
||||
public void recycle() {
|
||||
MessagingMessage.super.recycle();
|
||||
setAlpha(1.0f);
|
||||
setTranslationY(0);
|
||||
sInstancePool.release(this);
|
||||
}
|
||||
|
||||
public static void dropCache() {
|
||||
sInstancePool = new Pools.SynchronizedPool<>(10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMeasuredType() {
|
||||
boolean measuredTooSmall = getMeasuredHeight()
|
||||
< getLayoutHeight() + getPaddingTop() + getPaddingBottom();
|
||||
if (measuredTooSmall) {
|
||||
return MEASURED_TOO_SMALL;
|
||||
} else {
|
||||
Layout layout = getLayout();
|
||||
if (layout == null) {
|
||||
return MEASURED_TOO_SMALL;
|
||||
}
|
||||
if (layout.getEllipsisCount(layout.getLineCount() - 1) > 0) {
|
||||
return MEASURED_SHORTENED;
|
||||
} else {
|
||||
return MEASURED_NORMAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxDisplayedLines(int lines) {
|
||||
setMaxLines(lines);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConsumedLines() {
|
||||
return getLineCount();
|
||||
}
|
||||
|
||||
public int getLayoutHeight() {
|
||||
Layout layout = getLayout();
|
||||
if (layout == null) {
|
||||
return 0;
|
||||
}
|
||||
return layout.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColor(int color) {
|
||||
setTextColor(color);
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,9 @@
|
||||
android:scaleType="centerCrop"
|
||||
android:importantForAccessibility="no" />
|
||||
<com.android.internal.widget.RemeasuringLinearLayout
|
||||
android:id="@+id/message_group_and_sender_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
<com.android.internal.widget.ImageFloatingTextView
|
||||
android:id="@+id/message_name"
|
||||
@@ -44,4 +44,10 @@
|
||||
android:spacing="2dp"
|
||||
android:layout_weight="1"/>
|
||||
</com.android.internal.widget.RemeasuringLinearLayout>
|
||||
<FrameLayout
|
||||
android:id="@+id/messaging_group_icon_container"
|
||||
android:layout_width="@dimen/messaging_avatar_size"
|
||||
android:layout_height="@dimen/messaging_avatar_size"
|
||||
android:layout_marginStart="12dp"
|
||||
android:visibility="gone"/>
|
||||
</com.android.internal.widget.MessagingGroup>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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
|
||||
-->
|
||||
<com.android.internal.widget.MessagingImageMessage
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/message_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/messaging_image_extra_spacing"
|
||||
android:scaleType="fitStart"
|
||||
/>
|
||||
@@ -14,7 +14,7 @@
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License
|
||||
-->
|
||||
<com.android.internal.widget.MessagingMessage
|
||||
<com.android.internal.widget.MessagingTextMessage
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/message_text"
|
||||
style="@style/Widget.Material.Notification.MessagingText"
|
||||
@@ -262,6 +262,18 @@
|
||||
<!-- The spacing between messages in Notification.MessagingStyle -->
|
||||
<dimen name="notification_messaging_spacing">6dp</dimen>
|
||||
|
||||
<!-- The rounding for messaging images -->
|
||||
<dimen name="messaging_image_rounding">4dp</dimen>
|
||||
|
||||
<!-- The minimum size for any image in messaging style in order to be displayed -->
|
||||
<dimen name="messaging_image_min_size">44dp</dimen>
|
||||
|
||||
<!-- The maximum size for any image in messaging style in order to be displayed -->
|
||||
<dimen name="messaging_image_max_height">136dp</dimen>
|
||||
|
||||
<!-- Extra spacing before and after images in messaging style -->
|
||||
<dimen name="messaging_image_extra_spacing">8dp</dimen>
|
||||
|
||||
<!-- Preferred width and height of the search view. -->
|
||||
<dimen name="search_view_preferred_width">320dip</dimen>
|
||||
<dimen name="search_view_preferred_height">48dip</dimen>
|
||||
|
||||
@@ -3168,7 +3168,8 @@
|
||||
<java-symbol type="dimen" name="chooser_service_spacing" />
|
||||
<java-symbol type="bool" name="config_showSysuiShutdown" />
|
||||
|
||||
<java-symbol type="layout" name="notification_template_messaging_message" />
|
||||
<java-symbol type="layout" name="notification_template_messaging_text_message" />
|
||||
<java-symbol type="layout" name="notification_template_messaging_image_message" />
|
||||
<java-symbol type="layout" name="notification_template_messaging_group" />
|
||||
<java-symbol type="id" name="message_text" />
|
||||
<java-symbol type="id" name="message_name" />
|
||||
@@ -3183,6 +3184,11 @@
|
||||
<java-symbol type="id" name="clip_children_tag" />
|
||||
<java-symbol type="drawable" name="ic_reply_notification_large" />
|
||||
<java-symbol type="dimen" name="messaging_avatar_size" />
|
||||
<java-symbol type="dimen" name="messaging_image_rounding" />
|
||||
<java-symbol type="dimen" name="messaging_image_min_size" />
|
||||
<java-symbol type="dimen" name="messaging_image_max_height" />
|
||||
<java-symbol type="dimen" name="messaging_image_extra_spacing" />
|
||||
<java-symbol type="id" name="messaging_group_icon_container" />
|
||||
|
||||
<java-symbol type="integer" name="config_stableDeviceDisplayWidth" />
|
||||
<java-symbol type="integer" name="config_stableDeviceDisplayHeight" />
|
||||
|
||||
@@ -127,7 +127,7 @@ public class MessagingLinearLayoutTest {
|
||||
assertEquals(355, mView.getMeasuredHeight());;
|
||||
}
|
||||
|
||||
private class FakeImageFloatingTextView extends MessagingMessage {
|
||||
private class FakeImageFloatingTextView extends MessagingTextMessage {
|
||||
|
||||
public static final int LINE_HEIGHT = 50;
|
||||
private final int mNumLines;
|
||||
|
||||
@@ -58,6 +58,8 @@
|
||||
<item type="id" name="notification_plugin"/>
|
||||
<item type="id" name="transformation_start_x_tag"/>
|
||||
<item type="id" name="transformation_start_y_tag"/>
|
||||
<item type="id" name="transformation_start_actual_width"/>
|
||||
<item type="id" name="transformation_start_actual_height"/>
|
||||
<item type="id" name="transformation_start_scale_x_tag"/>
|
||||
<item type="id" name="transformation_start_scale_y_tag"/>
|
||||
<item type="id" name="continuous_clipping_tag"/>
|
||||
|
||||
@@ -21,6 +21,8 @@ import android.util.Pools;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.internal.widget.MessagingImageMessage;
|
||||
import com.android.internal.widget.MessagingMessage;
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.CrossFadeHelper;
|
||||
@@ -117,13 +119,15 @@ public class ImageTransformState extends TransformState {
|
||||
|
||||
@Override
|
||||
protected boolean transformScale(TransformState otherState) {
|
||||
return true;
|
||||
return sameAs(otherState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycle() {
|
||||
super.recycle();
|
||||
sInstancePool.release(this);
|
||||
if (getClass() == ImageTransformState.class) {
|
||||
sInstancePool.release(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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.systemui.statusbar.notification;
|
||||
|
||||
import android.util.Pools;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.internal.widget.MessagingImageMessage;
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.ViewTransformationHelper;
|
||||
|
||||
/**
|
||||
* A transform state of a image view.
|
||||
*/
|
||||
public class MessagingImageTransformState extends ImageTransformState {
|
||||
private static Pools.SimplePool<MessagingImageTransformState> sInstancePool
|
||||
= new Pools.SimplePool<>(40);
|
||||
private static final int START_ACTUAL_WIDTH = R.id.transformation_start_actual_width;
|
||||
private static final int START_ACTUAL_HEIGHT = R.id.transformation_start_actual_height;
|
||||
private MessagingImageMessage mImageMessage;
|
||||
|
||||
@Override
|
||||
public void initFrom(View view, TransformInfo transformInfo) {
|
||||
super.initFrom(view, transformInfo);
|
||||
mImageMessage = (MessagingImageMessage) view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean sameAs(TransformState otherState) {
|
||||
if (super.sameAs(otherState)) {
|
||||
return true;
|
||||
}
|
||||
if (otherState instanceof MessagingImageTransformState) {
|
||||
MessagingImageTransformState otherMessage = (MessagingImageTransformState) otherState;
|
||||
return mImageMessage.sameAs(otherMessage.mImageMessage);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static MessagingImageTransformState obtain() {
|
||||
MessagingImageTransformState instance = sInstancePool.acquire();
|
||||
if (instance != null) {
|
||||
return instance;
|
||||
}
|
||||
return new MessagingImageTransformState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean transformScale(TransformState otherState) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void transformViewFrom(TransformState otherState, int transformationFlags,
|
||||
ViewTransformationHelper.CustomTransformation customTransformation,
|
||||
float transformationAmount) {
|
||||
super.transformViewFrom(otherState, transformationFlags, customTransformation,
|
||||
transformationAmount);
|
||||
float interpolatedValue = mDefaultInterpolator.getInterpolation(
|
||||
transformationAmount);
|
||||
if (otherState instanceof MessagingImageTransformState && sameAs(otherState)) {
|
||||
MessagingImageMessage otherMessage
|
||||
= ((MessagingImageTransformState) otherState).mImageMessage;
|
||||
if (transformationAmount == 0.0f) {
|
||||
setStartActualWidth(otherMessage.getActualWidth());
|
||||
setStartActualHeight(otherMessage.getActualHeight());
|
||||
}
|
||||
float startActualWidth = getStartActualWidth();
|
||||
mImageMessage.setActualWidth(
|
||||
(int) NotificationUtils.interpolate(startActualWidth,
|
||||
mImageMessage.getStaticWidth(),
|
||||
interpolatedValue));
|
||||
float startActualHeight = getStartActualHeight();
|
||||
mImageMessage.setActualHeight(
|
||||
(int) NotificationUtils.interpolate(startActualHeight,
|
||||
mImageMessage.getHeight(),
|
||||
interpolatedValue));
|
||||
}
|
||||
}
|
||||
|
||||
public int getStartActualWidth() {
|
||||
Object tag = mTransformedView.getTag(START_ACTUAL_WIDTH);
|
||||
return tag == null ? -1 : (int) tag;
|
||||
}
|
||||
|
||||
public void setStartActualWidth(int actualWidth) {
|
||||
mTransformedView.setTag(START_ACTUAL_WIDTH, actualWidth);
|
||||
}
|
||||
|
||||
public int getStartActualHeight() {
|
||||
Object tag = mTransformedView.getTag(START_ACTUAL_HEIGHT);
|
||||
return tag == null ? -1 : (int) tag;
|
||||
}
|
||||
|
||||
public void setStartActualHeight(int actualWidth) {
|
||||
mTransformedView.setTag(START_ACTUAL_HEIGHT, actualWidth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycle() {
|
||||
super.recycle();
|
||||
if (getClass() == MessagingImageTransformState.class) {
|
||||
sInstancePool.release(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetTransformedView() {
|
||||
super.resetTransformedView();
|
||||
mImageMessage.setActualWidth(mImageMessage.getStaticWidth());
|
||||
mImageMessage.setActualHeight(mImageMessage.getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reset() {
|
||||
super.reset();
|
||||
mImageMessage = null;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.android.internal.widget.MessagingGroup;
|
||||
import com.android.internal.widget.MessagingImageMessage;
|
||||
import com.android.internal.widget.MessagingLayout;
|
||||
import com.android.internal.widget.MessagingLinearLayout;
|
||||
import com.android.internal.widget.MessagingMessage;
|
||||
@@ -30,6 +31,7 @@ import com.android.systemui.Interpolators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A transform state of the action list
|
||||
@@ -156,6 +158,7 @@ public class MessagingLayoutTransformState extends TransformState {
|
||||
}
|
||||
appear(ownGroup.getAvatar(), transformationAmount);
|
||||
appear(ownGroup.getSenderView(), transformationAmount);
|
||||
appear(ownGroup.getIsolatedMessage(), transformationAmount);
|
||||
setClippingDeactivated(ownGroup.getSenderView(), true);
|
||||
setClippingDeactivated(ownGroup.getAvatar(), true);
|
||||
}
|
||||
@@ -187,12 +190,13 @@ public class MessagingLayoutTransformState extends TransformState {
|
||||
}
|
||||
disappear(ownGroup.getAvatar(), transformationAmount);
|
||||
disappear(ownGroup.getSenderView(), transformationAmount);
|
||||
disappear(ownGroup.getIsolatedMessage(), transformationAmount);
|
||||
setClippingDeactivated(ownGroup.getSenderView(), true);
|
||||
setClippingDeactivated(ownGroup.getAvatar(), true);
|
||||
}
|
||||
|
||||
private void appear(View child, float transformationAmount) {
|
||||
if (child.getVisibility() == View.GONE) {
|
||||
if (child == null || child.getVisibility() == View.GONE) {
|
||||
return;
|
||||
}
|
||||
TransformState ownState = TransformState.createFrom(child, mTransformInfo);
|
||||
@@ -201,7 +205,7 @@ public class MessagingLayoutTransformState extends TransformState {
|
||||
}
|
||||
|
||||
private void disappear(View child, float transformationAmount) {
|
||||
if (child.getVisibility() == View.GONE) {
|
||||
if (child == null || child.getVisibility() == View.GONE) {
|
||||
return;
|
||||
}
|
||||
TransformState ownState = TransformState.createFrom(child, mTransformInfo);
|
||||
@@ -224,22 +228,24 @@ public class MessagingLayoutTransformState extends TransformState {
|
||||
|
||||
private void transformGroups(MessagingGroup ownGroup, MessagingGroup otherGroup,
|
||||
float transformationAmount, boolean to) {
|
||||
boolean useLinearTransformation =
|
||||
otherGroup.getIsolatedMessage() == null && !mTransformInfo.isAnimating();
|
||||
transformView(transformationAmount, to, ownGroup.getSenderView(), otherGroup.getSenderView(),
|
||||
true /* sameAsAny */);
|
||||
true /* sameAsAny */, useLinearTransformation);
|
||||
transformView(transformationAmount, to, ownGroup.getAvatar(), otherGroup.getAvatar(),
|
||||
true /* sameAsAny */);
|
||||
MessagingLinearLayout ownMessages = ownGroup.getMessageContainer();
|
||||
MessagingLinearLayout otherMessages = otherGroup.getMessageContainer();
|
||||
true /* sameAsAny */, useLinearTransformation);
|
||||
List<MessagingMessage> ownMessages = ownGroup.getMessages();
|
||||
List<MessagingMessage> otherMessages = otherGroup.getMessages();
|
||||
float previousTranslation = 0;
|
||||
for (int i = 0; i < ownMessages.getChildCount(); i++) {
|
||||
View child = ownMessages.getChildAt(ownMessages.getChildCount() - 1 - i);
|
||||
for (int i = 0; i < ownMessages.size(); i++) {
|
||||
View child = ownMessages.get(ownMessages.size() - 1 - i).getView();
|
||||
if (isGone(child)) {
|
||||
continue;
|
||||
}
|
||||
int otherIndex = otherMessages.getChildCount() - 1 - i;
|
||||
int otherIndex = otherMessages.size() - 1 - i;
|
||||
View otherChild = null;
|
||||
if (otherIndex >= 0) {
|
||||
otherChild = otherMessages.getChildAt(otherIndex);
|
||||
otherChild = otherMessages.get(otherIndex).getView();
|
||||
if (isGone(otherChild)) {
|
||||
otherChild = null;
|
||||
}
|
||||
@@ -252,7 +258,12 @@ public class MessagingLayoutTransformState extends TransformState {
|
||||
transformationAmount = 1.0f - transformationAmount;
|
||||
}
|
||||
}
|
||||
transformView(transformationAmount, to, child, otherChild, false /* sameAsAny */);
|
||||
transformView(transformationAmount, to, child, otherChild, false, /* sameAsAny */
|
||||
useLinearTransformation);
|
||||
if (transformationAmount == 0.0f
|
||||
&& otherGroup.getIsolatedMessage() == otherChild) {
|
||||
ownGroup.setTransformingImages(true);
|
||||
}
|
||||
if (otherChild == null) {
|
||||
child.setTranslationY(previousTranslation);
|
||||
setClippingDeactivated(child, true);
|
||||
@@ -264,12 +275,13 @@ public class MessagingLayoutTransformState extends TransformState {
|
||||
previousTranslation = child.getTranslationY();
|
||||
}
|
||||
}
|
||||
ownGroup.updateClipRect();
|
||||
}
|
||||
|
||||
private void transformView(float transformationAmount, boolean to, View ownView,
|
||||
View otherView, boolean sameAsAny) {
|
||||
View otherView, boolean sameAsAny, boolean useLinearTransformation) {
|
||||
TransformState ownState = TransformState.createFrom(ownView, mTransformInfo);
|
||||
if (!mTransformInfo.isAnimating()) {
|
||||
if (useLinearTransformation) {
|
||||
ownState.setDefaultInterpolator(Interpolators.LINEAR);
|
||||
}
|
||||
ownState.setIsSameAsAnyView(sameAsAny);
|
||||
@@ -339,11 +351,15 @@ public class MessagingLayoutTransformState extends TransformState {
|
||||
if (!isGone(ownGroup)) {
|
||||
MessagingLinearLayout ownMessages = ownGroup.getMessageContainer();
|
||||
for (int j = 0; j < ownMessages.getChildCount(); j++) {
|
||||
MessagingMessage child = (MessagingMessage) ownMessages.getChildAt(j);
|
||||
View child = ownMessages.getChildAt(j);
|
||||
setVisible(child, visible, force);
|
||||
}
|
||||
setVisible(ownGroup.getAvatar(), visible, force);
|
||||
setVisible(ownGroup.getSenderView(), visible, force);
|
||||
MessagingImageMessage isolatedMessage = ownGroup.getIsolatedMessage();
|
||||
if (isolatedMessage != null) {
|
||||
setVisible(isolatedMessage, visible, force);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -375,11 +391,17 @@ public class MessagingLayoutTransformState extends TransformState {
|
||||
}
|
||||
resetTransformedView(ownGroup.getAvatar());
|
||||
resetTransformedView(ownGroup.getSenderView());
|
||||
MessagingImageMessage isolatedMessage = ownGroup.getIsolatedMessage();
|
||||
if (isolatedMessage != null) {
|
||||
resetTransformedView(isolatedMessage);
|
||||
}
|
||||
setClippingDeactivated(ownGroup.getAvatar(), false);
|
||||
setClippingDeactivated(ownGroup.getSenderView(), false);
|
||||
ownGroup.setTranslationY(0);
|
||||
ownGroup.getMessageContainer().setTranslationY(0);
|
||||
}
|
||||
ownGroup.setTransformingImages(false);
|
||||
ownGroup.updateClipRect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.widget.MessagingImageMessage;
|
||||
import com.android.internal.widget.MessagingPropertyAnimator;
|
||||
import com.android.internal.widget.ViewClippingUtil;
|
||||
import com.android.systemui.Interpolators;
|
||||
@@ -80,7 +81,7 @@ public class TransformState {
|
||||
private boolean mSameAsAny;
|
||||
private float mTransformationEndY = UNDEFINED;
|
||||
private float mTransformationEndX = UNDEFINED;
|
||||
private Interpolator mDefaultInterpolator = Interpolators.FAST_OUT_SLOW_IN;
|
||||
protected Interpolator mDefaultInterpolator = Interpolators.FAST_OUT_SLOW_IN;
|
||||
|
||||
public void initFrom(View view, TransformInfo transformInfo) {
|
||||
mTransformedView = view;
|
||||
@@ -131,7 +132,7 @@ public class TransformState {
|
||||
transformViewFrom(otherState, TRANSFORM_Y, null, transformationAmount);
|
||||
}
|
||||
|
||||
private void transformViewFrom(TransformState otherState, int transformationFlags,
|
||||
protected void transformViewFrom(TransformState otherState, int transformationFlags,
|
||||
ViewTransformationHelper.CustomTransformation customTransformation,
|
||||
float transformationAmount) {
|
||||
final View transformedView = mTransformedView;
|
||||
@@ -449,6 +450,11 @@ public class TransformState {
|
||||
result.initFrom(view, transformInfo);
|
||||
return result;
|
||||
}
|
||||
if (view instanceof MessagingImageMessage) {
|
||||
MessagingImageTransformState result = MessagingImageTransformState.obtain();
|
||||
result.initFrom(view, transformInfo);
|
||||
return result;
|
||||
}
|
||||
if (view instanceof ImageView) {
|
||||
ImageTransformState result = ImageTransformState.obtain();
|
||||
result.initFrom(view, transformInfo);
|
||||
|
||||
Reference in New Issue
Block a user