|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 package org.mozilla.gecko.tests.helpers; |
|
6 |
|
7 import static org.mozilla.gecko.tests.helpers.AssertionHelper.fFail; |
|
8 |
|
9 import java.lang.reflect.Field; |
|
10 |
|
11 import android.content.Context; |
|
12 import android.view.View; |
|
13 |
|
14 /** |
|
15 * Provides helper functions for accessing Android framework features |
|
16 * |
|
17 * This class uses reflection to access framework functionalities that are |
|
18 * unavailable through the regular Android API. Using reflection in this |
|
19 * case is okay because it does not touch Gecko classes that go through |
|
20 * ProGuard. |
|
21 */ |
|
22 public final class FrameworkHelper { |
|
23 |
|
24 private FrameworkHelper() { /* To disallow instantiation. */ } |
|
25 |
|
26 private static Field getClassField(final Class<?> clazz, final String fieldName) |
|
27 throws NoSuchFieldException { |
|
28 Class<?> cls = clazz; |
|
29 do { |
|
30 try { |
|
31 return cls.getDeclaredField(fieldName); |
|
32 } catch (final NoSuchFieldException e) { |
|
33 cls = cls.getSuperclass(); |
|
34 } |
|
35 } while (cls != null); |
|
36 // We tried getDeclaredField before; now try getField instead. |
|
37 // getField behaves differently in that getField traverses the inheritance |
|
38 // list, but it only works on public fields. While getField won't get us |
|
39 // anything new, it makes code cleaner by throwing an exception for us. |
|
40 return clazz.getField(fieldName); |
|
41 } |
|
42 |
|
43 private static Object getField(final Object obj, final String fieldName) { |
|
44 try { |
|
45 final Field field = getClassField(obj.getClass(), fieldName); |
|
46 final boolean accessible = field.isAccessible(); |
|
47 field.setAccessible(true); |
|
48 final Object ret = field.get(obj); |
|
49 field.setAccessible(accessible); |
|
50 return ret; |
|
51 } catch (final NoSuchFieldException e) { |
|
52 // We expect a valid field name; if it's not valid, |
|
53 // the caller is doing something wrong and should be fixed. |
|
54 fFail("Argument field should be a valid field name: " + e.toString()); |
|
55 } catch (final IllegalAccessException e) { |
|
56 // This should not happen. If it does, setAccessible above is not working. |
|
57 fFail("Field should be accessible: " + e.toString()); |
|
58 } |
|
59 throw new IllegalStateException("Should not continue from previous failures"); |
|
60 } |
|
61 |
|
62 private static void setField(final Object obj, final String fieldName, final Object value) { |
|
63 try { |
|
64 final Field field = getClassField(obj.getClass(), fieldName); |
|
65 final boolean accessible = field.isAccessible(); |
|
66 field.setAccessible(true); |
|
67 field.set(obj, value); |
|
68 field.setAccessible(accessible); |
|
69 return; |
|
70 } catch (final NoSuchFieldException e) { |
|
71 // We expect a valid field name; if it's not valid, |
|
72 // the caller is doing something wrong and should be fixed. |
|
73 fFail("Argument field should be a valid field name: " + e.toString()); |
|
74 } catch (final IllegalAccessException e) { |
|
75 // This should not happen. If it does, setAccessible above is not working. |
|
76 fFail("Field should be accessible: " + e.toString()); |
|
77 } |
|
78 throw new IllegalStateException("Cannot continue from previous failures"); |
|
79 } |
|
80 |
|
81 public static Context getViewContext(final View v) { |
|
82 return (Context) getField(v, "mContext"); |
|
83 } |
|
84 |
|
85 public static void setViewContext(final View v, final Context c) { |
|
86 setField(v, "mContext", c); |
|
87 } |
|
88 } |