mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-20 16:22:43 +00:00
Compare commits
1
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