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