Drastically reduce number of projection instances we create.

Via SimplePool
fork-5.53.8
Alex Hart 2021-10-25 14:12:08 -03:00 zatwierdzone przez GitHub
rodzic 98fce53cf1
commit b34bb2e7d7
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
12 zmienionych plików z 145 dodań i 64 usunięć

Wyświetl plik

@ -57,6 +57,7 @@ import org.thoughtcrime.securesms.util.CachedInflater;
import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.MessageRecordUtil; import org.thoughtcrime.securesms.util.MessageRecordUtil;
import org.thoughtcrime.securesms.util.Projection; import org.thoughtcrime.securesms.util.Projection;
import org.thoughtcrime.securesms.util.ProjectionList;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration; import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.ViewUtil;
@ -704,7 +705,7 @@ public class ConversationAdapter
} }
@Override @Override
public @NonNull List<Projection> getColorizerProjections(@NonNull ViewGroup coordinateRoot) { public @NonNull ProjectionList getColorizerProjections(@NonNull ViewGroup coordinateRoot) {
return getBindable().getColorizerProjections(coordinateRoot); return getBindable().getColorizerProjections(coordinateRoot);
} }
} }

Wyświetl plik

@ -122,6 +122,7 @@ import org.thoughtcrime.securesms.util.InterceptableLongClickCopyLinkSpan;
import org.thoughtcrime.securesms.util.LongClickMovementMethod; import org.thoughtcrime.securesms.util.LongClickMovementMethod;
import org.thoughtcrime.securesms.util.MessageRecordUtil; import org.thoughtcrime.securesms.util.MessageRecordUtil;
import org.thoughtcrime.securesms.util.Projection; import org.thoughtcrime.securesms.util.Projection;
import org.thoughtcrime.securesms.util.ProjectionList;
import org.thoughtcrime.securesms.util.SearchUtil; import org.thoughtcrime.securesms.util.SearchUtil;
import org.thoughtcrime.securesms.util.StringUtil; import org.thoughtcrime.securesms.util.StringUtil;
import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.ThemeUtil;
@ -221,6 +222,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
private Colorizer colorizer; private Colorizer colorizer;
private boolean hasWallpaper; private boolean hasWallpaper;
private float lastYDownRelativeToThis; private float lastYDownRelativeToThis;
private ProjectionList colorizerProjections = new ProjectionList(3);
public ConversationItem(Context context) { public ConversationItem(Context context) {
this(context, null); this(context, null);
@ -530,6 +532,10 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
if (conversationRecipient != null) { if (conversationRecipient != null) {
conversationRecipient.removeForeverObserver(this); conversationRecipient.removeForeverObserver(this);
} }
bodyBubble.setVideoPlayerProjection(null);
bodyBubble.setQuoteViewProjection(null);
cancelPulseOutlinerAnimation(); cancelPulseOutlinerAnimation();
} }
@ -588,12 +594,17 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
} }
private static int getProjectionTop(@NonNull View child) { 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) { private static int getProjectionBottom(@NonNull View child) {
Projection projection = Projection.relativeToViewRoot(child, null); Projection projection = Projection.relativeToViewRoot(child, null);
return (int) projection.getY() + projection.getHeight(); int bottom = (int) projection.getY() + projection.getHeight();
projection.release();
return bottom;
} }
@Override @Override
@ -1719,8 +1730,8 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
} }
@Override @Override
public @NonNull List<Projection> getColorizerProjections(@NonNull ViewGroup coordinateRoot) { public @NonNull ProjectionList getColorizerProjections(@NonNull ViewGroup coordinateRoot) {
List<Projection> projections = new LinkedList<>(); colorizerProjections.clear();
if (messageRecord.isOutgoing() && if (messageRecord.isOutgoing() &&
!hasNoBubble(messageRecord) && !hasNoBubble(messageRecord) &&
@ -1731,9 +1742,9 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
Projection videoToBubble = bodyBubble.getVideoPlayerProjection(); Projection videoToBubble = bodyBubble.getVideoPlayerProjection();
if (videoToBubble != null) { if (videoToBubble != null) {
Projection videoToRoot = Projection.translateFromDescendantToParentCoords(videoToBubble, bodyBubble, coordinateRoot); Projection videoToRoot = Projection.translateFromDescendantToParentCoords(videoToBubble, bodyBubble, coordinateRoot);
projections.addAll(Projection.getCapAndTail(bodyBubbleToRoot, videoToRoot)); colorizerProjections.addAll(Projection.getCapAndTail(bodyBubbleToRoot, videoToRoot));
} else { } 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); Projection footerProjection = getActiveFooter(messageRecord).getProjection(coordinateRoot);
if (footerProjection != null) { 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) quoteView != null)
{ {
bodyBubble.setQuoteViewProjection(quoteView.getProjection(bodyBubble)); 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 @Override

Wyświetl plik

@ -63,11 +63,19 @@ public class ConversationItemBodyBubble extends LinearLayout {
} }
public void setQuoteViewProjection(@Nullable Projection quoteViewProjection) { public void setQuoteViewProjection(@Nullable Projection quoteViewProjection) {
if (this.quoteViewProjection != null) {
this.quoteViewProjection.release();
}
this.quoteViewProjection = quoteViewProjection; this.quoteViewProjection = quoteViewProjection;
clipProjectionDrawable.setProjections(getProjections()); clipProjectionDrawable.setProjections(getProjections());
} }
public void setVideoPlayerProjection(@Nullable Projection videoPlayerProjection) { public void setVideoPlayerProjection(@Nullable Projection videoPlayerProjection) {
if (this.videoPlayerProjection != null) {
this.videoPlayerProjection.release();
}
this.videoPlayerProjection = videoPlayerProjection; this.videoPlayerProjection = videoPlayerProjection;
clipProjectionDrawable.setProjections(getProjections()); clipProjectionDrawable.setProjections(getProjections());
} }

Wyświetl plik

@ -39,6 +39,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.Projection; import org.thoughtcrime.securesms.util.Projection;
import org.thoughtcrime.securesms.util.ProjectionList;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
@ -59,7 +60,9 @@ import java.util.concurrent.ExecutionException;
public final class ConversationUpdateItem extends FrameLayout public final class ConversationUpdateItem extends FrameLayout
implements BindableConversationItem 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; private Set<MultiselectPart> batchSelected;
@ -221,8 +224,8 @@ public final class ConversationUpdateItem extends FrameLayout
} }
@Override @Override
public @NonNull List<Projection> getColorizerProjections(@NonNull ViewGroup coordinateRoot) { public @NonNull ProjectionList getColorizerProjections(@NonNull ViewGroup coordinateRoot) {
return Collections.emptyList(); return EMPTY_PROJECTION_LIST;
} }
@Override @Override

Wyświetl plik

@ -1,12 +1,12 @@
package org.thoughtcrime.securesms.conversation.colors package org.thoughtcrime.securesms.conversation.colors
import android.view.ViewGroup 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 * Denotes that a class can be colorized. The class is responsible for
* generating its own projection. * generating its own projection.
*/ */
interface Colorizable { interface Colorizable {
fun getColorizerProjections(coordinateRoot: ViewGroup): List<Projection> fun getColorizerProjections(coordinateRoot: ViewGroup): ProjectionList
} }

Wyświetl plik

@ -40,4 +40,9 @@ class ColorizerView @JvmOverloads constructor(
super.draw(canvas) super.draw(canvas)
} }
} }
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
projections.forEach { it.release() }
}
} }

