mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-27 11:42:52 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a73d93ec36 |
@@ -0,0 +1,91 @@
|
|||||||
|
package sh.paseo.diagnosis;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.text.Layout;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
import com.facebook.react.bridge.BridgeReactContext;
|
||||||
|
import com.facebook.react.common.mapbuffer.WritableMapBuffer;
|
||||||
|
import com.facebook.react.soloader.OpenSourceMergedSoMapping;
|
||||||
|
import com.facebook.react.uimanager.DisplayMetricsHolder;
|
||||||
|
import com.facebook.react.uimanager.PixelUtil;
|
||||||
|
import com.facebook.react.uimanager.ThemedReactContext;
|
||||||
|
import com.facebook.react.views.text.ReactTextUpdate;
|
||||||
|
import com.facebook.react.views.text.ReactTextView;
|
||||||
|
import com.facebook.react.views.text.TextLayoutManager;
|
||||||
|
import com.facebook.soloader.SoLoader;
|
||||||
|
import com.facebook.yoga.YogaMeasureMode;
|
||||||
|
import com.facebook.yoga.YogaMeasureOutput;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/** Native reproduction for #4368. Uses the measurement and rendering paths used by RN Text. */
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class AboutFontWeightTest {
|
||||||
|
@Test public void ordinaryVersionFitsItsMeasuredBox() { checkVersion(0); }
|
||||||
|
@Test public void boldVersionFitsItsMeasuredBox() { checkVersion(300); }
|
||||||
|
|
||||||
|
private void checkVersion(int adjustment) {
|
||||||
|
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
|
||||||
|
Context app = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||||
|
Configuration configuration = new Configuration(app.getResources().getConfiguration());
|
||||||
|
configuration.fontWeightAdjustment = adjustment;
|
||||||
|
Context configured = app.createConfigurationContext(configuration);
|
||||||
|
try {
|
||||||
|
SoLoader.init(app, OpenSourceMergedSoMapping.INSTANCE);
|
||||||
|
} catch (java.io.IOException error) {
|
||||||
|
throw new AssertionError("Could not initialize React Native", error);
|
||||||
|
}
|
||||||
|
DisplayMetricsHolder.initDisplayMetrics(configured);
|
||||||
|
ThemedReactContext context = new ThemedReactContext(new BridgeReactContext(configured), configured);
|
||||||
|
|
||||||
|
WritableMapBuffer attributes = new WritableMapBuffer();
|
||||||
|
attributes.put(4, 15.0); // TextAttributeProps.TA_KEY_FONT_SIZE
|
||||||
|
attributes.put(9, true); // TextAttributeProps.TA_KEY_ALLOW_FONT_SCALING
|
||||||
|
WritableMapBuffer fragment = new WritableMapBuffer();
|
||||||
|
fragment.put(0, "v0.12.0");
|
||||||
|
fragment.put(1, 1);
|
||||||
|
fragment.put(5, attributes);
|
||||||
|
WritableMapBuffer fragments = new WritableMapBuffer();
|
||||||
|
fragments.put(0, fragment);
|
||||||
|
WritableMapBuffer text = new WritableMapBuffer();
|
||||||
|
text.put(1, "v0.12.0");
|
||||||
|
text.put(2, fragments);
|
||||||
|
text.put(4, attributes);
|
||||||
|
WritableMapBuffer paragraph = new WritableMapBuffer();
|
||||||
|
paragraph.put(2, "highQuality");
|
||||||
|
paragraph.put(5, "none");
|
||||||
|
|
||||||
|
long measured = TextLayoutManager.measureText(context, text, paragraph,
|
||||||
|
1000, YogaMeasureMode.AT_MOST, 1000, YogaMeasureMode.AT_MOST, null, null);
|
||||||
|
int width = (int) Math.ceil(PixelUtil.toPixelFromDIP(YogaMeasureOutput.getWidth(measured)));
|
||||||
|
int height = (int) Math.ceil(PixelUtil.toPixelFromDIP(YogaMeasureOutput.getHeight(measured)));
|
||||||
|
Spannable content = TextLayoutManager.INSTANCE.getOrCreateSpannableForText(context, text, null);
|
||||||
|
ReactTextView view = new ReactTextView(context);
|
||||||
|
view.setId(2); // Fabric tag, matching the app's new-architecture Text path.
|
||||||
|
view.setText(new ReactTextUpdate(content, -1, false, 0,
|
||||||
|
Layout.BREAK_STRATEGY_HIGH_QUALITY, Layout.JUSTIFICATION_MODE_NONE));
|
||||||
|
view.measure(exact(width), exact(height));
|
||||||
|
view.layout(0, 0, width, height);
|
||||||
|
Layout rendered = view.getLayout();
|
||||||
|
Log.i("AboutFontWeightTest", "adjustment=" + adjustment + " box=" + width + "x" + height
|
||||||
|
+ " lines=" + rendered.getLineCount() + " lineWidth=" + rendered.getLineWidth(0)
|
||||||
|
+ " weight=" + view.getTypeface().getWeight());
|
||||||
|
assertEquals("The complete version should fit on its measured line", 1, rendered.getLineCount());
|
||||||
|
assertTrue("The last digit must remain inside the allocated width",
|
||||||
|
rendered.getLineWidth(0) <= width);
|
||||||
|
assertTrue("The complete line must remain inside the allocated height",
|
||||||
|
rendered.getLineBottom(0) <= height);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int exact(int size) {
|
||||||
|
return View.MeasureSpec.makeMeasureSpec(size, View.MeasureSpec.EXACTLY);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# About version clipping reproduction ([#4368](https://github.com/getpaseo/paseo/issues/4368))
|
||||||
|
|
||||||
|
This is a diagnostic branch, with no production fix. The Android test compares
|
||||||
|
React Native's measured box for `v0.12.0` with the native TextView layout that
|
||||||
|
renders the same attributed text. It runs both ordinary and system-bold text.
|
||||||
|
It does not turn off accessibility scaling or bold text.
|
||||||
|
|
||||||
|
## Run the native test
|
||||||
|
|
||||||
|
From the repository root, prepare a development Android project if necessary:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd packages/app
|
||||||
|
APP_VARIANT=development npx expo prebuild --platform android --no-install
|
||||||
|
```
|
||||||
|
|
||||||
|
In generated `android/app/build.gradle`, add:
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
android.defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
dependencies {
|
||||||
|
androidTestImplementation 'androidx.test:runner:1.6.2'
|
||||||
|
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy this test into the generated test source set, then build the app and tests:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir -p android/app/src/androidTest/java/sh/paseo/diagnosis
|
||||||
|
cp e2e/mobile/about-font-size/AboutFontWeightTest.java android/app/src/androidTest/java/sh/paseo/diagnosis/
|
||||||
|
cd android
|
||||||
|
APP_VARIANT=development ./gradlew :app:assembleDebug :app:assembleDebugAndroidTest \
|
||||||
|
-PreactNativeArchitectures=x86_64 --no-daemon --max-workers=2 -Dorg.gradle.parallel=false
|
||||||
|
adb install -r app/build/outputs/apk/debug/app-debug.apk
|
||||||
|
adb install -r app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk
|
||||||
|
adb shell am instrument -w -e class sh.paseo.diagnosis.AboutFontWeightTest \
|
||||||
|
sh.paseo.debug.test/androidx.test.runner.AndroidJUnitRunner
|
||||||
|
```
|
||||||
|
|
||||||
|
The test requires Android 12 or later for `fontWeightAdjustment`. The native
|
||||||
|
contexts set that adjustment to 0 and 300 independently; they do not change the
|
||||||
|
device setting. The selected device's density and font scale still apply.
|
||||||
|
|
||||||
|
On Android 16 at 540 dpi and 2× font scale, both contexts receive a 301×123-pixel
|
||||||
|
box. The ordinary-weight control passes, with one rendered line. The bold test
|
||||||
|
fails with two rendered lines, so the final digit falls outside the allocated
|
||||||
|
single-line height. This isolates the measurement/rendering disagreement from
|
||||||
|
the About row's flex layout. A native dependency/build decision is still needed
|
||||||
|
before implementing a fix; changing an About-row style would leave the producer
|
||||||
|
of the incorrect measurement in place.
|
||||||
|
|
||||||
|
## UI comparison
|
||||||
|
|
||||||
|
Observed on Android 16, with current main's JavaScript and a freshly built native
|
||||||
|
development app: open Settings → About with 2× system font scale and 540 dpi on
|
||||||
|
a 1080-pixel-wide emulator. With system bold text, the displayed app version
|
||||||
|
loses its final digit (`v0.12.`). At the same display and text sizes without
|
||||||
|
system bold, the complete `v0.12.0` is visible. Cold-launch after configuration
|
||||||
|
changes before comparing; changing font scale in a running app also produced
|
||||||
|
temporary vertical clipping, which disappeared after a cold launch.
|
||||||
|
|
||||||
|
This substitutes standard Android accessibility settings for Samsung Easy Mode.
|
||||||
|
It verifies the truncated-version symptom, not Samsung's complete UI or the
|
||||||
|
already-fixed home-screen overlap. The reporter used Paseo 0.7.2; this branch
|
||||||
|
uses main `dd244c076d5f17d56d5592c7fb173dc7fdc2bbae`.
|
||||||
Reference in New Issue
Block a user