kopia lustrzana https://github.com/ryukoposting/Signal-Android
Drastically reduce number of projection instances we create.
Via SimplePoolfork-5.53.8
rodzic
98fce53cf1
commit
b34bb2e7d7
|
@ -57,6 +57,7 @@ import org.thoughtcrime.securesms.util.CachedInflater;
|
|||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.MessageRecordUtil;
|
||||
import org.thoughtcrime.securesms.util.Projection;
|
||||
import org.thoughtcrime.securesms.util.ProjectionList;
|
||||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
@ -704,7 +705,7 @@ public class ConversationAdapter
|
|||
}
|
||||
|
||||
@Override
|
||||
public @NonNull List<Projection> getColorizerProjections(@NonNull ViewGroup coordinateRoot) {
|
||||
public @NonNull ProjectionList getColorizerProjections(@NonNull ViewGroup coordinateRoot) {
|
||||
return getBindable().getColorizerProjections(coordinateRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,6 +122,7 @@ import org.thoughtcrime.securesms.util.InterceptableLongClickCopyLinkSpan;
|
|||
import org.thoughtcrime.securesms.util.LongClickMovementMethod;
|
||||
import org.thoughtcrime.securesms.util.MessageRecordUtil;
|
||||
import org.thoughtcrime.securesms.util.Projection;
|
||||
import org.thoughtcrime.securesms.util.ProjectionList;
|
||||
import org.thoughtcrime.securesms.util.SearchUtil;
|
||||
import org.thoughtcrime.securesms.util.StringUtil;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
|
@ -221,6 +222,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
|||
private Colorizer colorizer;
|
||||
private boolean hasWallpaper;
|
||||
private float lastYDownRelativeToThis;
|
||||
private ProjectionList colorizerProjections = new ProjectionList(3);
|
||||
|
||||
public ConversationItem(Context context) {
|
||||
this(context, null);
|
||||
|
@ -530,6 +532,10 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
|||
if (conversationRecipient != null) {
|
||||
conversationRecipient.removeForeverObserver(this);
|
||||
}
|
||||
|
||||
bodyBubble.setVideoPlayerProjection(null);
|
||||
bodyBubble.setQuoteViewProjection(null);
|
||||
|
||||
cancelPulseOutlinerAnimation();
|
||||
}
|
||||
|
||||
|
@ -588,12 +594,17 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
|||
}
|
||||
|
||||
private static int getProjectionTop(@NonNull View child) {
|
||||
return (int) Projection.relativeToViewRoot(child, null).getY();
|
||||
Projection projection = Projection.relativeToViewRoot(child, null);
|
||||
int y = (int) projection.getY();
|
||||
projection.release();
|
||||
return y;
|
||||
}
|
||||
|
||||
private static int getProjectionBottom(@NonNull View child) {
|
||||
Projection projection = Projection.relativeToViewRoot(child, null);
|
||||
return (int) projection.getY() + projection.getHeight();
|
||||
int bottom = (int) projection.getY() + projection.getHeight();
|
||||
projection.release();
|
||||
return bottom;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1719,8 +1730,8 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
|||
}
|
||||
|
||||
@Override
|
||||
public @NonNull List<Projection> getColorizerProjections(@NonNull ViewGroup coordinateRoot) {
|
||||
List<Projection> projections = new LinkedList<>();
|
||||
public @NonNull ProjectionList getColorizerProjections(@NonNull ViewGroup coordinateRoot) {
|
||||
colorizerProjections.clear();
|
||||
|
||||
if (messageRecord.isOutgoing() &&
|
||||
!hasNoBubble(messageRecord) &&
|
||||
|
@ -1731,9 +1742,9 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
|||
Projection videoToBubble = bodyBubble.getVideoPlayerProjection();
|
||||
if (videoToBubble != null) {
|
||||
Projection videoToRoot = Projection.translateFromDescendantToParentCoords(videoToBubble, bodyBubble, coordinateRoot);
|
||||
projections.addAll(Projection.getCapAndTail(bodyBubbleToRoot, videoToRoot));
|
||||
colorizerProjections.addAll(Projection.getCapAndTail(bodyBubbleToRoot, videoToRoot));
|
||||
} else {
|
||||
projections.add(bodyBubbleToRoot);
|
||||
colorizerProjections.add(bodyBubbleToRoot);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1743,7 +1754,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
|||
{
|
||||
Projection footerProjection = getActiveFooter(messageRecord).getProjection(coordinateRoot);
|
||||
if (footerProjection != null) {
|
||||
projections.add(footerProjection.translateX(bodyBubble.getTranslationX()));
|
||||
colorizerProjections.add(footerProjection.translateX(bodyBubble.getTranslationX()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1752,10 +1763,14 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
|||
quoteView != null)
|
||||
{
|
||||
bodyBubble.setQuoteViewProjection(quoteView.getProjection(bodyBubble));
|
||||
projections.add(quoteView.getProjection(coordinateRoot).translateX(bodyBubble.getTranslationX() + this.getTranslationX()));
|
||||
colorizerProjections.add(quoteView.getProjection(coordinateRoot).translateX(bodyBubble.getTranslationX() + this.getTranslationX()));
|
||||
}
|
||||
|
||||
return projections.stream().map(p -> p.translateY(this.getTranslationY())).collect(Collectors.toList());
|
||||
for (int i = 0; i < colorizerProjections.size(); i++) {
|
||||
colorizerProjections.get(i).translateY(getTranslationY());
|
||||
}
|
||||
|
||||
return colorizerProjections;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -63,11 +63,19 @@ public class ConversationItemBodyBubble extends LinearLayout {
|
|||
}
|
||||
|
||||
public void setQuoteViewProjection(@Nullable Projection quoteViewProjection) {
|
||||
if (this.quoteViewProjection != null) {
|
||||
this.quoteViewProjection.release();
|
||||
}
|
||||
|
||||
this.quoteViewProjection = quoteViewProjection;
|
||||
clipProjectionDrawable.setProjections(getProjections());
|
||||
}
|
||||
|
||||
public void setVideoPlayerProjection(@Nullable Projection videoPlayerProjection) {
|
||||
if (this.videoPlayerProjection != null) {
|
||||
this.videoPlayerProjection.release();
|
||||
}
|
||||
|
||||
this.videoPlayerProjection = videoPlayerProjection;
|
||||
clipProjectionDrawable.setProjections(getProjections());
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
|
|||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||
import org.thoughtcrime.securesms.util.Projection;
|
||||
import org.thoughtcrime.securesms.util.ProjectionList;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
@ -59,7 +60,9 @@ import java.util.concurrent.ExecutionException;
|
|||
public final class ConversationUpdateItem extends FrameLayout
|
||||
implements BindableConversationItem
|
||||
{
|
||||
private static final String TAG = Log.tag(ConversationUpdateItem.class);
|
||||
private static final String TAG = Log.tag(ConversationUpdateItem.class);
|
||||
private static final ProjectionList EMPTY_PROJECTION_LIST = new ProjectionList();
|
||||
|
||||
|
||||
private Set<MultiselectPart> batchSelected;
|
||||
|
||||
|
@ -221,8 +224,8 @@ public final class ConversationUpdateItem extends FrameLayout
|
|||
}
|
||||
|
||||
@Override
|
||||
public @NonNull List<Projection> getColorizerProjections(@NonNull ViewGroup coordinateRoot) {
|
||||
return Collections.emptyList();
|
||||
public @NonNull ProjectionList getColorizerProjections(@NonNull ViewGroup coordinateRoot) {
|
||||
return EMPTY_PROJECTION_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package org.thoughtcrime.securesms.conversation.colors
|
||||
|
||||
import android.view.ViewGroup
|
||||
import org.thoughtcrime.securesms.util.Projection
|
||||
import org.thoughtcrime.securesms.util.ProjectionList
|
||||
|
||||
/**
|
||||
* Denotes that a class can be colorized. The class is responsible for
|
||||
* generating its own projection.
|
||||
*/
|
||||
interface Colorizable {
|
||||
fun getColorizerProjections(coordinateRoot: ViewGroup): List<Projection>
|
||||
fun getColorizerProjections(coordinateRoot: ViewGroup): ProjectionList
|
||||
}
|
||||
|
|
|
@ -40,4 +40,9 @@ class ColorizerView @JvmOverloads constructor(
|
|||
super.draw(canvas)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
projections.forEach { it.release() }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,8 +98,10 @@ class RecyclerViewColorizer(private val recyclerView: RecyclerView) {
|
|||
if (child != null) {
|
||||
val holder = parent.getChildViewHolder(child)
|
||||
if (holder is Colorizable) {
|
||||
holder.getColorizerProjections(parent).forEach {
|
||||
c.drawPath(it.path, holePunchPaint)
|
||||
holder.getColorizerProjections(parent).use { list ->
|
||||
list.forEach {
|
||||
c.drawPath(it.path, holePunchPaint)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import com.airbnb.lottie.SimpleColorFilter
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.conversation.ConversationAdapter
|
||||
import org.thoughtcrime.securesms.util.Projection
|
||||
import org.thoughtcrime.securesms.util.SetUtil
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil
|
||||
import org.thoughtcrime.securesms.util.ViewUtil
|
||||
|
@ -157,9 +156,17 @@ class MultiselectItemDecoration(
|
|||
|
||||
val parts: MultiselectCollection = child.conversationMessage.multiselectCollection
|
||||
|
||||
val projections: List<Projection> = child.getColorizerProjections(parent) + if (child.canPlayContent()) listOf(child.getGiphyMp4PlayableProjection(parent)) else emptyList()
|
||||
val projections = child.getColorizerProjections(parent)
|
||||
if (child.canPlayContent()) {
|
||||
projections.add(child.getGiphyMp4PlayableProjection(parent))
|
||||
}
|
||||
|
||||
path.reset()
|
||||
projections.forEach { it.applyToPath(path) }
|
||||
projections.use { list ->
|
||||
list.forEach {
|
||||
it.applyToPath(path)
|
||||
}
|
||||
}
|
||||
|
||||
canvas.save()
|
||||
canvas.clipPath(path, Region.Op.DIFFERENCE)
|
||||
|
@ -341,13 +348,16 @@ class MultiselectItemDecoration(
|
|||
parent.forEach { child ->
|
||||
if (child is Multiselectable && child.conversationMessage == inFocus.conversationMessage) {
|
||||
path.addRect(child.left.toFloat(), child.top.toFloat(), child.right.toFloat(), child.bottom.toFloat(), Path.Direction.CW)
|
||||
child.getColorizerProjections(parent).forEach {
|
||||
path.op(it.path, Path.Op.DIFFERENCE)
|
||||
child.getColorizerProjections(parent).use { list ->
|
||||
list.forEach {
|
||||
path.op(it.path, Path.Op.DIFFERENCE)
|
||||
}
|
||||
}
|
||||
|
||||
if (child.canPlayContent()) {
|
||||
val mp4GifProjection = child.getGiphyMp4PlayableProjection(child.rootView as ViewGroup)
|
||||
path.op(mp4GifProjection.path, Path.Op.DIFFERENCE)
|
||||
mp4GifProjection.release()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,6 +105,8 @@ public final class GiphyMp4ProjectionRecycler implements GiphyMp4PlaybackControl
|
|||
}
|
||||
|
||||
holder.setCorners(projection.getCorners());
|
||||
|
||||
projection.release();
|
||||
}
|
||||
|
||||
private void startPlayback(@NonNull RecyclerView parent, @NonNull GiphyMp4ProjectionPlayerHolder holder, @NonNull GiphyMp4Playable giphyMp4Playable) {
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.sms.MessageSender;
|
|||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.ExpirationUtil;
|
||||
import org.thoughtcrime.securesms.util.Projection;
|
||||
import org.thoughtcrime.securesms.util.ProjectionList;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.sql.Date;
|
||||
|
@ -250,7 +251,7 @@ final class MessageHeaderViewHolder extends RecyclerView.ViewHolder implements G
|
|||
}
|
||||
|
||||
@Override
|
||||
public @NonNull List<Projection> getColorizerProjections(@NonNull ViewGroup coordinateRoot) {
|
||||
public @NonNull ProjectionList getColorizerProjections(@NonNull ViewGroup coordinateRoot) {
|
||||
return conversationItem.getColorizerProjections(coordinateRoot);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import android.view.ViewGroup;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.util.Pools;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
|
@ -24,30 +25,41 @@ import java.util.Objects;
|
|||
*/
|
||||
public final class Projection {
|
||||
|
||||
private final float x;
|
||||
private final float y;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final Corners corners;
|
||||
private final Path path;
|
||||
private final RectF rect;
|
||||
private float x;
|
||||
private float y;
|
||||
private int width;
|
||||
private int height;
|
||||
private Corners corners;
|
||||
private Path path;
|
||||
private RectF rect;
|
||||
|
||||
public Projection(float x, float y, int width, int height, @Nullable Corners corners) {
|
||||
private Projection() {
|
||||
x = 0f;
|
||||
y = 0f;
|
||||
width = 0;
|
||||
height = 0;
|
||||
corners = null;
|
||||
path = new Path();
|
||||
rect = new RectF();
|
||||
}
|
||||
|
||||
private Projection set(float x, float y, int width, int height, @Nullable Corners corners) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.corners = corners;
|
||||
this.path = new Path();
|
||||
|
||||
rect = new RectF();
|
||||
rect.set(x, y, x + width, y + height);
|
||||
path.reset();
|
||||
|
||||
if (corners != null) {
|
||||
path.addRoundRect(rect, corners.toRadii(), Path.Direction.CW);
|
||||
} else {
|
||||
path.addRect(rect, Path.Direction.CW);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getX() {
|
||||
|
@ -86,35 +98,12 @@ public final class Projection {
|
|||
}
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final Projection that = (Projection) o;
|
||||
return Float.compare(that.x, x) == 0 &&
|
||||
Float.compare(that.y, y) == 0 &&
|
||||
width == that.width &&
|
||||
height == that.height &&
|
||||
Objects.equals(corners, that.corners);
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
return Objects.hash(x, y, width, height, corners);
|
||||
}
|
||||
|
||||
public @NonNull Projection translateX(float xTranslation) {
|
||||
return new Projection(x + xTranslation, y, width, height, corners);
|
||||
return set(x + xTranslation, y, width, height, corners);
|
||||
}
|
||||
|
||||
public @NonNull Projection translateY(float yTranslation) {
|
||||
return new Projection(x, y + yTranslation, width, height, corners);
|
||||
}
|
||||
|
||||
public @NonNull Projection withDimensions(int width, int height) {
|
||||
return new Projection(x, y, width, height, corners);
|
||||
}
|
||||
|
||||
public @NonNull Projection withHeight(int height) {
|
||||
return new Projection(x, y, width, height, corners);
|
||||
return set(x, y + yTranslation, width, height, corners);
|
||||
}
|
||||
|
||||
public static @NonNull Projection relativeToParent(@NonNull ViewGroup parent, @NonNull View view, @Nullable Corners corners) {
|
||||
|
@ -122,7 +111,7 @@ public final class Projection {
|
|||
|
||||
view.getDrawingRect(viewBounds);
|
||||
parent.offsetDescendantRectToMyCoords(view, viewBounds);
|
||||
return new Projection(viewBounds.left, viewBounds.top, view.getWidth(), view.getHeight(), corners);
|
||||
return acquireAndSet(viewBounds.left, viewBounds.top, view.getWidth(), view.getHeight(), corners);
|
||||
}
|
||||
|
||||
public static @NonNull Projection relativeToViewRoot(@NonNull View view, @Nullable Corners corners) {
|
||||
|
@ -132,7 +121,7 @@ public final class Projection {
|
|||
view.getDrawingRect(viewBounds);
|
||||
root.offsetDescendantRectToMyCoords(view, viewBounds);
|
||||
|
||||
return new Projection(viewBounds.left, viewBounds.top, view.getWidth(), view.getHeight(), corners);
|
||||
return acquireAndSet(viewBounds.left, viewBounds.top, view.getWidth(), view.getHeight(), corners);
|
||||
}
|
||||
|
||||
public static @NonNull Projection relativeToViewWithCommonRoot(@NonNull View toProject, @NonNull View viewWithCommonRoot, @Nullable Corners corners) {
|
||||
|
@ -143,7 +132,7 @@ public final class Projection {
|
|||
root.offsetDescendantRectToMyCoords(toProject, viewBounds);
|
||||
root.offsetRectIntoDescendantCoords(viewWithCommonRoot, viewBounds);
|
||||
|
||||
return new Projection(viewBounds.left, viewBounds.top, toProject.getWidth(), toProject.getHeight(), corners);
|
||||
return acquireAndSet(viewBounds.left, viewBounds.top, toProject.getWidth(), toProject.getHeight(), corners);
|
||||
}
|
||||
|
||||
public static @NonNull Projection translateFromDescendantToParentCoords(@NonNull Projection descendantProjection, @NonNull View descendant, @NonNull ViewGroup parent) {
|
||||
|
@ -153,7 +142,7 @@ public final class Projection {
|
|||
|
||||
parent.offsetDescendantRectToMyCoords(descendant, viewBounds);
|
||||
|
||||
return new Projection(viewBounds.left, viewBounds.top, descendantProjection.width, descendantProjection.height, descendantProjection.corners);
|
||||
return acquireAndSet(viewBounds.left, viewBounds.top, descendantProjection.width, descendantProjection.height, descendantProjection.corners);
|
||||
}
|
||||
|
||||
public static @NonNull List<Projection> getCapAndTail(@NonNull Projection parentProjection, @NonNull Projection childProjection) {
|
||||
|
@ -187,11 +176,47 @@ public final class Projection {
|
|||
}
|
||||
|
||||
return Arrays.asList(
|
||||
new Projection(topX, topY, topWidth, topHeight, topCorners),
|
||||
new Projection(bottomX, bottomY, bottomWidth, bottomHeight, bottomCorners)
|
||||
acquireAndSet(topX, topY, topWidth, topHeight, topCorners),
|
||||
acquireAndSet(bottomX, bottomY, bottomWidth, bottomHeight, bottomCorners)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* We keep a maximum of 125 Projections around at any one time.
|
||||
*/
|
||||
private static final Pools.SimplePool<Projection> projectionPool = new Pools.SimplePool<>(125);
|
||||
|
||||
/**
|
||||
* Acquire a projection. This will try to grab one from the pool, and, upon failure, will
|
||||
* allocate a new one instead.
|
||||
*/
|
||||
private static @NonNull Projection acquire() {
|
||||
Projection fromPool = projectionPool.acquire();
|
||||
if (fromPool != null) {
|
||||
return fromPool;
|
||||
} else {
|
||||
return new Projection();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire a projection and set its fields as specified.
|
||||
*/
|
||||
private static @NonNull Projection acquireAndSet(float x, float y, int width, int height, @Nullable Corners corners) {
|
||||
Projection projection = acquire();
|
||||
projection.set(x, y, width, height, corners);
|
||||
|
||||
return projection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Projections should only be kept around for the absolute maximum amount of time they are needed.
|
||||
*/
|
||||
public void release() {
|
||||
projectionPool.release(this);
|
||||
}
|
||||
|
||||
|
||||
public static final class Corners {
|
||||
private final float topLeft;
|
||||
private final float topRight;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package org.thoughtcrime.securesms.util
|
||||
|
||||
import java.io.Closeable
|
||||
|
||||
class ProjectionList(size: Int = 0) : ArrayList<Projection>(size), Closeable {
|
||||
override fun close() {
|
||||
forEach { it.release() }
|
||||
}
|
||||
}
|
Ładowanie…
Reference in New Issue