Wyświetl plik

@ -98,8 +98,10 @@ class RecyclerViewColorizer(private val recyclerView: RecyclerView) {
if (child != null) { if (child != null) {
val holder = parent.getChildViewHolder(child) val holder = parent.getChildViewHolder(child)
if (holder is Colorizable) { if (holder is Colorizable) {
holder.getColorizerProjections(parent).forEach { holder.getColorizerProjections(parent).use { list ->
c.drawPath(it.path, holePunchPaint) list.forEach {
c.drawPath(it.path, holePunchPaint)
}
} }
} }
} }

Wyświetl plik

@ -21,7 +21,6 @@ import androidx.recyclerview.widget.RecyclerView
import com.airbnb.lottie.SimpleColorFilter import com.airbnb.lottie.SimpleColorFilter
import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.conversation.ConversationAdapter import org.thoughtcrime.securesms.conversation.ConversationAdapter
import org.thoughtcrime.securesms.util.Projection
import org.thoughtcrime.securesms.util.SetUtil import org.thoughtcrime.securesms.util.SetUtil
import org.thoughtcrime.securesms.util.ThemeUtil import org.thoughtcrime.securesms.util.ThemeUtil
import org.thoughtcrime.securesms.util.ViewUtil import org.thoughtcrime.securesms.util.ViewUtil
@ -157,9 +156,17 @@ class MultiselectItemDecoration(
val parts: MultiselectCollection = child.conversationMessage.multiselectCollection 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() path.reset()
projections.forEach { it.applyToPath(path) } projections.use { list ->
list.forEach {
it.applyToPath(path)
}
}
canvas.save() canvas.save()
canvas.clipPath(path, Region.Op.DIFFERENCE) canvas.clipPath(path, Region.Op.DIFFERENCE)
@ -341,13 +348,16 @@ class MultiselectItemDecoration(
parent.forEach { child -> parent.forEach { child ->
if (child is Multiselectable && child.conversationMessage == inFocus.conversationMessage) { 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) path.addRect(child.left.toFloat(), child.top.toFloat(), child.right.toFloat(), child.bottom.toFloat(), Path.Direction.CW)
child.getColorizerProjections(parent).forEach { child.getColorizerProjections(parent).use { list ->
path.op(it.path, Path.Op.DIFFERENCE) list.forEach {
path.op(it.path, Path.Op.DIFFERENCE)
}
} }
if (child.canPlayContent()) { if (child.canPlayContent()) {
val mp4GifProjection = child.getGiphyMp4PlayableProjection(child.rootView as ViewGroup) val mp4GifProjection = child.getGiphyMp4PlayableProjection(child.rootView as ViewGroup)
path.op(mp4GifProjection.path, Path.Op.DIFFERENCE) path.op(mp4GifProjection.path, Path.Op.DIFFERENCE)
mp4GifProjection.release()
} }
} }
} }

Wyświetl plik

@ -105,6 +105,8 @@ public final class GiphyMp4ProjectionRecycler implements GiphyMp4PlaybackControl
} }
holder.setCorners(projection.getCorners()); holder.setCorners(projection.getCorners());
projection.release();
} }
private void startPlayback(@NonNull RecyclerView parent, @NonNull GiphyMp4ProjectionPlayerHolder holder, @NonNull GiphyMp4Playable giphyMp4Playable) { private void startPlayback(@NonNull RecyclerView parent, @NonNull GiphyMp4ProjectionPlayerHolder holder, @NonNull GiphyMp4Playable giphyMp4Playable) {

Wyświetl plik

@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.DateUtils; import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.ExpirationUtil; import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.Projection; import org.thoughtcrime.securesms.util.Projection;
import org.thoughtcrime.securesms.util.ProjectionList;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import java.sql.Date; import java.sql.Date;
@ -250,7 +251,7 @@ final class MessageHeaderViewHolder extends RecyclerView.ViewHolder implements G
} }
@Override @Override
public @NonNull List<Projection> getColorizerProjections(@NonNull ViewGroup coordinateRoot) { public @NonNull ProjectionList getColorizerProjections(@NonNull ViewGroup coordinateRoot) {
return conversationItem.getColorizerProjections(coordinateRoot); return conversationItem.getColorizerProjections(coordinateRoot);
} }

