Adapt ReadableMapBuffer to MapBuffer interface
Summary: Updates `ReadableMapBuffer` to conform to `MapBuffer` interface, to allow interchangeable use of `Readable/WritableMapBuffer` in the code. Notable changes: - MapBuffer.Entry getters are now represented as Kotlin properties and appended `Value` suffix (e.g. `entry.getInt()` becomes `entry.getIntValue()` in Java, or `entry.intValue` in Kotlin) - `ByteBuffer` is imported in constructor instead of on demand. This method doesn't copy the data as the bytes are read directly from native heap, and benchmarking a 500-byte `MapBuffer` shows no difference in import speed. - Internal logic of `ReadableMapBuffer` uses `UShort` kotlin type for key retrieval, for more correct representation of values. - Explicit exception throws are replaced with `require` and `check` methods for `IllegalArgumentException` and `IllegalStateException` (default FB conversion). The change also updates `ReadableMapBuffer` usages to `MapBuffer` interface where possible (virtually everywhere except JNI methods). Changelog: [Android][Changed] - Adopt `MapBuffer` interface for `ReadableMapBuffer` Reviewed By: mdvacca Differential Revision: D35218633 fbshipit-source-id: 515dd974c27b2978ade325b2e1750ab8f068a20a
This commit is contained in:
Родитель
cf6f3b680b
Коммит
81e4249315
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.common.mapbuffer
|
||||
|
||||
import com.facebook.react.bridge.ReactMarker
|
||||
import com.facebook.react.bridge.ReactMarkerConstants
|
||||
import com.facebook.soloader.SoLoader
|
||||
import com.facebook.systrace.Systrace
|
||||
|
||||
object MapBufferSoLoader {
|
||||
@Volatile private var didInit = false
|
||||
|
||||
@JvmStatic
|
||||
fun staticInit() {
|
||||
if (didInit) {
|
||||
return
|
||||
}
|
||||
didInit = true
|
||||
|
||||
Systrace.beginSection(
|
||||
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
|
||||
"ReadableMapBufferSoLoader.staticInit::load:mapbufferjni")
|
||||
ReactMarker.logMarker(ReactMarkerConstants.LOAD_REACT_NATIVE_MAPBUFFER_SO_FILE_START)
|
||||
SoLoader.loadLibrary("mapbufferjni")
|
||||
ReactMarker.logMarker(ReactMarkerConstants.LOAD_REACT_NATIVE_MAPBUFFER_SO_FILE_END)
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE)
|
||||
}
|
||||
}
|
|
@ -1,418 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.common.mapbuffer;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.jni.HybridData;
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* TODO T83483191: add documentation.
|
||||
*
|
||||
* <p>NOTE: {@link ReadableMapBuffer} is NOT thread safe.
|
||||
*/
|
||||
public class ReadableMapBuffer implements Iterable<ReadableMapBuffer.MapBufferEntry> {
|
||||
|
||||
static {
|
||||
ReadableMapBufferSoLoader.staticInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Data types supported by MapBuffer. Keep in sync with definition in `MapBuffer.h`, as enum
|
||||
* serialization relies on correct order.
|
||||
*/
|
||||
public enum DataType {
|
||||
BOOL,
|
||||
INT,
|
||||
DOUBLE,
|
||||
STRING,
|
||||
MAP;
|
||||
}
|
||||
|
||||
// Value used to verify if the data is serialized with LittleEndian order.
|
||||
private static final int ALIGNMENT = 0xFE;
|
||||
|
||||
// 8 bytes = 2 (alignment) + 2 (count) + 4 (size)
|
||||
private static final int HEADER_SIZE = 8;
|
||||
|
||||
// 10 bytes = 2 (key) + 2 (type) + 8 (value)
|
||||
private static final int BUCKET_SIZE = 12;
|
||||
|
||||
// 2 bytes = 2 (key)
|
||||
private static final int TYPE_OFFSET = 2;
|
||||
|
||||
// 4 bytes = 2 (key) + 2 (type)
|
||||
private static final int VALUE_OFFSET = 4;
|
||||
|
||||
private static final int INT_SIZE = 4;
|
||||
|
||||
@Nullable ByteBuffer mBuffer = null;
|
||||
|
||||
// Amount of items serialized on the ByteBuffer
|
||||
private int mCount = 0;
|
||||
|
||||
@DoNotStrip
|
||||
private ReadableMapBuffer(HybridData hybridData) {
|
||||
mHybridData = hybridData;
|
||||
}
|
||||
|
||||
private ReadableMapBuffer(ByteBuffer buffer) {
|
||||
mBuffer = buffer;
|
||||
readHeader();
|
||||
}
|
||||
|
||||
private native ByteBuffer importByteBuffer();
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@DoNotStrip
|
||||
@Nullable
|
||||
private HybridData mHybridData;
|
||||
|
||||
private static int getKeyOffsetForBucketIndex(int bucketIndex) {
|
||||
return HEADER_SIZE + BUCKET_SIZE * bucketIndex;
|
||||
}
|
||||
|
||||
// returns the relative offset of the first byte of dynamic data
|
||||
private int getOffsetForDynamicData() {
|
||||
// TODO T83483191: check if there's dynamic data?
|
||||
return getKeyOffsetForBucketIndex(mCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key Key to search for
|
||||
* @return the "bucket index" for a key or -1 if not found. It uses a binary search algorithm
|
||||
* (log(n))
|
||||
*/
|
||||
private int getBucketIndexForKey(int key) {
|
||||
importByteBufferAndReadHeader();
|
||||
int lo = 0;
|
||||
int hi = getCount() - 1;
|
||||
while (lo <= hi) {
|
||||
final int mid = (lo + hi) >>> 1;
|
||||
final int midVal = readUnsignedShort(getKeyOffsetForBucketIndex(mid));
|
||||
if (midVal < key) {
|
||||
lo = mid + 1;
|
||||
} else if (midVal > key) {
|
||||
hi = mid - 1;
|
||||
} else {
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private DataType readDataType(int bucketIndex) {
|
||||
int value = readUnsignedShort(getKeyOffsetForBucketIndex(bucketIndex) + TYPE_OFFSET);
|
||||
return DataType.values()[value];
|
||||
}
|
||||
|
||||
private int getTypedValueOffsetForKey(int key, DataType expected) {
|
||||
int bucketIndex = getBucketIndexForKey(key);
|
||||
if (bucketIndex == -1) {
|
||||
throw new IllegalArgumentException("Key not found: " + key);
|
||||
}
|
||||
|
||||
DataType dataType = readDataType(bucketIndex);
|
||||
if (dataType != expected) {
|
||||
throw new IllegalStateException(
|
||||
"Expected "
|
||||
+ expected
|
||||
+ " for key: "
|
||||
+ key
|
||||
+ " found "
|
||||
+ dataType.toString()
|
||||
+ " instead.");
|
||||
}
|
||||
|
||||
return getKeyOffsetForBucketIndex(bucketIndex) + VALUE_OFFSET;
|
||||
}
|
||||
|
||||
private int readUnsignedShort(int bufferPosition) {
|
||||
return mBuffer.getShort(bufferPosition) & 0xFFFF;
|
||||
}
|
||||
|
||||
private double readDoubleValue(int bufferPosition) {
|
||||
return mBuffer.getDouble(bufferPosition);
|
||||
}
|
||||
|
||||
private int readIntValue(int bufferPosition) {
|
||||
return mBuffer.getInt(bufferPosition);
|
||||
}
|
||||
|
||||
private boolean readBooleanValue(int bufferPosition) {
|
||||
return readIntValue(bufferPosition) == 1;
|
||||
}
|
||||
|
||||
private String readStringValue(int bufferPosition) {
|
||||
int offset = getOffsetForDynamicData() + mBuffer.getInt(bufferPosition);
|
||||
|
||||
int sizeOfString = mBuffer.getInt(offset);
|
||||
byte[] result = new byte[sizeOfString];
|
||||
|
||||
int stringOffset = offset + INT_SIZE;
|
||||
|
||||
mBuffer.position(stringOffset);
|
||||
mBuffer.get(result, 0, sizeOfString);
|
||||
|
||||
return new String(result);
|
||||
}
|
||||
|
||||
private ReadableMapBuffer readMapBufferValue(int position) {
|
||||
int offset = getOffsetForDynamicData() + mBuffer.getInt(position);
|
||||
|
||||
int sizeMapBuffer = mBuffer.getInt(offset);
|
||||
byte[] buffer = new byte[sizeMapBuffer];
|
||||
|
||||
int bufferOffset = offset + INT_SIZE;
|
||||
|
||||
mBuffer.position(bufferOffset);
|
||||
mBuffer.get(buffer, 0, sizeMapBuffer);
|
||||
|
||||
return new ReadableMapBuffer(ByteBuffer.wrap(buffer));
|
||||
}
|
||||
|
||||
private void readHeader() {
|
||||
// byte order
|
||||
short storedAlignment = mBuffer.getShort();
|
||||
if (storedAlignment != ALIGNMENT) {
|
||||
mBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
// count
|
||||
mCount = readUnsignedShort(mBuffer.position());
|
||||
}
|
||||
|
||||
/**
|
||||
* Binary search of the key inside the mapBuffer (log(n)).
|
||||
*
|
||||
* @param key Key to search for
|
||||
* @return true if and only if the Key received as a parameter is stored in the MapBuffer.
|
||||
*/
|
||||
public boolean hasKey(int key) {
|
||||
// TODO T83483191: Add tests
|
||||
return getBucketIndexForKey(key) != -1;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DataType getType(int key) {
|
||||
int bucketIndex = getBucketIndexForKey(key);
|
||||
|
||||
if (bucketIndex == -1) {
|
||||
throw new IllegalArgumentException("Key not found: " + key);
|
||||
}
|
||||
|
||||
return readDataType(bucketIndex);
|
||||
}
|
||||
|
||||
/** @return amount of elements stored into the MapBuffer */
|
||||
public int getCount() {
|
||||
importByteBufferAndReadHeader();
|
||||
return mCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key {@link int} representing the key
|
||||
* @return return the int associated to the Key received as a parameter.
|
||||
*/
|
||||
public int getInt(int key) {
|
||||
return readIntValue(getTypedValueOffsetForKey(key, DataType.INT));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key {@link int} representing the key
|
||||
* @return return the double associated to the Key received as a parameter.
|
||||
*/
|
||||
public double getDouble(int key) {
|
||||
return readDoubleValue(getTypedValueOffsetForKey(key, DataType.DOUBLE));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key {@link int} representing the key
|
||||
* @return return the int associated to the Key received as a parameter.
|
||||
*/
|
||||
public String getString(int key) {
|
||||
return readStringValue(getTypedValueOffsetForKey(key, DataType.STRING));
|
||||
}
|
||||
|
||||
public boolean getBoolean(int key) {
|
||||
return readBooleanValue(getTypedValueOffsetForKey(key, DataType.BOOL));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key {@link int} representing the key
|
||||
* @return return the int associated to the Key received as a parameter.
|
||||
*/
|
||||
public ReadableMapBuffer getMapBuffer(int key) {
|
||||
return readMapBufferValue(getTypedValueOffsetForKey(key, DataType.MAP));
|
||||
}
|
||||
|
||||
/**
|
||||
* Import ByteBuffer from C++, read the header and move the current cursor at the start of the
|
||||
* payload.
|
||||
*/
|
||||
private ByteBuffer importByteBufferAndReadHeader() {
|
||||
if (mBuffer != null) {
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
// mBuffer = importByteBufferAllocateDirect();
|
||||
mBuffer = importByteBuffer();
|
||||
|
||||
readHeader();
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
private void assertKeyExists(int key, int bucketIndex) {
|
||||
int storedKey = readUnsignedShort(getKeyOffsetForBucketIndex(bucketIndex));
|
||||
if (storedKey != key) {
|
||||
throw new IllegalStateException(
|
||||
"Stored key doesn't match parameter - expected: " + key + " - found: " + storedKey);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
ByteBuffer byteBuffer = importByteBufferAndReadHeader();
|
||||
byteBuffer.rewind();
|
||||
return byteBuffer.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (!(obj instanceof ReadableMapBuffer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ReadableMapBuffer other = (ReadableMapBuffer) obj;
|
||||
ByteBuffer thisByteBuffer = importByteBufferAndReadHeader();
|
||||
ByteBuffer otherByteBuffer = other.importByteBufferAndReadHeader();
|
||||
if (thisByteBuffer == otherByteBuffer) {
|
||||
return true;
|
||||
}
|
||||
thisByteBuffer.rewind();
|
||||
otherByteBuffer.rewind();
|
||||
return thisByteBuffer.equals(otherByteBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder("{");
|
||||
for (MapBufferEntry entry : this) {
|
||||
int key = entry.getKey();
|
||||
builder.append(key);
|
||||
builder.append('=');
|
||||
switch (entry.getType()) {
|
||||
case BOOL:
|
||||
builder.append(entry.getBoolean());
|
||||
break;
|
||||
case INT:
|
||||
builder.append(entry.getInt());
|
||||
break;
|
||||
case DOUBLE:
|
||||
builder.append(entry.getDouble());
|
||||
break;
|
||||
case STRING:
|
||||
builder.append(entry.getString());
|
||||
break;
|
||||
case MAP:
|
||||
builder.append(entry.getReadableMapBuffer().toString());
|
||||
break;
|
||||
}
|
||||
builder.append(',');
|
||||
}
|
||||
builder.append('}');
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/** @return an {@link Iterator<MapBufferEntry>} for the entries of this MapBuffer. */
|
||||
@Override
|
||||
public Iterator<MapBufferEntry> iterator() {
|
||||
return new Iterator<MapBufferEntry>() {
|
||||
int current = 0;
|
||||
final int last = getCount() - 1;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return current <= last;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapBufferEntry next() {
|
||||
return new MapBufferEntry(getKeyOffsetForBucketIndex(current++));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** This class represents an Entry of the {@link ReadableMapBuffer} class. */
|
||||
public class MapBufferEntry {
|
||||
private final int mBucketOffset;
|
||||
|
||||
private MapBufferEntry(int position) {
|
||||
mBucketOffset = position;
|
||||
}
|
||||
|
||||
private void assertType(DataType expected) {
|
||||
DataType dataType = getType();
|
||||
if (expected != dataType) {
|
||||
throw new IllegalStateException(
|
||||
"Expected "
|
||||
+ expected
|
||||
+ " for key: "
|
||||
+ getKey()
|
||||
+ " found "
|
||||
+ dataType.toString()
|
||||
+ " instead.");
|
||||
}
|
||||
}
|
||||
|
||||
/** @return a {@link short} that represents the key of this {@link MapBufferEntry}. */
|
||||
public int getKey() {
|
||||
return readUnsignedShort(mBucketOffset);
|
||||
}
|
||||
|
||||
public DataType getType() {
|
||||
return DataType.values()[readUnsignedShort(mBucketOffset + TYPE_OFFSET)];
|
||||
}
|
||||
|
||||
/** @return the double value that is stored in this {@link MapBufferEntry}. */
|
||||
public double getDouble() {
|
||||
// TODO T83483191 Extend serialization of MapBuffer to return null if there's no value
|
||||
// stored in this MapBufferEntry.
|
||||
assertType(DataType.DOUBLE);
|
||||
return readDoubleValue(mBucketOffset + VALUE_OFFSET);
|
||||
}
|
||||
|
||||
/** @return the int value that is stored in this {@link MapBufferEntry}. */
|
||||
public int getInt() {
|
||||
assertType(DataType.INT);
|
||||
return readIntValue(mBucketOffset + VALUE_OFFSET);
|
||||
}
|
||||
|
||||
/** @return the boolean value that is stored in this {@link MapBufferEntry}. */
|
||||
public boolean getBoolean() {
|
||||
assertType(DataType.BOOL);
|
||||
return readBooleanValue(mBucketOffset + VALUE_OFFSET);
|
||||
}
|
||||
|
||||
/** @return the String value that is stored in this {@link MapBufferEntry}. */
|
||||
public String getString() {
|
||||
assertType(DataType.STRING);
|
||||
return readStringValue(mBucketOffset + VALUE_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link ReadableMapBuffer} value that is stored in this {@link MapBufferEntry}.
|
||||
*/
|
||||
public ReadableMapBuffer getReadableMapBuffer() {
|
||||
assertType(DataType.MAP);
|
||||
return readMapBufferValue(mBucketOffset + VALUE_OFFSET);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,298 @@
|
|||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.common.mapbuffer
|
||||
|
||||
import com.facebook.jni.HybridData
|
||||
import com.facebook.proguard.annotations.DoNotStrip
|
||||
import com.facebook.react.common.mapbuffer.MapBuffer.Companion.KEY_RANGE
|
||||
import java.lang.StringBuilder
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import javax.annotation.concurrent.NotThreadSafe
|
||||
|
||||
/**
|
||||
* Read-only implementation of the [MapBuffer], imported from C++ environment. Use
|
||||
* `<react/common/mapbuffer/jni/JReadableMapBuffer.h> to create it.
|
||||
*
|
||||
* See [MapBuffer] documentation for more details
|
||||
*/
|
||||
@NotThreadSafe
|
||||
@DoNotStrip
|
||||
class ReadableMapBuffer : MapBuffer {
|
||||
|
||||
// Hybrid data must be kept in the `mHybridData` field for fbjni to work
|
||||
@field:DoNotStrip private val mHybridData: HybridData?
|
||||
|
||||
// Byte data of the mapBuffer
|
||||
private val buffer: ByteBuffer
|
||||
// Amount of items serialized on the ByteBuffer
|
||||
override var count = 0
|
||||
private set
|
||||
|
||||
@DoNotStrip
|
||||
private constructor(hybridData: HybridData) {
|
||||
this.mHybridData = hybridData
|
||||
this.buffer = importByteBuffer()
|
||||
readHeader()
|
||||
}
|
||||
|
||||
private constructor(buffer: ByteBuffer) {
|
||||
this.mHybridData = null
|
||||
this.buffer = buffer
|
||||
readHeader()
|
||||
}
|
||||
|
||||
private external fun importByteBuffer(): ByteBuffer
|
||||
|
||||
private fun readHeader() {
|
||||
// byte order
|
||||
val storedAlignment = buffer.short
|
||||
if (storedAlignment.toInt() != ALIGNMENT) {
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN)
|
||||
}
|
||||
// count
|
||||
count = readUnsignedShort(buffer.position()).toInt()
|
||||
}
|
||||
|
||||
// returns the relative offset of the first byte of dynamic data
|
||||
private val offsetForDynamicData: Int
|
||||
get() = getKeyOffsetForBucketIndex(count)
|
||||
|
||||
/**
|
||||
* @param key Key to search for
|
||||
* @return the "bucket index" for a key or -1 if not found. It uses a binary search algorithm
|
||||
* (log(n))
|
||||
*/
|
||||
private fun getBucketIndexForKey(intKey: Int): Int {
|
||||
if (intKey !in KEY_RANGE) {
|
||||
return -1
|
||||
}
|
||||
val key = intKey.toUShort()
|
||||
|
||||
var lo = 0
|
||||
var hi = count - 1
|
||||
while (lo <= hi) {
|
||||
val mid = lo + hi ushr 1
|
||||
val midVal = readUnsignedShort(getKeyOffsetForBucketIndex(mid))
|
||||
when {
|
||||
midVal < key -> lo = mid + 1
|
||||
midVal > key -> hi = mid - 1
|
||||
else -> return mid
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
private fun readDataType(bucketIndex: Int): MapBuffer.DataType {
|
||||
val value = readUnsignedShort(getKeyOffsetForBucketIndex(bucketIndex) + TYPE_OFFSET).toInt()
|
||||
return MapBuffer.DataType.values()[value]
|
||||
}
|
||||
|
||||
private fun getTypedValueOffsetForKey(key: Int, expected: MapBuffer.DataType): Int {
|
||||
val bucketIndex = getBucketIndexForKey(key)
|
||||
require(bucketIndex != -1) { "Key not found: $key" }
|
||||
val dataType = readDataType(bucketIndex)
|
||||
check(!(dataType !== expected)) { "Expected $expected for key: $key, found $dataType instead." }
|
||||
return getKeyOffsetForBucketIndex(bucketIndex) + VALUE_OFFSET
|
||||
}
|
||||
|
||||
private fun readUnsignedShort(bufferPosition: Int): UShort {
|
||||
return buffer.getShort(bufferPosition).toUShort()
|
||||
}
|
||||
|
||||
private fun readDoubleValue(bufferPosition: Int): Double {
|
||||
return buffer.getDouble(bufferPosition)
|
||||
}
|
||||
|
||||
private fun readIntValue(bufferPosition: Int): Int {
|
||||
return buffer.getInt(bufferPosition)
|
||||
}
|
||||
|
||||
private fun readBooleanValue(bufferPosition: Int): Boolean {
|
||||
return readIntValue(bufferPosition) == 1
|
||||
}
|
||||
|
||||
private fun readStringValue(bufferPosition: Int): String {
|
||||
val offset = offsetForDynamicData + buffer.getInt(bufferPosition)
|
||||
val sizeOfString = buffer.getInt(offset)
|
||||
val result = ByteArray(sizeOfString)
|
||||
val stringOffset = offset + Int.SIZE_BYTES
|
||||
buffer.position(stringOffset)
|
||||
buffer[result, 0, sizeOfString]
|
||||
return String(result)
|
||||
}
|
||||
|
||||
private fun readMapBufferValue(position: Int): ReadableMapBuffer {
|
||||
val offset = offsetForDynamicData + buffer.getInt(position)
|
||||
val sizeMapBuffer = buffer.getInt(offset)
|
||||
val newBuffer = ByteArray(sizeMapBuffer)
|
||||
val bufferOffset = offset + Int.SIZE_BYTES
|
||||
buffer.position(bufferOffset)
|
||||
buffer[newBuffer, 0, sizeMapBuffer]
|
||||
return ReadableMapBuffer(ByteBuffer.wrap(newBuffer))
|
||||
}
|
||||
|
||||
private fun getKeyOffsetForBucketIndex(bucketIndex: Int): Int {
|
||||
return HEADER_SIZE + BUCKET_SIZE * bucketIndex
|
||||
}
|
||||
|
||||
override fun contains(key: Int): Boolean {
|
||||
// TODO T83483191: Add tests
|
||||
return getBucketIndexForKey(key) != -1
|
||||
}
|
||||
|
||||
override fun getKeyOffset(key: Int): Int = getBucketIndexForKey(key)
|
||||
|
||||
override fun entryAt(offset: Int): MapBuffer.Entry =
|
||||
MapBufferEntry(getKeyOffsetForBucketIndex(offset))
|
||||
|
||||
override fun getType(key: Int): MapBuffer.DataType {
|
||||
val bucketIndex = getBucketIndexForKey(key)
|
||||
require(bucketIndex != -1) { "Key not found: $key" }
|
||||
return readDataType(bucketIndex)
|
||||
}
|
||||
|
||||
override fun getInt(key: Int): Int =
|
||||
readIntValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.INT))
|
||||
|
||||
override fun getDouble(key: Int): Double =
|
||||
readDoubleValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.DOUBLE))
|
||||
|
||||
override fun getString(key: Int): String =
|
||||
readStringValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.STRING))
|
||||
|
||||
override fun getBoolean(key: Int): Boolean =
|
||||
readBooleanValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.BOOL))
|
||||
|
||||
override fun getMapBuffer(key: Int): ReadableMapBuffer =
|
||||
readMapBufferValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.MAP))
|
||||
|
||||
override fun hashCode(): Int {
|
||||
buffer.rewind()
|
||||
return buffer.hashCode()
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is ReadableMapBuffer) {
|
||||
return false
|
||||
}
|
||||
val thisByteBuffer = buffer
|
||||
val otherByteBuffer = other.buffer
|
||||
if (thisByteBuffer === otherByteBuffer) {
|
||||
return true
|
||||
}
|
||||
thisByteBuffer.rewind()
|
||||
otherByteBuffer.rewind()
|
||||
return thisByteBuffer == otherByteBuffer
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
val builder = StringBuilder("{")
|
||||
for (entry in this) {
|
||||
val key = entry.key
|
||||
builder.append(key)
|
||||
builder.append('=')
|
||||
when (entry.type) {
|
||||
MapBuffer.DataType.BOOL -> builder.append(entry.booleanValue)
|
||||
MapBuffer.DataType.INT -> builder.append(entry.intValue)
|
||||
MapBuffer.DataType.DOUBLE -> builder.append(entry.doubleValue)
|
||||
MapBuffer.DataType.STRING -> builder.append(entry.stringValue)
|
||||
MapBuffer.DataType.MAP -> builder.append(entry.mapBufferValue.toString())
|
||||
}
|
||||
builder.append(',')
|
||||
}
|
||||
builder.append('}')
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<MapBuffer.Entry> {
|
||||
return object : Iterator<MapBuffer.Entry> {
|
||||
var current = 0
|
||||
val last = count - 1
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
return current <= last
|
||||
}
|
||||
|
||||
override fun next(): MapBuffer.Entry {
|
||||
return MapBufferEntry(getKeyOffsetForBucketIndex(current++))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class MapBufferEntry(private val bucketOffset: Int) : MapBuffer.Entry {
|
||||
private fun assertType(expected: MapBuffer.DataType) {
|
||||
val dataType = type
|
||||
check(!(expected !== dataType)) {
|
||||
("Expected " +
|
||||
expected +
|
||||
" for key: " +
|
||||
key +
|
||||
" found " +
|
||||
dataType.toString() +
|
||||
" instead.")
|
||||
}
|
||||
}
|
||||
|
||||
override val key: Int
|
||||
get() = readUnsignedShort(bucketOffset).toInt()
|
||||
override val type: MapBuffer.DataType
|
||||
get() = MapBuffer.DataType.values()[readUnsignedShort(bucketOffset + TYPE_OFFSET).toInt()]
|
||||
|
||||
override val doubleValue: Double
|
||||
get() {
|
||||
assertType(MapBuffer.DataType.DOUBLE)
|
||||
return readDoubleValue(bucketOffset + VALUE_OFFSET)
|
||||
}
|
||||
|
||||
override val intValue: Int
|
||||
get() {
|
||||
assertType(MapBuffer.DataType.INT)
|
||||
return readIntValue(bucketOffset + VALUE_OFFSET)
|
||||
}
|
||||
|
||||
override val booleanValue: Boolean
|
||||
get() {
|
||||
assertType(MapBuffer.DataType.BOOL)
|
||||
return readBooleanValue(bucketOffset + VALUE_OFFSET)
|
||||
}
|
||||
|
||||
override val stringValue: String
|
||||
get() {
|
||||
assertType(MapBuffer.DataType.STRING)
|
||||
return readStringValue(bucketOffset + VALUE_OFFSET)
|
||||
}
|
||||
|
||||
override val mapBufferValue: MapBuffer
|
||||
get() {
|
||||
assertType(MapBuffer.DataType.MAP)
|
||||
return readMapBufferValue(bucketOffset + VALUE_OFFSET)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Value used to verify if the data is serialized with LittleEndian order.
|
||||
private const val ALIGNMENT = 0xFE
|
||||
|
||||
// 8 bytes = 2 (alignment) + 2 (count) + 4 (size)
|
||||
private const val HEADER_SIZE = 8
|
||||
|
||||
// 10 bytes = 2 (key) + 2 (type) + 8 (value)
|
||||
private const val BUCKET_SIZE = 12
|
||||
|
||||
// 2 bytes = 2 (key)
|
||||
private const val TYPE_OFFSET = 2
|
||||
|
||||
// 4 bytes = 2 (key) + 2 (type)
|
||||
private const val VALUE_OFFSET = 4
|
||||
|
||||
init {
|
||||
MapBufferSoLoader.staticInit()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.common.mapbuffer;
|
||||
|
||||
import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE;
|
||||
|
||||
import com.facebook.react.bridge.ReactMarker;
|
||||
import com.facebook.react.bridge.ReactMarkerConstants;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
import com.facebook.systrace.Systrace;
|
||||
|
||||
public class ReadableMapBufferSoLoader {
|
||||
private static volatile boolean sDidInit = false;
|
||||
|
||||
public static void staticInit() {
|
||||
if (sDidInit) {
|
||||
return;
|
||||
}
|
||||
Systrace.beginSection(
|
||||
Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
|
||||
"ReadableMapBufferSoLoader.staticInit::load:mapbufferjni");
|
||||
ReactMarker.logMarker(ReactMarkerConstants.LOAD_REACT_NATIVE_MAPBUFFER_SO_FILE_START);
|
||||
SoLoader.loadLibrary("mapbufferjni");
|
||||
ReactMarker.logMarker(ReactMarkerConstants.LOAD_REACT_NATIVE_MAPBUFFER_SO_FILE_END);
|
||||
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
sDidInit = true;
|
||||
}
|
||||
}
|
|
@ -164,7 +164,7 @@ class WritableMapBuffer : MapBuffer {
|
|||
|
||||
companion object {
|
||||
init {
|
||||
ReadableMapBufferSoLoader.staticInit()
|
||||
MapBufferSoLoader.staticInit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ rn_android_library(
|
|||
"pfh:ReactNative_CommonInfrastructurePlaceholde",
|
||||
"supermodule:xplat/default/public.react_native.infra",
|
||||
],
|
||||
language = "KOTLIN",
|
||||
provided_deps = [
|
||||
react_native_dep("third-party/android/androidx:annotation"),
|
||||
react_native_dep("third-party/android/androidx:core"),
|
||||
|
@ -21,6 +22,7 @@ rn_android_library(
|
|||
react_native_dep("third-party/android/androidx:legacy-support-core-ui"),
|
||||
react_native_dep("third-party/android/androidx:legacy-support-core-utils"),
|
||||
],
|
||||
pure_kotlin = False,
|
||||
required_for_source_only_abi = True,
|
||||
visibility = [
|
||||
"PUBLIC",
|
||||
|
|
|
@ -11,7 +11,7 @@ import androidx.annotation.NonNull;
|
|||
import com.facebook.react.bridge.JSIModuleProvider;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.UIManager;
|
||||
import com.facebook.react.common.mapbuffer.ReadableMapBufferSoLoader;
|
||||
import com.facebook.react.common.mapbuffer.MapBufferSoLoader;
|
||||
import com.facebook.react.config.ReactFeatureFlags;
|
||||
import com.facebook.react.fabric.events.EventBeatManager;
|
||||
import com.facebook.react.uimanager.ViewManagerRegistry;
|
||||
|
@ -46,7 +46,7 @@ public class FabricJSIModuleProvider implements JSIModuleProvider<UIManager> {
|
|||
final Binding binding = new Binding();
|
||||
|
||||
if (ReactFeatureFlags.isMapBufferSerializationEnabled()) {
|
||||
ReadableMapBufferSoLoader.staticInit();
|
||||
MapBufferSoLoader.staticInit();
|
||||
}
|
||||
|
||||
binding.register(
|
||||
|
|
|
@ -23,7 +23,7 @@ import com.facebook.react.bridge.ReadableArray;
|
|||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.RetryableMountingLayerException;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.common.mapbuffer.ReadableMapBuffer;
|
||||
import com.facebook.react.common.mapbuffer.MapBuffer;
|
||||
import com.facebook.react.fabric.FabricUIManager;
|
||||
import com.facebook.react.fabric.events.EventEmitterWrapper;
|
||||
import com.facebook.react.fabric.mounting.mountitems.MountItem;
|
||||
|
@ -396,9 +396,9 @@ public class MountingManager {
|
|||
public long measureMapBuffer(
|
||||
@NonNull ReactContext context,
|
||||
@NonNull String componentName,
|
||||
@NonNull ReadableMapBuffer localData,
|
||||
@NonNull ReadableMapBuffer props,
|
||||
@Nullable ReadableMapBuffer state,
|
||||
@NonNull MapBuffer localData,
|
||||
@NonNull MapBuffer props,
|
||||
@Nullable MapBuffer state,
|
||||
float width,
|
||||
@NonNull YogaMeasureMode widthMode,
|
||||
float height,
|
||||
|
|
|
@ -15,7 +15,7 @@ import com.facebook.react.bridge.BaseJavaModule;
|
|||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.common.mapbuffer.ReadableMapBuffer;
|
||||
import com.facebook.react.common.mapbuffer.MapBuffer;
|
||||
import com.facebook.react.touch.JSResponderHandler;
|
||||
import com.facebook.react.touch.ReactInterceptingViewGroup;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
@ -333,9 +333,9 @@ public abstract class ViewManager<T extends View, C extends ReactShadowNode>
|
|||
* ViewManager
|
||||
*
|
||||
* @param context {@link com.facebook.react.bridge.ReactContext} used for the view.
|
||||
* @param localData {@link ReadableMapBuffer} containing "local data" defined in C++
|
||||
* @param props {@link ReadableMapBuffer} containing JS props
|
||||
* @param state {@link ReadableMapBuffer} containing state defined in C++
|
||||
* @param localData {@link MapBuffer} containing "local data" defined in C++
|
||||
* @param props {@link MapBuffer} containing JS props
|
||||
* @param state {@link MapBuffer} containing state defined in C++
|
||||
* @param width width of the view (usually zero)
|
||||
* @param widthMode widthMode used during calculation of layout
|
||||
* @param height height of the view (usually zero)
|
||||
|
@ -353,10 +353,10 @@ public abstract class ViewManager<T extends View, C extends ReactShadowNode>
|
|||
*/
|
||||
public long measure(
|
||||
Context context,
|
||||
ReadableMapBuffer localData,
|
||||
ReadableMapBuffer props,
|
||||
MapBuffer localData,
|
||||
MapBuffer props,
|
||||
// TODO(T114731225): review whether state parameter is needed
|
||||
@Nullable ReadableMapBuffer state,
|
||||
@Nullable MapBuffer state,
|
||||
float width,
|
||||
YogaMeasureMode widthMode,
|
||||
float height,
|
||||
|
|
|
@ -9,6 +9,8 @@ rn_android_library(
|
|||
"pfh:ReactNative_CommonInfrastructurePlaceholde",
|
||||
"supermodule:xplat/default/public.react_native.infra",
|
||||
],
|
||||
language = "KOTLIN",
|
||||
pure_kotlin = False,
|
||||
required_for_source_only_abi = True,
|
||||
visibility = [
|
||||
"PUBLIC",
|
||||
|
|
|
@ -15,7 +15,7 @@ import com.facebook.react.bridge.ReadableMap;
|
|||
import com.facebook.react.bridge.ReadableNativeMap;
|
||||
import com.facebook.react.common.MapBuilder;
|
||||
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||
import com.facebook.react.common.mapbuffer.ReadableMapBuffer;
|
||||
import com.facebook.react.common.mapbuffer.MapBuffer;
|
||||
import com.facebook.react.config.ReactFeatureFlags;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
import com.facebook.react.uimanager.IViewManagerWithChildren;
|
||||
|
@ -112,7 +112,7 @@ public class ReactTextViewManager
|
|||
}
|
||||
|
||||
if (ReactFeatureFlags.isMapBufferSerializationEnabled()) {
|
||||
ReadableMapBuffer stateMapBuffer = stateWrapper.getStateDataMapBuffer();
|
||||
MapBuffer stateMapBuffer = stateWrapper.getStateDataMapBuffer();
|
||||
if (stateMapBuffer != null) {
|
||||
return getReactTextUpdate(view, props, stateMapBuffer);
|
||||
}
|
||||
|
@ -142,11 +142,10 @@ public class ReactTextViewManager
|
|||
TextAttributeProps.getJustificationMode(props));
|
||||
}
|
||||
|
||||
private Object getReactTextUpdate(
|
||||
ReactTextView view, ReactStylesDiffMap props, ReadableMapBuffer state) {
|
||||
private Object getReactTextUpdate(ReactTextView view, ReactStylesDiffMap props, MapBuffer state) {
|
||||
|
||||
ReadableMapBuffer attributedString = state.getMapBuffer(TX_STATE_KEY_ATTRIBUTED_STRING);
|
||||
ReadableMapBuffer paragraphAttributes = state.getMapBuffer(TX_STATE_KEY_PARAGRAPH_ATTRIBUTES);
|
||||
MapBuffer attributedString = state.getMapBuffer(TX_STATE_KEY_ATTRIBUTED_STRING);
|
||||
MapBuffer paragraphAttributes = state.getMapBuffer(TX_STATE_KEY_PARAGRAPH_ATTRIBUTES);
|
||||
Spannable spanned =
|
||||
TextLayoutManagerMapBuffer.getOrCreateSpannableForText(
|
||||
view.getContext(), attributedString, mReactTextViewManagerCallback);
|
||||
|
@ -205,9 +204,9 @@ public class ReactTextViewManager
|
|||
@Override
|
||||
public long measure(
|
||||
Context context,
|
||||
ReadableMapBuffer localData,
|
||||
ReadableMapBuffer props,
|
||||
@Nullable ReadableMapBuffer state,
|
||||
MapBuffer localData,
|
||||
MapBuffer props,
|
||||
@Nullable MapBuffer state,
|
||||
float width,
|
||||
YogaMeasureMode widthMode,
|
||||
float height,
|
||||
|
|
|
@ -16,7 +16,7 @@ import androidx.annotation.Nullable;
|
|||
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.common.mapbuffer.ReadableMapBuffer;
|
||||
import com.facebook.react.common.mapbuffer.MapBuffer;
|
||||
import com.facebook.react.uimanager.PixelUtil;
|
||||
import com.facebook.react.uimanager.ReactAccessibilityDelegate;
|
||||
import com.facebook.react.uimanager.ReactStylesDiffMap;
|
||||
|
@ -137,51 +137,48 @@ public class TextAttributeProps {
|
|||
|
||||
private TextAttributeProps() {}
|
||||
|
||||
/**
|
||||
* Build a TextAttributeProps using data from the {@link ReadableMapBuffer} received as a
|
||||
* parameter.
|
||||
*/
|
||||
public static TextAttributeProps fromReadableMapBuffer(ReadableMapBuffer props) {
|
||||
/** Build a TextAttributeProps using data from the {@link MapBuffer} received as a parameter. */
|
||||
public static TextAttributeProps fromMapBuffer(MapBuffer props) {
|
||||
TextAttributeProps result = new TextAttributeProps();
|
||||
|
||||
// TODO T83483191: Review constants that are not being set!
|
||||
Iterator<ReadableMapBuffer.MapBufferEntry> iterator = props.iterator();
|
||||
Iterator<MapBuffer.Entry> iterator = props.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
ReadableMapBuffer.MapBufferEntry entry = iterator.next();
|
||||
MapBuffer.Entry entry = iterator.next();
|
||||
switch (entry.getKey()) {
|
||||
case TA_KEY_FOREGROUND_COLOR:
|
||||
result.setColor(entry.getInt());
|
||||
result.setColor(entry.getIntValue());
|
||||
break;
|
||||
case TA_KEY_BACKGROUND_COLOR:
|
||||
result.setBackgroundColor(entry.getInt());
|
||||
result.setBackgroundColor(entry.getIntValue());
|
||||
break;
|
||||
case TA_KEY_OPACITY:
|
||||
break;
|
||||
case TA_KEY_FONT_FAMILY:
|
||||
result.setFontFamily(entry.getString());
|
||||
result.setFontFamily(entry.getStringValue());
|
||||
break;
|
||||
case TA_KEY_FONT_SIZE:
|
||||
result.setFontSize((float) entry.getDouble());
|
||||
result.setFontSize((float) entry.getDoubleValue());
|
||||
break;
|
||||
case TA_KEY_FONT_SIZE_MULTIPLIER:
|
||||
break;
|
||||
case TA_KEY_FONT_WEIGHT:
|
||||
result.setFontWeight(entry.getString());
|
||||
result.setFontWeight(entry.getStringValue());
|
||||
break;
|
||||
case TA_KEY_FONT_STYLE:
|
||||
result.setFontStyle(entry.getString());
|
||||
result.setFontStyle(entry.getStringValue());
|
||||
break;
|
||||
case TA_KEY_FONT_VARIANT:
|
||||
result.setFontVariant(entry.getReadableMapBuffer());
|
||||
result.setFontVariant(entry.getMapBufferValue());
|
||||
break;
|
||||
case TA_KEY_ALLOW_FONT_SCALING:
|
||||
result.setAllowFontScaling(entry.getBoolean());
|
||||
result.setAllowFontScaling(entry.getBooleanValue());
|
||||
break;
|
||||
case TA_KEY_LETTER_SPACING:
|
||||
result.setLetterSpacing((float) entry.getDouble());
|
||||
result.setLetterSpacing((float) entry.getDoubleValue());
|
||||
break;
|
||||
case TA_KEY_LINE_HEIGHT:
|
||||
result.setLineHeight((float) entry.getDouble());
|
||||
result.setLineHeight((float) entry.getDoubleValue());
|
||||
break;
|
||||
case TA_KEY_ALIGNMENT:
|
||||
break;
|
||||
|
@ -190,23 +187,23 @@ public class TextAttributeProps {
|
|||
case TA_KEY_TEXT_DECORATION_COLOR:
|
||||
break;
|
||||
case TA_KEY_TEXT_DECORATION_LINE:
|
||||
result.setTextDecorationLine(entry.getString());
|
||||
result.setTextDecorationLine(entry.getStringValue());
|
||||
break;
|
||||
case TA_KEY_TEXT_DECORATION_STYLE:
|
||||
break;
|
||||
case TA_KEY_TEXT_SHADOW_RADIUS:
|
||||
result.setTextShadowRadius((float) entry.getDouble());
|
||||
result.setTextShadowRadius((float) entry.getDoubleValue());
|
||||
break;
|
||||
case TA_KEY_TEXT_SHADOW_COLOR:
|
||||
result.setTextShadowColor(entry.getInt());
|
||||
result.setTextShadowColor(entry.getIntValue());
|
||||
break;
|
||||
case TA_KEY_IS_HIGHLIGHTED:
|
||||
break;
|
||||
case TA_KEY_LAYOUT_DIRECTION:
|
||||
result.setLayoutDirection(entry.getString());
|
||||
result.setLayoutDirection(entry.getStringValue());
|
||||
break;
|
||||
case TA_KEY_ACCESSIBILITY_ROLE:
|
||||
result.setAccessibilityRole(entry.getString());
|
||||
result.setAccessibilityRole(entry.getStringValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -419,17 +416,17 @@ public class TextAttributeProps {
|
|||
mFontFeatureSettings = ReactTypefaceUtils.parseFontVariant(fontVariant);
|
||||
}
|
||||
|
||||
private void setFontVariant(@Nullable ReadableMapBuffer fontVariant) {
|
||||
private void setFontVariant(@Nullable MapBuffer fontVariant) {
|
||||
if (fontVariant == null || fontVariant.getCount() == 0) {
|
||||
mFontFeatureSettings = null;
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> features = new ArrayList<>();
|
||||
Iterator<ReadableMapBuffer.MapBufferEntry> iterator = fontVariant.iterator();
|
||||
Iterator<MapBuffer.Entry> iterator = fontVariant.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
ReadableMapBuffer.MapBufferEntry entry = iterator.next();
|
||||
String value = entry.getString();
|
||||
MapBuffer.Entry entry = iterator.next();
|
||||
String value = entry.getStringValue();
|
||||
if (value != null) {
|
||||
switch (value) {
|
||||
case "small-caps":
|
||||
|
|
|
@ -28,7 +28,7 @@ import com.facebook.react.bridge.ReactNoCrashSoftException;
|
|||
import com.facebook.react.bridge.ReactSoftExceptionLogger;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.common.build.ReactBuildConfig;
|
||||
import com.facebook.react.common.mapbuffer.ReadableMapBuffer;
|
||||
import com.facebook.react.common.mapbuffer.MapBuffer;
|
||||
import com.facebook.react.uimanager.PixelUtil;
|
||||
import com.facebook.yoga.YogaConstants;
|
||||
import com.facebook.yoga.YogaMeasureMode;
|
||||
|
@ -78,7 +78,7 @@ public class TextLayoutManagerMapBuffer {
|
|||
|
||||
private static final Object sSpannableCacheLock = new Object();
|
||||
private static final boolean DEFAULT_INCLUDE_FONT_PADDING = true;
|
||||
private static final LruCache<ReadableMapBuffer, Spannable> sSpannableCache =
|
||||
private static final LruCache<MapBuffer, Spannable> sSpannableCache =
|
||||
new LruCache<>(spannableCacheSize);
|
||||
private static final ConcurrentHashMap<Integer, Spannable> sTagToSpannableCache =
|
||||
new ConcurrentHashMap<>();
|
||||
|
@ -97,39 +97,36 @@ public class TextLayoutManagerMapBuffer {
|
|||
sTagToSpannableCache.remove(reactTag);
|
||||
}
|
||||
|
||||
public static boolean isRTL(ReadableMapBuffer attributedString) {
|
||||
ReadableMapBuffer fragments = attributedString.getMapBuffer(AS_KEY_FRAGMENTS);
|
||||
public static boolean isRTL(MapBuffer attributedString) {
|
||||
MapBuffer fragments = attributedString.getMapBuffer(AS_KEY_FRAGMENTS);
|
||||
if (fragments.getCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ReadableMapBuffer fragment = fragments.getMapBuffer((short) 0);
|
||||
ReadableMapBuffer textAttributes = fragment.getMapBuffer(FR_KEY_TEXT_ATTRIBUTES);
|
||||
MapBuffer fragment = fragments.getMapBuffer((short) 0);
|
||||
MapBuffer textAttributes = fragment.getMapBuffer(FR_KEY_TEXT_ATTRIBUTES);
|
||||
return TextAttributeProps.getLayoutDirection(
|
||||
textAttributes.getString(TextAttributeProps.TA_KEY_LAYOUT_DIRECTION))
|
||||
== LayoutDirection.RTL;
|
||||
}
|
||||
|
||||
private static void buildSpannableFromFragment(
|
||||
Context context,
|
||||
ReadableMapBuffer fragments,
|
||||
SpannableStringBuilder sb,
|
||||
List<SetSpanOperation> ops) {
|
||||
Context context, MapBuffer fragments, SpannableStringBuilder sb, List<SetSpanOperation> ops) {
|
||||
|
||||
for (int i = 0, length = fragments.getCount(); i < length; i++) {
|
||||
ReadableMapBuffer fragment = fragments.getMapBuffer(i);
|
||||
MapBuffer fragment = fragments.getMapBuffer(i);
|
||||
int start = sb.length();
|
||||
|
||||
TextAttributeProps textAttributes =
|
||||
TextAttributeProps.fromReadableMapBuffer(fragment.getMapBuffer(FR_KEY_TEXT_ATTRIBUTES));
|
||||
TextAttributeProps.fromMapBuffer(fragment.getMapBuffer(FR_KEY_TEXT_ATTRIBUTES));
|
||||
|
||||
sb.append(
|
||||
TextTransform.apply(fragment.getString(FR_KEY_STRING), textAttributes.mTextTransform));
|
||||
|
||||
int end = sb.length();
|
||||
int reactTag =
|
||||
fragment.hasKey(FR_KEY_REACT_TAG) ? fragment.getInt(FR_KEY_REACT_TAG) : View.NO_ID;
|
||||
if (fragment.hasKey(FR_KEY_IS_ATTACHMENT) && fragment.getBoolean(FR_KEY_IS_ATTACHMENT)) {
|
||||
fragment.contains(FR_KEY_REACT_TAG) ? fragment.getInt(FR_KEY_REACT_TAG) : View.NO_ID;
|
||||
if (fragment.contains(FR_KEY_IS_ATTACHMENT) && fragment.getBoolean(FR_KEY_IS_ATTACHMENT)) {
|
||||
float width = PixelUtil.toPixelFromSP(fragment.getDouble(FR_KEY_WIDTH));
|
||||
float height = PixelUtil.toPixelFromSP(fragment.getDouble(FR_KEY_HEIGHT));
|
||||
ops.add(
|
||||
|
@ -203,7 +200,7 @@ public class TextLayoutManagerMapBuffer {
|
|||
// public because both ReactTextViewManager and ReactTextInputManager need to use this
|
||||
public static Spannable getOrCreateSpannableForText(
|
||||
Context context,
|
||||
ReadableMapBuffer attributedString,
|
||||
MapBuffer attributedString,
|
||||
@Nullable ReactTextViewManagerCallback reactTextViewManagerCallback) {
|
||||
|
||||
Spannable preparedSpannableText;
|
||||
|
@ -228,7 +225,7 @@ public class TextLayoutManagerMapBuffer {
|
|||
|
||||
private static Spannable createSpannableFromAttributedString(
|
||||
Context context,
|
||||
ReadableMapBuffer attributedString,
|
||||
MapBuffer attributedString,
|
||||
@Nullable ReactTextViewManagerCallback reactTextViewManagerCallback) {
|
||||
|
||||
SpannableStringBuilder sb = new SpannableStringBuilder();
|
||||
|
@ -351,8 +348,8 @@ public class TextLayoutManagerMapBuffer {
|
|||
|
||||
public static long measureText(
|
||||
Context context,
|
||||
ReadableMapBuffer attributedString,
|
||||
ReadableMapBuffer paragraphAttributes,
|
||||
MapBuffer attributedString,
|
||||
MapBuffer paragraphAttributes,
|
||||
float width,
|
||||
YogaMeasureMode widthYogaMeasureMode,
|
||||
float height,
|
||||
|
@ -363,7 +360,7 @@ public class TextLayoutManagerMapBuffer {
|
|||
// TODO(5578671): Handle text direction (see View#getTextDirectionHeuristic)
|
||||
TextPaint textPaint = sTextPaintInstance;
|
||||
Spannable text;
|
||||
if (attributedString.hasKey(AS_KEY_CACHE_ID)) {
|
||||
if (attributedString.contains(AS_KEY_CACHE_ID)) {
|
||||
int cacheId = attributedString.getInt(AS_KEY_CACHE_ID);
|
||||
if (ENABLE_MEASURE_LOGGING) {
|
||||
FLog.e(TAG, "Get cached spannable for cacheId[" + cacheId + "]");
|
||||
|
@ -387,7 +384,7 @@ public class TextLayoutManagerMapBuffer {
|
|||
TextAttributeProps.getTextBreakStrategy(
|
||||
paragraphAttributes.getString(PA_KEY_TEXT_BREAK_STRATEGY));
|
||||
boolean includeFontPadding =
|
||||
paragraphAttributes.hasKey(PA_KEY_INCLUDE_FONT_PADDING)
|
||||
paragraphAttributes.contains(PA_KEY_INCLUDE_FONT_PADDING)
|
||||
? paragraphAttributes.getBoolean(PA_KEY_INCLUDE_FONT_PADDING)
|
||||
: DEFAULT_INCLUDE_FONT_PADDING;
|
||||
int hyphenationFrequency =
|
||||
|
@ -415,7 +412,7 @@ public class TextLayoutManagerMapBuffer {
|
|||
hyphenationFrequency);
|
||||
|
||||
int maximumNumberOfLines =
|
||||
paragraphAttributes.hasKey(PA_KEY_MAX_NUMBER_OF_LINES)
|
||||
paragraphAttributes.contains(PA_KEY_MAX_NUMBER_OF_LINES)
|
||||
? paragraphAttributes.getInt(PA_KEY_MAX_NUMBER_OF_LINES)
|
||||
: UNSET;
|
||||
|
||||
|
@ -554,8 +551,8 @@ public class TextLayoutManagerMapBuffer {
|
|||
|
||||
public static WritableArray measureLines(
|
||||
@NonNull Context context,
|
||||
ReadableMapBuffer attributedString,
|
||||
ReadableMapBuffer paragraphAttributes,
|
||||
MapBuffer attributedString,
|
||||
MapBuffer paragraphAttributes,
|
||||
float width) {
|
||||
|
||||
TextPaint textPaint = sTextPaintInstance;
|
||||
|
@ -566,7 +563,7 @@ public class TextLayoutManagerMapBuffer {
|
|||
TextAttributeProps.getTextBreakStrategy(
|
||||
paragraphAttributes.getString(PA_KEY_TEXT_BREAK_STRATEGY));
|
||||
boolean includeFontPadding =
|
||||
paragraphAttributes.hasKey(PA_KEY_INCLUDE_FONT_PADDING)
|
||||
paragraphAttributes.contains(PA_KEY_INCLUDE_FONT_PADDING)
|
||||
? paragraphAttributes.getBoolean(PA_KEY_INCLUDE_FONT_PADDING)
|
||||
: DEFAULT_INCLUDE_FONT_PADDING;
|
||||
int hyphenationFrequency =
|
||||
|
|
|
@ -14,7 +14,7 @@ import com.facebook.react.bridge.DynamicFromObject
|
|||
import com.facebook.react.bridge.JavaOnlyArray
|
||||
import com.facebook.react.bridge.JavaOnlyMap
|
||||
import com.facebook.react.bridge.ReadableMap
|
||||
import com.facebook.react.common.mapbuffer.ReadableMapBuffer
|
||||
import com.facebook.react.common.mapbuffer.MapBuffer
|
||||
import com.facebook.react.uimanager.PixelUtil
|
||||
import com.facebook.react.uimanager.PointerEvents
|
||||
|
||||
|
@ -97,134 +97,131 @@ object ReactMapBufferPropSetter {
|
|||
|
||||
private const val UNDEF_COLOR = Int.MAX_VALUE
|
||||
|
||||
fun setProps(view: ReactViewGroup, viewManager: ReactViewManager, props: ReadableMapBuffer) {
|
||||
fun setProps(view: ReactViewGroup, viewManager: ReactViewManager, props: MapBuffer) {
|
||||
for (entry in props) {
|
||||
when (entry.key) {
|
||||
VP_ACCESSIBILITY_ACTIONS -> {
|
||||
viewManager.accessibilityActions(view, entry.readableMapBuffer)
|
||||
viewManager.accessibilityActions(view, entry.mapBufferValue)
|
||||
}
|
||||
VP_ACCESSIBILITY_HINT -> {
|
||||
viewManager.setAccessibilityHint(view, entry.string.takeIf { it.isNotEmpty() })
|
||||
viewManager.setAccessibilityHint(view, entry.stringValue.takeIf { it.isNotEmpty() })
|
||||
}
|
||||
VP_ACCESSIBILITY_LABEL -> {
|
||||
viewManager.setAccessibilityLabel(view, entry.string.takeIf { it.isNotEmpty() })
|
||||
viewManager.setAccessibilityLabel(view, entry.stringValue.takeIf { it.isNotEmpty() })
|
||||
}
|
||||
VP_ACCESSIBILITY_LABELLED_BY -> {
|
||||
viewManager.accessibilityLabelledBy(view, entry.readableMapBuffer)
|
||||
viewManager.accessibilityLabelledBy(view, entry.mapBufferValue)
|
||||
}
|
||||
VP_ACCESSIBILITY_LIVE_REGION -> {
|
||||
view.accessibilityLiveRegion(entry.int)
|
||||
view.accessibilityLiveRegion(entry.intValue)
|
||||
}
|
||||
VP_ACCESSIBILITY_ROLE -> {
|
||||
viewManager.setAccessibilityRole(view, entry.string.takeIf { it.isNotEmpty() })
|
||||
viewManager.setAccessibilityRole(view, entry.stringValue.takeIf { it.isNotEmpty() })
|
||||
}
|
||||
VP_ACCESSIBILITY_STATE -> {
|
||||
viewManager.accessibilityState(view, entry.readableMapBuffer)
|
||||
viewManager.accessibilityState(view, entry.mapBufferValue)
|
||||
}
|
||||
VP_ACCESSIBILITY_VALUE -> {
|
||||
viewManager.accessibilityValue(view, entry.string)
|
||||
viewManager.accessibilityValue(view, entry.stringValue)
|
||||
}
|
||||
VP_ACCESSIBLE -> {
|
||||
viewManager.setAccessible(view, entry.boolean)
|
||||
viewManager.setAccessible(view, entry.booleanValue)
|
||||
}
|
||||
VP_BACKFACE_VISIBILITY -> {
|
||||
viewManager.backfaceVisibility(view, entry.int)
|
||||
viewManager.backfaceVisibility(view, entry.intValue)
|
||||
}
|
||||
VP_BG_COLOR -> {
|
||||
// TODO: color for some reason can be object in Java but not in C++
|
||||
viewManager.backgroundColor(view, entry.int)
|
||||
viewManager.backgroundColor(view, entry.intValue)
|
||||
}
|
||||
VP_BORDER_COLOR -> {
|
||||
viewManager.borderColor(view, entry.readableMapBuffer)
|
||||
viewManager.borderColor(view, entry.mapBufferValue)
|
||||
}
|
||||
VP_BORDER_RADII -> {
|
||||
viewManager.borderRadius(view, entry.readableMapBuffer)
|
||||
viewManager.borderRadius(view, entry.mapBufferValue)
|
||||
}
|
||||
VP_BORDER_STYLE -> {
|
||||
viewManager.borderStyle(view, entry.int)
|
||||
viewManager.borderStyle(view, entry.intValue)
|
||||
}
|
||||
VP_ELEVATION -> {
|
||||
viewManager.setElevation(view, entry.double.toFloat())
|
||||
viewManager.setElevation(view, entry.doubleValue.toFloat())
|
||||
}
|
||||
VP_FOCUSABLE -> {
|
||||
viewManager.setFocusable(view, entry.boolean)
|
||||
viewManager.setFocusable(view, entry.booleanValue)
|
||||
}
|
||||
VP_HAS_TV_FOCUS -> {
|
||||
viewManager.setTVPreferredFocus(view, entry.boolean)
|
||||
viewManager.setTVPreferredFocus(view, entry.booleanValue)
|
||||
}
|
||||
VP_HIT_SLOP -> {
|
||||
view.hitSlop(entry.readableMapBuffer)
|
||||
view.hitSlop(entry.mapBufferValue)
|
||||
}
|
||||
VP_IMPORTANT_FOR_ACCESSIBILITY -> {
|
||||
view.importantForAccessibility(entry.int)
|
||||
view.importantForAccessibility(entry.intValue)
|
||||
}
|
||||
VP_NATIVE_BACKGROUND -> {
|
||||
viewManager.nativeBackground(view, entry.readableMapBuffer)
|
||||
viewManager.nativeBackground(view, entry.mapBufferValue)
|
||||
}
|
||||
VP_NATIVE_FOREGROUND -> {
|
||||
viewManager.nativeForeground(view, entry.readableMapBuffer)
|
||||
viewManager.nativeForeground(view, entry.mapBufferValue)
|
||||
}
|
||||
VP_NATIVE_ID -> {
|
||||
viewManager.setNativeId(view, entry.string.takeIf { it.isNotEmpty() })
|
||||
viewManager.setNativeId(view, entry.stringValue.takeIf { it.isNotEmpty() })
|
||||
}
|
||||
VP_OFFSCREEN_ALPHA_COMPOSITING -> {
|
||||
viewManager.setNeedsOffscreenAlphaCompositing(view, entry.boolean)
|
||||
viewManager.setNeedsOffscreenAlphaCompositing(view, entry.booleanValue)
|
||||
}
|
||||
VP_OPACITY -> {
|
||||
viewManager.setOpacity(view, entry.double.toFloat())
|
||||
viewManager.setOpacity(view, entry.doubleValue.toFloat())
|
||||
}
|
||||
VP_POINTER_EVENTS -> {
|
||||
view.pointerEvents(entry.int)
|
||||
view.pointerEvents(entry.intValue)
|
||||
}
|
||||
VP_POINTER_ENTER -> {
|
||||
viewManager.setPointerEnter(view, entry.boolean)
|
||||
viewManager.setPointerEnter(view, entry.booleanValue)
|
||||
}
|
||||
VP_POINTER_LEAVE -> {
|
||||
viewManager.setPointerLeave(view, entry.boolean)
|
||||
viewManager.setPointerLeave(view, entry.booleanValue)
|
||||
}
|
||||
VP_POINTER_MOVE -> {
|
||||
viewManager.setPointerMove(view, entry.boolean)
|
||||
viewManager.setPointerMove(view, entry.booleanValue)
|
||||
}
|
||||
VP_REMOVE_CLIPPED_SUBVIEW -> {
|
||||
viewManager.setRemoveClippedSubviews(view, entry.boolean)
|
||||
viewManager.setRemoveClippedSubviews(view, entry.booleanValue)
|
||||
}
|
||||
VP_RENDER_TO_HARDWARE_TEXTURE -> {
|
||||
viewManager.setRenderToHardwareTexture(view, entry.boolean)
|
||||
viewManager.setRenderToHardwareTexture(view, entry.booleanValue)
|
||||
}
|
||||
VP_SHADOW_COLOR -> {
|
||||
// TODO: color for some reason can be object in Java but not in C++
|
||||
viewManager.shadowColor(view, entry.int)
|
||||
viewManager.shadowColor(view, entry.intValue)
|
||||
}
|
||||
VP_TEST_ID -> {
|
||||
viewManager.setTestId(view, entry.string.takeIf { it.isNotEmpty() })
|
||||
viewManager.setTestId(view, entry.stringValue.takeIf { it.isNotEmpty() })
|
||||
}
|
||||
VP_TRANSFORM -> {
|
||||
viewManager.transform(view, entry.readableMapBuffer)
|
||||
viewManager.transform(view, entry.mapBufferValue)
|
||||
}
|
||||
VP_ZINDEX -> {
|
||||
viewManager.setZIndex(view, entry.int.toFloat())
|
||||
viewManager.setZIndex(view, entry.intValue.toFloat())
|
||||
}
|
||||
YG_BORDER_WIDTH -> {
|
||||
viewManager.borderWidth(view, entry.readableMapBuffer)
|
||||
viewManager.borderWidth(view, entry.mapBufferValue)
|
||||
}
|
||||
YG_OVERFLOW -> {
|
||||
viewManager.overflow(view, entry.int)
|
||||
viewManager.overflow(view, entry.intValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ReactViewManager.accessibilityActions(
|
||||
view: ReactViewGroup,
|
||||
mapBuffer: ReadableMapBuffer
|
||||
) {
|
||||
private fun ReactViewManager.accessibilityActions(view: ReactViewGroup, mapBuffer: MapBuffer) {
|
||||
val actions = mutableListOf<ReadableMap>()
|
||||
for (entry in mapBuffer) {
|
||||
val map = JavaOnlyMap()
|
||||
val action = entry.readableMapBuffer
|
||||
val action = entry.mapBufferValue
|
||||
if (action != null) {
|
||||
map.putString("name", action.getString(ACCESSIBILITY_ACTION_NAME))
|
||||
if (action.hasKey(ACCESSIBILITY_ACTION_LABEL)) {
|
||||
if (action.contains(ACCESSIBILITY_ACTION_LABEL)) {
|
||||
map.putString("label", action.getString(ACCESSIBILITY_ACTION_LABEL))
|
||||
}
|
||||
}
|
||||
|
@ -245,7 +242,7 @@ object ReactMapBufferPropSetter {
|
|||
ViewCompat.setAccessibilityLiveRegion(this, mode)
|
||||
}
|
||||
|
||||
private fun ReactViewManager.accessibilityState(view: ReactViewGroup, value: ReadableMapBuffer) {
|
||||
private fun ReactViewManager.accessibilityState(view: ReactViewGroup, value: MapBuffer) {
|
||||
val accessibilityState = JavaOnlyMap()
|
||||
accessibilityState.putBoolean("selected", value.getBoolean(ACCESSIBILITY_STATE_SELECTED))
|
||||
accessibilityState.putBoolean("busy", value.getBoolean(ACCESSIBILITY_STATE_BUSY))
|
||||
|
@ -273,17 +270,14 @@ object ReactMapBufferPropSetter {
|
|||
setAccessibilityValue(view, map)
|
||||
}
|
||||
|
||||
private fun ReactViewManager.accessibilityLabelledBy(
|
||||
view: ReactViewGroup,
|
||||
value: ReadableMapBuffer
|
||||
) {
|
||||
private fun ReactViewManager.accessibilityLabelledBy(view: ReactViewGroup, value: MapBuffer) {
|
||||
val converted =
|
||||
if (value.count == 0) {
|
||||
DynamicFromObject(null)
|
||||
} else {
|
||||
val array = JavaOnlyArray()
|
||||
for (label in value) {
|
||||
array.pushString(label.string)
|
||||
array.pushString(label.stringValue)
|
||||
}
|
||||
DynamicFromObject(array)
|
||||
}
|
||||
|
@ -306,7 +300,7 @@ object ReactMapBufferPropSetter {
|
|||
setBackgroundColor(view, color)
|
||||
}
|
||||
|
||||
private fun ReactViewManager.borderColor(view: ReactViewGroup, value: ReadableMapBuffer) {
|
||||
private fun ReactViewManager.borderColor(view: ReactViewGroup, value: MapBuffer) {
|
||||
for (entry in value) {
|
||||
val index =
|
||||
when (val key = entry.key) {
|
||||
|
@ -319,12 +313,12 @@ object ReactMapBufferPropSetter {
|
|||
EDGE_END -> 6
|
||||
else -> throw IllegalArgumentException("Unknown key for border color: $key")
|
||||
}
|
||||
val colorValue = entry.int
|
||||
val colorValue = entry.intValue
|
||||
setBorderColor(view, index, colorValue.takeIf { it != -1 })
|
||||
}
|
||||
}
|
||||
|
||||
private fun ReactViewManager.borderRadius(view: ReactViewGroup, value: ReadableMapBuffer) {
|
||||
private fun ReactViewManager.borderRadius(view: ReactViewGroup, value: MapBuffer) {
|
||||
for (entry in value) {
|
||||
val index =
|
||||
when (val key = entry.key) {
|
||||
|
@ -339,7 +333,7 @@ object ReactMapBufferPropSetter {
|
|||
CORNER_BOTTOM_END -> 8
|
||||
else -> throw IllegalArgumentException("Unknown key for border style: $key")
|
||||
}
|
||||
val borderRadius = entry.double
|
||||
val borderRadius = entry.doubleValue
|
||||
if (!borderRadius.isNaN()) {
|
||||
setBorderRadius(view, index, borderRadius.toFloat())
|
||||
}
|
||||
|
@ -357,7 +351,7 @@ object ReactMapBufferPropSetter {
|
|||
setBorderStyle(view, stringValue)
|
||||
}
|
||||
|
||||
private fun ReactViewGroup.hitSlop(value: ReadableMapBuffer) {
|
||||
private fun ReactViewGroup.hitSlop(value: MapBuffer) {
|
||||
val rect =
|
||||
Rect(
|
||||
PixelUtil.toPixelFromDIP(value.getDouble(EDGE_LEFT)).toInt(),
|
||||
|
@ -392,15 +386,15 @@ object ReactMapBufferPropSetter {
|
|||
setPointerEvents(pointerEvents)
|
||||
}
|
||||
|
||||
private fun ReactViewManager.transform(view: ReactViewGroup, value: ReadableMapBuffer) {
|
||||
private fun ReactViewManager.transform(view: ReactViewGroup, value: MapBuffer) {
|
||||
val list = JavaOnlyArray()
|
||||
for (entry in value) {
|
||||
list.pushDouble(entry.double)
|
||||
list.pushDouble(entry.doubleValue)
|
||||
}
|
||||
setTransform(view, list)
|
||||
}
|
||||
|
||||
private fun ReactViewManager.borderWidth(view: ReactViewGroup, value: ReadableMapBuffer) {
|
||||
private fun ReactViewManager.borderWidth(view: ReactViewGroup, value: MapBuffer) {
|
||||
for (entry in value) {
|
||||
val index =
|
||||
when (val key = entry.key) {
|
||||
|
@ -413,7 +407,7 @@ object ReactMapBufferPropSetter {
|
|||
EDGE_END -> 6
|
||||
else -> throw IllegalArgumentException("Unknown key for border width: $key")
|
||||
}
|
||||
val borderWidth = entry.double
|
||||
val borderWidth = entry.doubleValue
|
||||
if (!borderWidth.isNaN()) {
|
||||
setBorderWidth(view, index, borderWidth.toFloat())
|
||||
}
|
||||
|
@ -437,15 +431,15 @@ object ReactMapBufferPropSetter {
|
|||
setShadowColor(view, color)
|
||||
}
|
||||
|
||||
private fun ReactViewManager.nativeBackground(view: ReactViewGroup, value: ReadableMapBuffer) {
|
||||
private fun ReactViewManager.nativeBackground(view: ReactViewGroup, value: MapBuffer) {
|
||||
setNativeBackground(view, value.toJsDrawableDescription())
|
||||
}
|
||||
|
||||
private fun ReactViewManager.nativeForeground(view: ReactViewGroup, value: ReadableMapBuffer) {
|
||||
private fun ReactViewManager.nativeForeground(view: ReactViewGroup, value: MapBuffer) {
|
||||
setNativeForeground(view, value.toJsDrawableDescription())
|
||||
}
|
||||
|
||||
private fun ReadableMapBuffer.toJsDrawableDescription(): ReadableMap? {
|
||||
private fun MapBuffer.toJsDrawableDescription(): ReadableMap? {
|
||||
if (count == 0) {
|
||||
return null
|
||||
}
|
||||
|
@ -459,11 +453,11 @@ object ReactMapBufferPropSetter {
|
|||
}
|
||||
1 -> {
|
||||
result.putString("type", "RippleAndroid")
|
||||
if (hasKey(NATIVE_DRAWABLE_COLOR)) {
|
||||
if (contains(NATIVE_DRAWABLE_COLOR)) {
|
||||
result.putInt("color", getInt(NATIVE_DRAWABLE_COLOR))
|
||||
}
|
||||
result.putBoolean("borderless", getBoolean(NATIVE_DRAWABLE_BORDERLESS))
|
||||
if (hasKey(NATIVE_DRAWABLE_RIPPLE_RADIUS)) {
|
||||
if (contains(NATIVE_DRAWABLE_RIPPLE_RADIUS)) {
|
||||
result.putDouble("rippleRadius", getDouble(NATIVE_DRAWABLE_RIPPLE_RADIUS))
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче