• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.shadows;
2 
3 import static android.os.Build.VERSION_CODES.L;
4 import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;
5 import static android.os.Build.VERSION_CODES.M;
6 import static android.os.Build.VERSION_CODES.N;
7 import static android.os.Build.VERSION_CODES.N_MR1;
8 import static android.os.Build.VERSION_CODES.O;
9 import static android.os.Build.VERSION_CODES.O_MR1;
10 import static android.os.Build.VERSION_CODES.P;
11 import static android.os.Build.VERSION_CODES.Q;
12 import static org.robolectric.annotation.TextLayoutMode.Mode.REALISTIC;
13 
14 import android.graphics.ColorFilter;
15 import android.graphics.Paint;
16 import android.graphics.Paint.FontMetricsInt;
17 import android.graphics.PathEffect;
18 import android.graphics.RectF;
19 import android.graphics.Shader;
20 import android.graphics.Typeface;
21 import org.robolectric.annotation.ClassName;
22 import org.robolectric.annotation.Implementation;
23 import org.robolectric.annotation.Implements;
24 import org.robolectric.annotation.InDevelopment;
25 import org.robolectric.annotation.RealObject;
26 import org.robolectric.annotation.TextLayoutMode;
27 import org.robolectric.config.ConfigurationRegistry;
28 import org.robolectric.shadow.api.Shadow;
29 import org.robolectric.util.ReflectionHelpers.ClassParameter;
30 import org.robolectric.versioning.AndroidVersions;
31 import org.robolectric.versioning.AndroidVersions.U;
32 import org.robolectric.versioning.AndroidVersions.V;
33 
34 @SuppressWarnings({"UnusedDeclaration"})
35 @Implements(value = Paint.class)
36 public class ShadowPaint {
37 
38   private int color;
39   private Paint.Style style;
40   private Paint.Cap cap;
41   private Paint.Join join;
42   private float width;
43   private float shadowRadius;
44   private float shadowDx;
45   private float shadowDy;
46   private int shadowColor;
47   private Shader shader;
48   private int alpha;
49   private ColorFilter filter;
50   private boolean antiAlias;
51   private boolean dither;
52   private int flags;
53   private PathEffect pathEffect;
54   private float letterSpacing;
55   private float textScaleX = 1f;
56   private float textSkewX;
57   private float wordSpacing;
58 
59   @RealObject Paint paint;
60   private Typeface typeface;
61   private float textSize;
62   private Paint.Align textAlign = Paint.Align.LEFT;
63 
64   @Implementation
__constructor__(Paint otherPaint)65   protected void __constructor__(Paint otherPaint) {
66     ShadowPaint otherShadowPaint = Shadow.extract(otherPaint);
67     this.color = otherShadowPaint.color;
68     this.style = otherShadowPaint.style;
69     this.cap = otherShadowPaint.cap;
70     this.join = otherShadowPaint.join;
71     this.width = otherShadowPaint.width;
72     this.shadowRadius = otherShadowPaint.shadowRadius;
73     this.shadowDx = otherShadowPaint.shadowDx;
74     this.shadowDy = otherShadowPaint.shadowDy;
75     this.shadowColor = otherShadowPaint.shadowColor;
76     this.shader = otherShadowPaint.shader;
77     this.alpha = otherShadowPaint.alpha;
78     this.filter = otherShadowPaint.filter;
79     this.antiAlias = otherShadowPaint.antiAlias;
80     this.dither = otherShadowPaint.dither;
81     this.flags = otherShadowPaint.flags;
82     this.pathEffect = otherShadowPaint.pathEffect;
83     this.letterSpacing = otherShadowPaint.letterSpacing;
84     this.textScaleX = otherShadowPaint.textScaleX;
85     this.textSkewX = otherShadowPaint.textSkewX;
86     this.wordSpacing = otherShadowPaint.wordSpacing;
87 
88     Shadow.invokeConstructor(Paint.class, paint, ClassParameter.from(Paint.class, otherPaint));
89   }
90 
91   @Implementation(minSdk = N)
nInit()92   protected static long nInit() {
93     return 1;
94   }
95 
96   @Implementation
getFlags()97   protected int getFlags() {
98     return flags;
99   }
100 
101   @Implementation
setFlags(int flags)102   protected void setFlags(int flags) {
103     this.flags = flags;
104   }
105 
106   @Implementation
setUnderlineText(boolean underlineText)107   protected void setUnderlineText(boolean underlineText) {
108     if (underlineText) {
109       setFlags(flags | Paint.UNDERLINE_TEXT_FLAG);
110     } else {
111       setFlags(flags & ~Paint.UNDERLINE_TEXT_FLAG);
112     }
113   }
114 
115   @Implementation
setStrikeThruText(boolean strikeThruText)116   protected void setStrikeThruText(boolean strikeThruText) {
117     if (strikeThruText) {
118       setFlags(flags | Paint.STRIKE_THRU_TEXT_FLAG);
119     } else {
120       setFlags(flags & ~Paint.STRIKE_THRU_TEXT_FLAG);
121     }
122   }
123 
124   @Implementation
setShader(Shader shader)125   protected Shader setShader(Shader shader) {
126     this.shader = shader;
127     return shader;
128   }
129 
130   @Implementation
getAlpha()131   protected int getAlpha() {
132     return alpha;
133   }
134 
135   @Implementation
setAlpha(int alpha)136   protected void setAlpha(int alpha) {
137     this.alpha = alpha;
138   }
139 
140   @Implementation
getShader()141   protected Shader getShader() {
142     return shader;
143   }
144 
145   @Implementation
setColor(int color)146   protected void setColor(int color) {
147     this.color = color;
148   }
149 
150   @Implementation
getColor()151   protected int getColor() {
152     return color;
153   }
154 
155   @Implementation
setStyle(Paint.Style style)156   protected void setStyle(Paint.Style style) {
157     this.style = style;
158   }
159 
160   @Implementation
getStyle()161   protected Paint.Style getStyle() {
162     return style;
163   }
164 
165   @Implementation
setStrokeCap(Paint.Cap cap)166   protected void setStrokeCap(Paint.Cap cap) {
167     this.cap = cap;
168   }
169 
170   @Implementation
getStrokeCap()171   protected Paint.Cap getStrokeCap() {
172     return cap;
173   }
174 
175   @Implementation
setStrokeJoin(Paint.Join join)176   protected void setStrokeJoin(Paint.Join join) {
177     this.join = join;
178   }
179 
180   @Implementation
getStrokeJoin()181   protected Paint.Join getStrokeJoin() {
182     return join;
183   }
184 
185   @Implementation
setStrokeWidth(float width)186   protected void setStrokeWidth(float width) {
187     this.width = width;
188   }
189 
190   @Implementation
getStrokeWidth()191   protected float getStrokeWidth() {
192     return width;
193   }
194 
195   @Implementation
setShadowLayer(float radius, float dx, float dy, int color)196   protected void setShadowLayer(float radius, float dx, float dy, int color) {
197     shadowRadius = radius;
198     shadowDx = dx;
199     shadowDy = dy;
200     shadowColor = color;
201   }
202 
203   @Implementation
getTypeface()204   protected Typeface getTypeface() {
205     return typeface;
206   }
207 
208   @Implementation
setTypeface(Typeface typeface)209   protected Typeface setTypeface(Typeface typeface) {
210     this.typeface = typeface;
211     return typeface;
212   }
213 
214   @Implementation(minSdk = AndroidVersions.Baklava.SDK_INT)
215   @InDevelopment
setTypefaceWithoutWarning(Typeface typeface)216   protected Typeface setTypefaceWithoutWarning(Typeface typeface) {
217     this.typeface = typeface;
218     return typeface;
219   }
220 
221   @Implementation
getTextSize()222   protected float getTextSize() {
223     return textSize;
224   }
225 
226   @Implementation
setTextSize(float textSize)227   protected void setTextSize(float textSize) {
228     this.textSize = textSize;
229   }
230 
231   @Implementation
getTextScaleX()232   protected float getTextScaleX() {
233     return textScaleX;
234   }
235 
236   @Implementation
setTextScaleX(float scaleX)237   protected void setTextScaleX(float scaleX) {
238     this.textScaleX = scaleX;
239   }
240 
241   @Implementation
getTextSkewX()242   protected float getTextSkewX() {
243     return textSkewX;
244   }
245 
246   @Implementation
setTextSkewX(float skewX)247   protected void setTextSkewX(float skewX) {
248     this.textSkewX = skewX;
249   }
250 
251   @Implementation(minSdk = L)
getLetterSpacing()252   protected float getLetterSpacing() {
253     return letterSpacing;
254   }
255 
256   @Implementation(minSdk = L)
setLetterSpacing(float letterSpacing)257   protected void setLetterSpacing(float letterSpacing) {
258     this.letterSpacing = letterSpacing;
259   }
260 
261   @Implementation(minSdk = Q)
getWordSpacing()262   protected float getWordSpacing() {
263     return wordSpacing;
264   }
265 
266   @Implementation(minSdk = Q)
setWordSpacing(float wordSpacing)267   protected void setWordSpacing(float wordSpacing) {
268     this.wordSpacing = wordSpacing;
269   }
270 
271   @Implementation
setTextAlign(Paint.Align align)272   protected void setTextAlign(Paint.Align align) {
273     textAlign = align;
274   }
275 
276   @Implementation
getTextAlign()277   protected Paint.Align getTextAlign() {
278     return textAlign;
279   }
280 
281   /**
282    * @return shadow radius (Paint related shadow, not Robolectric Shadow)
283    */
getShadowRadius()284   public float getShadowRadius() {
285     return shadowRadius;
286   }
287 
288   /**
289    * @return shadow Dx (Paint related shadow, not Robolectric Shadow)
290    */
getShadowDx()291   public float getShadowDx() {
292     return shadowDx;
293   }
294 
295   /**
296    * @return shadow Dx (Paint related shadow, not Robolectric Shadow)
297    */
getShadowDy()298   public float getShadowDy() {
299     return shadowDy;
300   }
301 
302   /**
303    * @return shadow color (Paint related shadow, not Robolectric Shadow)
304    */
getShadowColor()305   public int getShadowColor() {
306     return shadowColor;
307   }
308 
getCap()309   public Paint.Cap getCap() {
310     return cap;
311   }
312 
getJoin()313   public Paint.Join getJoin() {
314     return join;
315   }
316 
getWidth()317   public float getWidth() {
318     return width;
319   }
320 
321   @Implementation
getColorFilter()322   protected ColorFilter getColorFilter() {
323     return filter;
324   }
325 
326   @Implementation
setColorFilter(ColorFilter filter)327   protected ColorFilter setColorFilter(ColorFilter filter) {
328     this.filter = filter;
329     return filter;
330   }
331 
332   @Implementation
setAntiAlias(boolean antiAlias)333   protected void setAntiAlias(boolean antiAlias) {
334     this.flags = (flags & ~Paint.ANTI_ALIAS_FLAG) | (antiAlias ? Paint.ANTI_ALIAS_FLAG : 0);
335   }
336 
337   @Implementation
setDither(boolean dither)338   protected void setDither(boolean dither) {
339     this.dither = dither;
340   }
341 
342   @Implementation
isDither()343   protected boolean isDither() {
344     return dither;
345   }
346 
347   @Implementation
isAntiAlias()348   protected boolean isAntiAlias() {
349     return (flags & Paint.ANTI_ALIAS_FLAG) == Paint.ANTI_ALIAS_FLAG;
350   }
351 
352   @Implementation
isFilterBitmap()353   protected boolean isFilterBitmap() {
354     return (flags & Paint.FILTER_BITMAP_FLAG) == Paint.FILTER_BITMAP_FLAG;
355   }
356 
357   @Implementation
setFilterBitmap(boolean filterBitmap)358   protected void setFilterBitmap(boolean filterBitmap) {
359     this.flags =
360         (flags & ~Paint.FILTER_BITMAP_FLAG) | (filterBitmap ? Paint.FILTER_BITMAP_FLAG : 0);
361   }
362 
363   @Implementation
getPathEffect()364   protected PathEffect getPathEffect() {
365     return pathEffect;
366   }
367 
368   @Implementation
setPathEffect(PathEffect effect)369   protected PathEffect setPathEffect(PathEffect effect) {
370     this.pathEffect = effect;
371     return effect;
372   }
373 
374   @Implementation
measureText(String text)375   protected float measureText(String text) {
376     return applyTextScaleX(text.length());
377   }
378 
379   @Implementation
measureText(CharSequence text, int start, int end)380   protected float measureText(CharSequence text, int start, int end) {
381     return applyTextScaleX(end - start);
382   }
383 
384   @Implementation
measureText(String text, int start, int end)385   protected float measureText(String text, int start, int end) {
386     return applyTextScaleX(end - start);
387   }
388 
389   @Implementation
measureText(char[] text, int index, int count)390   protected float measureText(char[] text, int index, int count) {
391     return applyTextScaleX(count);
392   }
393 
applyTextScaleX(float textWidth)394   private float applyTextScaleX(float textWidth) {
395     return Math.max(0f, textScaleX) * textWidth;
396   }
397 
398   @Implementation(maxSdk = M)
native_breakText( long native_object, long native_typeface, char[] text, int index, int count, float maxWidth, int bidiFlags, float[] measuredWidth)399   protected static int native_breakText(
400       long native_object,
401       long native_typeface,
402       char[] text,
403       int index,
404       int count,
405       float maxWidth,
406       int bidiFlags,
407       float[] measuredWidth) {
408     return breakText(text, maxWidth, measuredWidth);
409   }
410 
411   @Implementation(minSdk = N, maxSdk = O_MR1)
nBreakText( long nObject, long nTypeface, char[] text, int index, int count, float maxWidth, int bidiFlags, float[] measuredWidth)412   protected static int nBreakText(
413       long nObject,
414       long nTypeface,
415       char[] text,
416       int index,
417       int count,
418       float maxWidth,
419       int bidiFlags,
420       float[] measuredWidth) {
421     return breakText(text, maxWidth, measuredWidth);
422   }
423 
424   @Implementation(minSdk = P)
nBreakText( long nObject, char[] text, int index, int count, float maxWidth, int bidiFlags, float[] measuredWidth)425   protected static int nBreakText(
426       long nObject,
427       char[] text,
428       int index,
429       int count,
430       float maxWidth,
431       int bidiFlags,
432       float[] measuredWidth) {
433     return breakText(text, maxWidth, measuredWidth);
434   }
435 
breakText(char[] text, float maxWidth, float[] measuredWidth)436   private static int breakText(char[] text, float maxWidth, float[] measuredWidth) {
437     if (measuredWidth != null) {
438       measuredWidth[0] = maxWidth;
439     }
440     return text.length;
441   }
442 
443   @Implementation(maxSdk = M)
native_breakText( long native_object, long native_typeface, String text, boolean measureForwards, float maxWidth, int bidiFlags, float[] measuredWidth)444   protected static int native_breakText(
445       long native_object,
446       long native_typeface,
447       String text,
448       boolean measureForwards,
449       float maxWidth,
450       int bidiFlags,
451       float[] measuredWidth) {
452     return breakText(text, maxWidth, measuredWidth);
453   }
454 
455   @Implementation(minSdk = N, maxSdk = O_MR1)
nBreakText( long nObject, long nTypeface, String text, boolean measureForwards, float maxWidth, int bidiFlags, float[] measuredWidth)456   protected static int nBreakText(
457       long nObject,
458       long nTypeface,
459       String text,
460       boolean measureForwards,
461       float maxWidth,
462       int bidiFlags,
463       float[] measuredWidth) {
464     return breakText(text, maxWidth, measuredWidth);
465   }
466 
467   @Implementation(minSdk = P)
nBreakText( long nObject, String text, boolean measureForwards, float maxWidth, int bidiFlags, float[] measuredWidth)468   protected static int nBreakText(
469       long nObject,
470       String text,
471       boolean measureForwards,
472       float maxWidth,
473       int bidiFlags,
474       float[] measuredWidth) {
475     return breakText(text, maxWidth, measuredWidth);
476   }
477 
breakText(String text, float maxWidth, float[] measuredWidth)478   private static int breakText(String text, float maxWidth, float[] measuredWidth) {
479     if (measuredWidth != null) {
480       measuredWidth[0] = maxWidth;
481     }
482     return text.length();
483   }
484 
485   @Implementation(minSdk = V.SDK_INT)
nGetFontMetricsInt( long paintPtr, FontMetricsInt fmi, boolean useLocale)486   protected static int nGetFontMetricsInt(
487       long paintPtr, FontMetricsInt fmi, /* Ignored */ boolean useLocale) {
488     return nGetFontMetricsInt(paintPtr, fmi);
489   }
490 
491   @Implementation(minSdk = P, maxSdk = U.SDK_INT)
nGetFontMetricsInt(long paintPtr, FontMetricsInt fmi)492   protected static int nGetFontMetricsInt(long paintPtr, FontMetricsInt fmi) {
493     if (ConfigurationRegistry.get(TextLayoutMode.Mode.class) == REALISTIC) {
494       // TODO: hack, just set values to those we see on emulator
495       int descent = 7;
496       int ascent = -28;
497       int leading = 0;
498 
499       if (fmi != null) {
500         fmi.top = -32;
501         fmi.ascent = ascent;
502         fmi.descent = descent;
503         fmi.bottom = 9;
504         fmi.leading = leading;
505       }
506       return descent - ascent + leading;
507     }
508     return 0;
509   }
510 
511   @Implementation(minSdk = O, maxSdk = O_MR1)
nGetFontMetricsInt( long nativePaint, long nativeTypeface, FontMetricsInt fmi)512   protected static int nGetFontMetricsInt(
513       long nativePaint, long nativeTypeface, FontMetricsInt fmi) {
514     return nGetFontMetricsInt(nativePaint, fmi);
515   }
516 
517   @Implementation(minSdk = N, maxSdk = N_MR1)
nGetFontMetricsInt( long nativePaint, long nativeTypeface, @ClassName("android.graphics.Paint$FontMetricsInt") Object fmi)518   protected int nGetFontMetricsInt(
519       long nativePaint,
520       long nativeTypeface,
521       @ClassName("android.graphics.Paint$FontMetricsInt") Object fmi) {
522     return nGetFontMetricsInt(nativePaint, (FontMetricsInt) fmi);
523   }
524 
525   @Implementation(maxSdk = M)
getFontMetricsInt(FontMetricsInt fmi)526   protected int getFontMetricsInt(FontMetricsInt fmi) {
527     return nGetFontMetricsInt(0, fmi);
528   }
529 
530   @Implementation(minSdk = P)
nGetRunAdvance( long paintPtr, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset)531   protected static float nGetRunAdvance(
532       long paintPtr,
533       char[] text,
534       int start,
535       int end,
536       int contextStart,
537       int contextEnd,
538       boolean isRtl,
539       int offset) {
540     if (ConfigurationRegistry.get(TextLayoutMode.Mode.class) == REALISTIC) {
541       // be consistent with measureText for measurements, and measure 1 pixel per char
542       return end - start;
543     }
544     return 0f;
545   }
546 
547   @Implementation(minSdk = U.SDK_INT, maxSdk = U.SDK_INT)
nGetRunCharacterAdvance( long paintPtr, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset, float[] advances, int advancesIndex)548   protected static float nGetRunCharacterAdvance(
549       long paintPtr,
550       char[] text,
551       int start,
552       int end,
553       int contextStart,
554       int contextEnd,
555       boolean isRtl,
556       int offset,
557       float[] advances,
558       int advancesIndex) {
559     return nGetRunAdvance(paintPtr, text, start, end, contextStart, contextEnd, isRtl, offset);
560   }
561 
562   @Implementation(minSdk = V.SDK_INT)
563   @InDevelopment
nGetRunCharacterAdvance( long paintPtr, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset, float[] advances, int advancesIndex, RectF drawingBounds, @ClassName("android.graphics.Paint$RunInfo") Object runInfo)564   protected static float nGetRunCharacterAdvance(
565       long paintPtr,
566       char[] text,
567       int start,
568       int end,
569       int contextStart,
570       int contextEnd,
571       boolean isRtl,
572       int offset,
573       float[] advances,
574       int advancesIndex,
575       RectF drawingBounds,
576       @ClassName("android.graphics.Paint$RunInfo") Object runInfo) {
577     return nGetRunCharacterAdvance(
578         paintPtr,
579         text,
580         start,
581         end,
582         contextStart,
583         contextEnd,
584         isRtl,
585         offset,
586         advances,
587         advancesIndex);
588   }
589 
590   @Implementation(minSdk = N, maxSdk = O_MR1)
nGetRunAdvance( long paintPtr, long typefacePtr, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset)591   protected static float nGetRunAdvance(
592       long paintPtr,
593       long typefacePtr,
594       char[] text,
595       int start,
596       int end,
597       int contextStart,
598       int contextEnd,
599       boolean isRtl,
600       int offset) {
601     return nGetRunAdvance(paintPtr, text, start, end, contextStart, contextEnd, isRtl, offset);
602   }
603 
604   @Implementation(minSdk = M, maxSdk = M)
native_getRunAdvance( long nativeObject, long nativeTypeface, char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset)605   protected static float native_getRunAdvance(
606       long nativeObject,
607       long nativeTypeface,
608       char[] text,
609       int start,
610       int end,
611       int contextStart,
612       int contextEnd,
613       boolean isRtl,
614       int offset) {
615     return nGetRunAdvance(0, text, start, end, contextStart, contextEnd, isRtl, offset);
616   }
617 
618   @Implementation(maxSdk = LOLLIPOP_MR1)
native_getTextRunAdvances( long nativeObject, long nativeTypeface, char[] text, int index, int count, int contextIndex, int contextCount, boolean isRtl, float[] advances, int advancesIndex)619   protected static float native_getTextRunAdvances(
620       long nativeObject,
621       long nativeTypeface,
622       char[] text,
623       int index,
624       int count,
625       int contextIndex,
626       int contextCount,
627       boolean isRtl,
628       float[] advances,
629       int advancesIndex) {
630     return nGetRunAdvance(
631         0, text, index, index + count, contextIndex, contextIndex + contextCount, isRtl, index);
632   }
633 
634   @Implementation(maxSdk = LOLLIPOP_MR1)
native_getTextRunAdvances( long nativeObject, long nativeTypeface, String text, int start, int end, int contextStart, int contextEnd, boolean isRtl, float[] advances, int advancesIndex)635   protected static float native_getTextRunAdvances(
636       long nativeObject,
637       long nativeTypeface,
638       String text,
639       int start,
640       int end,
641       int contextStart,
642       int contextEnd,
643       boolean isRtl,
644       float[] advances,
645       int advancesIndex) {
646     return nGetRunAdvance(0, text.toCharArray(), start, end, contextStart, contextEnd, isRtl, 0);
647   }
648 }
649