1111import android .text .TextPaint ;
1212import android .text .style .CharacterStyle ;
1313import android .text .style .ForegroundColorSpan ;
14- import android .text .style .MetricAffectingSpan ;
1514import android .text .style .RelativeSizeSpan ;
1615import android .text .style .ScaleXSpan ;
16+ import android .util .DisplayMetrics ;
1717
1818import androidx .annotation .GuardedBy ;
1919import androidx .annotation .Nullable ;
@@ -292,33 +292,26 @@ private static boolean updateDislike(AtomicReference<Object> textRef, boolean is
292292 String middleSegmentedSeparatorString = " • " ;
293293 Spannable leftSeparatorSpan = newSpanUsingStylingOfAnotherSpan (oldSpannable , leftSegmentedSeparatorString );
294294 Spannable middleSeparatorSpan = newSpanUsingStylingOfAnotherSpan (oldSpannable , middleSegmentedSeparatorString );
295- // style the separator appearance to mimic the existing layout
296295 final int separatorColor = ThemeHelper .isDarkTheme ()
297- ? 0xFF414141 // dark gray
296+ ? 0x37A0A0A0 // transparent dark gray
298297 : 0xFFD9D9D9 ; // light gray
299298 addSpanStyling (leftSeparatorSpan , new ForegroundColorSpan (separatorColor ));
300299 addSpanStyling (middleSeparatorSpan , new ForegroundColorSpan (separatorColor ));
301- MetricAffectingSpan separatorStyle = new MetricAffectingSpan () {
302- final float separatorHorizontalCompression = 0.71f ; // horizontally compress the separator and its spacing
303-
304- @ Override
305- public void updateMeasureState (TextPaint tp ) {
306- tp .setTextScaleX (separatorHorizontalCompression );
307- }
308-
300+ CharacterStyle noAntiAliasingStyle = new CharacterStyle () {
309301 @ Override
310302 public void updateDrawState (TextPaint tp ) {
311- tp .setTextScaleX (separatorHorizontalCompression );
312- tp .setAntiAlias (false );
303+ tp .setAntiAlias (false ); // draw without anti-aliasing, to give a sharper edge
313304 }
314305 };
315- addSpanStyling (leftSeparatorSpan , separatorStyle );
316- addSpanStyling (middleSeparatorSpan , separatorStyle );
306+ addSpanStyling (leftSeparatorSpan , noAntiAliasingStyle );
307+ addSpanStyling (middleSeparatorSpan , noAntiAliasingStyle );
317308
318309 Spannable dislikeSpan = newSpannableWithDislikes (oldSpannable , voteData );
319310
320- // use a larger font size on the left separator, but this causes the entire span (including the like/dislike text)
321- // to move downward. Use a custom span to adjust the span back upward, at a relative ratio
311+ // Increase the size of the left separator, so it better matches the stock separator on the right.
312+ // But when using a larger font, the entire span (including the like/dislike text) becomes shifted downward.
313+ // To correct this, use additional spans to move the alignment back upward by a relative amount.
314+ setSegmentedAdjustmentValues ();
322315 class RelativeVerticalOffsetSpan extends CharacterStyle {
323316 final float relativeVerticalShiftRatio ;
324317
@@ -331,24 +324,17 @@ public void updateDrawState(TextPaint tp) {
331324 tp .baselineShift -= (int ) (relativeVerticalShiftRatio * tp .getFontMetrics ().top );
332325 }
333326 }
327+ // shift everything up, to compensate for the vertical movement caused by the font change below
328+ // each section needs it's own Relative span, otherwise alignment is wrong
329+ addSpanStyling (leftSeparatorSpan , new RelativeVerticalOffsetSpan (segmentedLeftSeparatorVerticalShiftRatio ));
334330
335- // Ratio values tested on Android 13, Samsung, Google and OnePlus branded phones, using screen densities of 300 to 560
336- // On other devices and fonts the left separator may be vertically shifted by a few pixels,
337- // but it's good enough and still visually better than not doing this scaling/shifting
338- final float verticalShiftRatio = -0.38f ; // shift up by 38%
339- final float verticalLeftSeparatorShiftRatio = -0.075f ; // shift up by 8%
340- final float horizontalStretchRatio = 0.92f ; // stretch narrower by 8%
341- final float leftSeparatorFontRatio = 1.87f ; // increase height by 87%
342-
343- addSpanStyling (leftSeparatorSpan , new RelativeSizeSpan (leftSeparatorFontRatio ));
344- addSpanStyling (leftSeparatorSpan , new ScaleXSpan (horizontalStretchRatio ));
345-
346- // shift the left separator up by a smaller amount, to visually align it after changing the size
347- addSpanStyling (leftSeparatorSpan , new RelativeVerticalOffsetSpan (verticalLeftSeparatorShiftRatio ));
348- addSpanStyling (likesSpan , new RelativeVerticalOffsetSpan (verticalShiftRatio ));
349- addSpanStyling (middleSeparatorSpan , new RelativeVerticalOffsetSpan (verticalShiftRatio ));
350- addSpanStyling (dislikeSpan , new RelativeVerticalOffsetSpan (verticalShiftRatio ));
331+ addSpanStyling (likesSpan , new RelativeVerticalOffsetSpan (segmentedVerticalShiftRatio ));
332+ addSpanStyling (middleSeparatorSpan , new RelativeVerticalOffsetSpan (segmentedVerticalShiftRatio ));
333+ addSpanStyling (dislikeSpan , new RelativeVerticalOffsetSpan (segmentedVerticalShiftRatio ));
351334
335+ // important: must add size scaling after vertical offset (otherwise alignment gets off)
336+ addSpanStyling (leftSeparatorSpan , new RelativeSizeSpan (segmentedLeftSeparatorFontRatio ));
337+ addSpanStyling (leftSeparatorSpan , new ScaleXSpan (segmentedLeftSeparatorHorizontalScaleRatio ));
352338 // middle separator does not need resizing
353339
354340 // put everything together
@@ -365,6 +351,65 @@ public void updateDrawState(TextPaint tp) {
365351 return true ;
366352 }
367353
354+ private static boolean segmentedValuesSet = false ;
355+ private static float segmentedVerticalShiftRatio ;
356+ private static float segmentedLeftSeparatorVerticalShiftRatio ;
357+ private static float segmentedLeftSeparatorFontRatio ;
358+ private static float segmentedLeftSeparatorHorizontalScaleRatio ;
359+
360+ /**
361+ * Set the segmented adjustment values, based on the device.
362+ */
363+ private static void setSegmentedAdjustmentValues () {
364+ if (segmentedValuesSet ) {
365+ return ;
366+ }
367+
368+ String deviceManufacturer = Build .MANUFACTURER ;
369+ final int deviceSdkVersion = Build .VERSION .SDK_INT ;
370+ LogHelper .printDebug (() -> "Device manufacturer: '" + deviceManufacturer + "' SDK: " + deviceSdkVersion );
371+
372+ //
373+ // IMPORTANT: configurations must be with the default system font size setting.
374+ //
375+ // In generally, a single configuration will give perfect layout for all devices of the same manufacturer
376+ final String configManufacturer ;
377+ final int configSdk ;
378+ switch (deviceManufacturer ) {
379+ default : // use Google layout by default
380+ case "Google" :
381+ // logging and documentation
382+ configManufacturer = "Google" ;
383+ configSdk = 33 ;
384+ // tested on Android 10 thru 13, and works well for all
385+ segmentedLeftSeparatorVerticalShiftRatio = segmentedVerticalShiftRatio = -0.18f ; // move separators and like/dislike up by 18%
386+ segmentedLeftSeparatorFontRatio = 1.8f ; // increase left separator size by 80%
387+ segmentedLeftSeparatorHorizontalScaleRatio = 0.65f ; // horizontally compress left separator by 35%
388+ break ;
389+ case "samsung" :
390+ configManufacturer = "samsung" ;
391+ configSdk = 33 ;
392+ // tested on S22
393+ segmentedLeftSeparatorVerticalShiftRatio = segmentedVerticalShiftRatio = -0.19f ;
394+ segmentedLeftSeparatorFontRatio = 1.5f ;
395+ segmentedLeftSeparatorHorizontalScaleRatio = 0.7f ;
396+ break ;
397+ case "OnePlus" :
398+ configManufacturer = "oneplus" ;
399+ configSdk = 33 ;
400+ // tested on OnePlus 8 Pro
401+ segmentedLeftSeparatorVerticalShiftRatio = -0.075f ;
402+ segmentedVerticalShiftRatio = -0.38f ;
403+ segmentedLeftSeparatorFontRatio = 1.87f ;
404+ segmentedLeftSeparatorHorizontalScaleRatio = 0.50f ;
405+ break ;
406+
407+ }
408+
409+ LogHelper .printDebug (() -> "Using layout adjustments based on manufacturer: '" + configManufacturer + "' SDK: " + configSdk );
410+ segmentedValuesSet = true ;
411+ }
412+
368413 /**
369414 * Correctly handles any unicode numbers (such as Arabic numbers)
370415 *
0 commit comments