Wyświetl plik

@ -9,6 +9,7 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.util.Pools;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
@ -24,30 +25,41 @@ import java.util.Objects;
*/ */
public final class Projection { public final class Projection {
private final float x; private float x;
private final float y; private float y;
private final int width; private int width;
private final int height; private int height;
private final Corners corners; private Corners corners;
private final Path path; private Path path;
private final RectF rect; 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.x = x;
this.y = y; this.y = y;
this.width = width; this.width = width;
this.height = height; this.height = height;
this.corners = corners; this.corners = corners;
this.path = new Path();
rect = new RectF();
rect.set(x, y, x + width, y + height); rect.set(x, y, x + width, y + height);
path.reset();
if (corners != null) { if (corners != null) {
path.addRoundRect(rect, corners.toRadii(), Path.Direction.CW); path.addRoundRect(rect, corners.toRadii(), Path.Direction.CW);
} else { } else {
path.addRect(rect, Path.Direction.CW); path.addRect(rect, Path.Direction.CW);
} }
return this;
} }
public float getX() { 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) { 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) { public @NonNull Projection translateY(float yTranslation) {
return new Projection(x, y + yTranslation, width, height, corners); return set(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);
} }
public static @NonNull Projection relativeToParent(@NonNull ViewGroup parent, @NonNull View view, @Nullable Corners 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); view.getDrawingRect(viewBounds);
parent.offsetDescendantRectToMyCoords(view, 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) { public static @NonNull Projection relativeToViewRoot(@NonNull View view, @Nullable Corners corners) {
@ -132,7 +121,7 @@ public final class Projection {
view.getDrawingRect(viewBounds); view.getDrawingRect(viewBounds);
root.offsetDescendantRectToMyCoords(view, 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) { 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.offsetDescendantRectToMyCoords(toProject, viewBounds);
root.offsetRectIntoDescendantCoords(viewWithCommonRoot, 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) { 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); 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) { public static @NonNull List<Projection> getCapAndTail(@NonNull Projection parentProjection, @NonNull Projection childProjection) {
@ -187,11 +176,47 @@ public final class Projection {
} }
return Arrays.asList( return Arrays.asList(
new Projection(topX, topY, topWidth, topHeight, topCorners), acquireAndSet(topX, topY, topWidth, topHeight, topCorners),
new Projection(bottomX, bottomY, bottomWidth, bottomHeight, bottomCorners) 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 { public static final class Corners {
private final float topLeft; private final float topLeft;
private final float topRight; private final float topRight;

Wyświetl plik

@ -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() }
}
}