what should be delegated
8 | * @param plugin where delegated to
9 | */
10 | public class AbstractDelegate {
11 |
12 | protected final T mOriginal;
13 |
14 | protected List mPlugins = new CopyOnWriteArrayList<>();
15 |
16 | public AbstractDelegate(final T original) {
17 | mOriginal = original;
18 | }
19 |
20 | @SuppressWarnings("unchecked")
21 | public Removable addPlugin(final P plugin) {
22 | plugin.addToDelegate(this, mOriginal);
23 | mPlugins.add(plugin);
24 |
25 | return new Removable() {
26 | @Override
27 | public void remove() {
28 | mPlugins.remove(plugin);
29 | plugin.removeFromDelegate();
30 | }
31 | };
32 | }
33 |
34 | public T getOriginal() {
35 | return mOriginal;
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/AbstractPlugin.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | import java.util.Stack;
4 |
5 | public class AbstractPlugin {
6 |
7 | final public Stack mSuperListeners = new Stack<>();
8 |
9 | private D mDelegate;
10 |
11 | private T mOriginal;
12 |
13 | public final void addToDelegate(final D delegate, final T original) {
14 | mDelegate = delegate;
15 | mOriginal = original;
16 | onAddedToDelegate();
17 | }
18 |
19 | public D getCompositeDelegate() {
20 | return mDelegate;
21 | }
22 |
23 | public T getOriginal() {
24 | return mOriginal;
25 | }
26 |
27 | public final void removeFromDelegate() {
28 | mDelegate = null;
29 | mOriginal = null;
30 | onRemovedFromDelegated();
31 | }
32 |
33 | public void verifyMethodCalledFromDelegate(final String method) {
34 | if (mSuperListeners.isEmpty()) {
35 | throw new IllegalStateException("Do not call " + method
36 | + " on a ActivityPlugin directly. You have to call mDelegate." + method
37 | + " or the call order of the plugins would be mixed up.");
38 | }
39 | final String superListener = mSuperListeners.peek().getMethodName();
40 | if (!superListener.equals(method)) {
41 | throw new IllegalStateException("You may have called "
42 | + method + " from " + superListener + " instead of calling getOriginal()."
43 | + method + ". Do not call " + method
44 | + " on a ActivityPlugin directly. You have to call mDelegate." + method
45 | + " or the call order of the plugins would be mixed up.");
46 | }
47 | }
48 |
49 | /**
50 | * callback when this plugin was added to a delegate. {@link #getOriginal()} and {@link
51 | * #getCompositeDelegate()} are now set
52 | */
53 | protected void onAddedToDelegate() {
54 |
55 | }
56 |
57 | /**
58 | * callback when this plugin was removed from the delegate. {@link #getOriginal()} and {@link
59 | * #getCompositeDelegate()} are now {@code null}
60 | */
61 | protected void onRemovedFromDelegated() {
62 |
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallFun0.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallFun0 extends NamedSuperCall {
4 |
5 | public CallFun0(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract R call();
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallFun1.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallFun1 extends NamedSuperCall {
4 |
5 | public CallFun1(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract R call(final T1 p1);
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallFun2.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallFun2 extends NamedSuperCall {
4 |
5 | public CallFun2(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract R call(final T1 p1, final T2 p2);
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallFun3.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallFun3 extends NamedSuperCall {
4 |
5 | public CallFun3(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract R call(final T1 p1, final T2 p2, final T3 p3);
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallFun4.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallFun4 extends NamedSuperCall {
4 |
5 | public CallFun4(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract R call(final T1 p1, final T2 p2, final T3 p3, final T4 p4);
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallFun5.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallFun5 extends NamedSuperCall {
4 |
5 | public CallFun5(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract R call(final T1 p1, final T2 p2, final T3 p3, final T4 p4, T5 p5);
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallFun6.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallFun6 extends NamedSuperCall {
4 |
5 | public CallFun6(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract R call(final T1 p1, final T2 p2, final T3 p3, final T4 p4, T5 p5, T6 p6);
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallFun7.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallFun7 extends NamedSuperCall {
4 |
5 | public CallFun7(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract R call(final T1 p1, final T2 p2, final T3 p3, final T4 p4, T5 p5, T6 p6, T7 p7);
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallFun8.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallFun8 extends NamedSuperCall {
4 |
5 | public CallFun8(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract R call(final T1 p1, final T2 p2, final T3 p3, final T4 p4, T5 p5, T6 p6, T7 p7,
10 | T8 p8);
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallFun9.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallFun9 extends NamedSuperCall {
4 |
5 | public CallFun9(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract R call(final T1 p1, final T2 p2, final T3 p3, final T4 p4, T5 p5, T6 p6, T7 p7,
10 | T8 p8, T9 p9);
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallVoid0.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallVoid0 extends NamedSuperCall {
4 |
5 | public CallVoid0(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract void call();
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallVoid1.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallVoid1 extends NamedSuperCall {
4 |
5 | public CallVoid1(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract void call(final T1 p1);
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallVoid2.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallVoid2 extends NamedSuperCall {
4 |
5 | public CallVoid2(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract void call(final T1 p1, final T2 p2);
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallVoid3.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallVoid3 extends NamedSuperCall {
4 |
5 | public CallVoid3(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract void call(final T1 p1, final T2 p2, final T3 p3);
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallVoid4.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallVoid4 extends NamedSuperCall {
4 |
5 | public CallVoid4(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract void call(final T1 p1, final T2 p2, final T3 p3, final T4 p4);
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallVoid5.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallVoid5 extends NamedSuperCall {
4 |
5 | public CallVoid5(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract void call(final T1 p1, final T2 p2, final T3 p3, final T4 p4, T5 p5);
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallVoid6.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallVoid6 extends NamedSuperCall {
4 |
5 | public CallVoid6(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract void call(final T1 p1, final T2 p2, final T3 p3, final T4 p4, T5 p5, T6 p6);
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallVoid7.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallVoid7 extends NamedSuperCall {
4 |
5 | public CallVoid7(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract void call(final T1 p1, final T2 p2, final T3 p3, final T4 p4, T5 p5, T6 p6,
10 | T7 p7);
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallVoid8.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallVoid8 extends NamedSuperCall {
4 |
5 | public CallVoid8(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract void call(final T1 p1, final T2 p2, final T3 p3, final T4 p4, T5 p5, T6 p6,
10 | T7 p7, T8 p8);
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/CallVoid9.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class CallVoid9 extends NamedSuperCall {
4 |
5 | public CallVoid9(final String methodName) {
6 | super(methodName);
7 | }
8 |
9 | public abstract void call(final T1 p1, final T2 p2, final T3 p3, final T4 p4, T5 p5, T6 p6,
10 | T7 p7, T8 p8, T9 p9);
11 | }
12 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/NamedSuperCall.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class NamedSuperCall {
4 |
5 | private final String mMethodName;
6 |
7 | public NamedSuperCall(final String methodName) {
8 | mMethodName = methodName;
9 | }
10 |
11 | public String getMethodName() {
12 | return mMethodName;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/NamedSuperCallVoid.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public abstract class NamedSuperCallVoid implements SuperCall {
4 |
5 | private final String mMethodName;
6 |
7 | public NamedSuperCallVoid(final String methodName) {
8 | mMethodName = methodName;
9 | }
10 |
11 | public String getMethodName() {
12 | return mMethodName;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/Removable.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public interface Removable {
4 |
5 | void remove();
6 | }
7 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/SuperCall.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public interface SuperCall {
4 |
5 | R call(Object... args);
6 | }
7 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/SuperCallVoid.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public interface SuperCallVoid {
4 |
5 | void call(Object... args);
6 | }
7 |
--------------------------------------------------------------------------------
/core/src/main/java/com/pascalwelsch/compositeandroid/core/SuppressedException.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | public class SuppressedException extends RuntimeException {
4 |
5 | /**
6 | * Constructs a new {@code RuntimeException} with the current stack trace
7 | * and the specified cause.
8 | *
9 | * @param throwable the cause of this exception.
10 | */
11 | public SuppressedException(final Throwable throwable) {
12 | super(throwable);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/core/src/test/java/com/pascalwelsch/compositeandroid/core/MixinInheritance.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.ArrayList;
6 | import java.util.Arrays;
7 | import java.util.List;
8 | import java.util.ListIterator;
9 |
10 | import static org.assertj.core.api.Java6Assertions.assertThat;
11 | import static org.assertj.core.api.Java6Assertions.fail;
12 |
13 | public class MixinInheritance {
14 |
15 |
16 | public static class Person {
17 |
18 | List said = new ArrayList<>();
19 |
20 | public void say(String s) {
21 | said.add(s);
22 | }
23 |
24 | public void sayHello() {
25 | say("Hello, I'm John");
26 | }
27 | }
28 |
29 | public static class PersonPlugin {
30 |
31 | public PersonDelegate mDelegate;
32 |
33 | SuperCall mSuperListener;
34 |
35 | public void say(String s) {
36 | mDelegate.say(s);
37 | }
38 |
39 | public void sayHello() {
40 | if (mSuperListener != null) {
41 | mSuperListener.call();
42 | } else {
43 | throw new IllegalStateException(
44 | "Do not call this method on a ActivityPlugin directly. You have to call `mDelegate.sayHello()` or the call order of the plugins would be mixed up.");
45 | }
46 | }
47 | }
48 |
49 | public static class PersonDelegate {
50 |
51 | public PersonPlugin mCallingPlugin;
52 |
53 | List mPlugins = new ArrayList<>();
54 |
55 | private CompositePerson mCompositePerson;
56 |
57 | public PersonDelegate(
58 | final CompositePerson compositePerson) {
59 |
60 | mCompositePerson = compositePerson;
61 | }
62 |
63 | public void say(String s) {
64 | mCompositePerson.say(s);
65 | }
66 |
67 | public void sayHello() {
68 | callHook(new PluginMethodAction() {
69 | @Override
70 | public void call(final PersonPlugin plugin, final Object... args) {
71 | plugin.sayHello();
72 | }
73 | }, new SuperCallVoid() {
74 | @Override
75 | public void call(final Object... args) {
76 | mCompositePerson.sayHello_super();
77 | }
78 | });
79 | }
80 |
81 | protected void callHook(
82 | final PluginMethodAction methodCall,
83 | final SuperCallVoid activitySuper,
84 | final Object... args) {
85 |
86 | final ArrayList plugins = new ArrayList<>(mPlugins);
87 | plugins.remove(mCallingPlugin);
88 |
89 | final ListIterator iterator = plugins.listIterator(plugins.size());
90 | callHook(iterator, methodCall, activitySuper, args);
91 | }
92 |
93 |
94 | void callHook(final ListIterator iterator,
95 | final PluginMethodAction methodCall,
96 | final SuperCallVoid activitySuper,
97 | final Object... args) {
98 |
99 | if (iterator.hasPrevious()) {
100 | final PersonPlugin plugin = iterator.previous();
101 | final SuperCall listener
102 | = new SuperCall() {
103 | @Override
104 | public Void call(final Object... args) {
105 | callHook(iterator, methodCall, activitySuper, args);
106 | return null;
107 | }
108 | };
109 | plugin.mSuperListener = listener;
110 | methodCall.call(plugin, args);
111 | plugin.mSuperListener = null;
112 | } else {
113 | activitySuper.call(args);
114 | }
115 | }
116 | }
117 |
118 | public static class CompositePerson extends Person {
119 |
120 | PersonDelegate delegate = new PersonDelegate(this);
121 |
122 | public void addPlugins(PersonPlugin plugin) {
123 | delegate.mPlugins.add(plugin);
124 | plugin.mDelegate = delegate;
125 | }
126 |
127 | @Override
128 | public void sayHello() {
129 | delegate.sayHello();
130 | }
131 |
132 | public void sayHello_super() {
133 | super.sayHello();
134 | }
135 | }
136 |
137 | public static class Boss extends PersonPlugin {
138 |
139 | @Override
140 | public void sayHello() {
141 | super.sayHello();
142 | say("I'm your boss");
143 | }
144 | }
145 |
146 | public static class Paranoid extends PersonPlugin {
147 |
148 | @Override
149 | public void sayHello() {
150 | say("Is somebody listening?");
151 | super.sayHello();
152 | }
153 | }
154 |
155 | public static class Good extends PersonPlugin {
156 |
157 | @Override
158 | public void sayHello() {
159 | super.sayHello();
160 | say("How are you?");
161 | }
162 | }
163 |
164 | public static class Surprise extends PersonPlugin {
165 |
166 | @Override
167 | public void sayHello() {
168 | say("Surprise!");
169 | super.sayHello();
170 | }
171 | }
172 |
173 | public interface PluginMethodAction {
174 |
175 | void call(PersonPlugin plugin, Object... args);
176 | }
177 |
178 | @Test
179 | public void testCallMethodFromSuper() throws Exception {
180 |
181 | final CompositePerson bossPerson = new CompositePerson();
182 | final Boss boss = new Boss();
183 | bossPerson.addPlugins(boss);
184 | try {
185 | boss.sayHello();
186 | fail("Exception excepted");
187 | } catch (IllegalStateException e) {
188 | assertThat(e).hasMessageContaining("order");
189 | }
190 | bossPerson.sayHello();
191 | assertThat(bossPerson.said)
192 | .isEqualTo(Arrays.asList("Hello, I'm John", "I'm your boss"));
193 |
194 | final CompositePerson paranoidPerson = new CompositePerson();
195 | final Paranoid paranoid = new Paranoid();
196 | paranoidPerson.addPlugins(paranoid);
197 | try {
198 | paranoid.sayHello();
199 | fail("Exception excepted");
200 | } catch (IllegalStateException e) {
201 | assertThat(e).hasMessageContaining("order");
202 | }
203 | paranoidPerson.said.clear();
204 | paranoidPerson.sayHello();
205 | assertThat(paranoidPerson.said)
206 | .isEqualTo(Arrays.asList("Is somebody listening?", "Hello, I'm John"));
207 |
208 | final CompositePerson paranoidBoss = new CompositePerson();
209 | paranoidBoss.addPlugins(paranoid);
210 | paranoidBoss.addPlugins(boss);
211 | try {
212 | paranoid.sayHello();
213 | fail("Exception excepted");
214 | } catch (IllegalStateException e) {
215 | assertThat(e).hasMessageContaining("order");
216 | }
217 | paranoidBoss.said.clear();
218 | paranoidBoss.sayHello();
219 | assertThat(paranoidBoss.said)
220 | .isEqualTo(Arrays.asList("Is somebody listening?", "Hello, I'm John",
221 | "I'm your boss"));
222 | try {
223 | boss.sayHello();
224 | fail("Exception excepted");
225 | } catch (IllegalStateException e) {
226 | assertThat(e).hasMessageContaining("order");
227 | }
228 |
229 | final CompositePerson goodBoss = new CompositePerson();
230 | goodBoss.addPlugins(boss);
231 | final Good good = new Good();
232 | goodBoss.addPlugins(good);
233 |
234 | goodBoss.sayHello();
235 | assertThat(goodBoss.said)
236 | .isEqualTo(Arrays.asList("Hello, I'm John", "I'm your boss", "How are you?"));
237 | try {
238 | good.sayHello();
239 | fail("Exception excepted");
240 | } catch (IllegalStateException e) {
241 | assertThat(e).hasMessageContaining("order");
242 | }
243 |
244 | }
245 |
246 | @Test
247 | public void testMixinInheritance() throws Exception {
248 |
249 | final CompositePerson boss = new CompositePerson();
250 | boss.addPlugins(new Boss());
251 | boss.sayHello();
252 | assertThat(boss.said)
253 | .isEqualTo(Arrays.asList("Hello, I'm John", "I'm your boss"));
254 |
255 | final CompositePerson person = new CompositePerson();
256 | person.addPlugins(new Paranoid());
257 | person.sayHello();
258 | assertThat(person.said)
259 | .isEqualTo(Arrays.asList("Is somebody listening?", "Hello, I'm John"));
260 |
261 | final CompositePerson paranoidBoss = new CompositePerson();
262 | paranoidBoss.addPlugins(new Paranoid());
263 | paranoidBoss.addPlugins(new Boss());
264 | paranoidBoss.sayHello();
265 | assertThat(paranoidBoss.said)
266 | .isEqualTo(Arrays.asList("Is somebody listening?", "Hello, I'm John",
267 | "I'm your boss"));
268 |
269 | final CompositePerson goodBoss = new CompositePerson();
270 | goodBoss.addPlugins(new Boss());
271 | goodBoss.addPlugins(new Good());
272 | goodBoss.sayHello();
273 |
274 | assertThat(goodBoss.said)
275 | .isEqualTo(Arrays.asList("Hello, I'm John", "I'm your boss", "How are you?"));
276 | }
277 |
278 | @Test
279 | public void testThreePlugins() throws Exception {
280 | final CompositePerson person = new CompositePerson();
281 | final Paranoid paranoid = new Paranoid();
282 |
283 | person.addPlugins(new Boss());
284 | person.addPlugins(paranoid);
285 | person.addPlugins(new Surprise());
286 | person.sayHello();
287 | assertThat(person.said)
288 | .isEqualTo(Arrays.asList("Surprise!", "Is somebody listening?", "Hello, I'm John",
289 | "I'm your boss"));
290 |
291 | // TODO think about how this could be solved
292 | // this is the fun part. Real inheritance would call "Surprise!" first. this happens when
293 | // code gets executed before calling super and there is nothing I can do against.
294 | // at least nothing got called twice
295 | person.said.clear();
296 |
297 | try {
298 | paranoid.sayHello();
299 | fail("Exception excepted");
300 | } catch (IllegalStateException e) {
301 | assertThat(e).hasMessageContaining("order");
302 | }
303 | }
304 |
305 | }
306 |
--------------------------------------------------------------------------------
/core/src/test/java/com/pascalwelsch/compositeandroid/core/NormalInheritance.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.core;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.ArrayList;
6 | import java.util.Arrays;
7 | import java.util.List;
8 |
9 | import static org.assertj.core.api.Java6Assertions.assertThat;
10 |
11 | public class NormalInheritance {
12 |
13 | public static class Person {
14 |
15 | List said = new ArrayList<>();
16 |
17 | public void say(String s) {
18 | said.add(s);
19 | }
20 |
21 | public void sayHello() {
22 | say("Hello, I'm John");
23 | }
24 | }
25 |
26 | public static class Boss extends Person {
27 |
28 | public final void callSayHello() {
29 | sayHello();
30 | }
31 |
32 | @Override
33 | public void sayHello() {
34 | super.sayHello();
35 | say("I'm your boss");
36 | }
37 | }
38 |
39 | public static class ParanoidPerson extends Person {
40 |
41 | public final void callSayHello() {
42 | sayHello();
43 | }
44 |
45 | @Override
46 | public void sayHello() {
47 | say("Is somebody listening?");
48 | super.sayHello();
49 | }
50 | }
51 |
52 | public static class ParanoidBoss extends ParanoidPerson {
53 |
54 | @Override
55 | public void sayHello() {
56 | super.sayHello();
57 | say("I'm your boss");
58 | }
59 | }
60 |
61 | public static class GoodBoss extends Boss {
62 |
63 | @Override
64 | public void sayHello() {
65 | super.sayHello();
66 | say("How are you?");
67 | }
68 | }
69 |
70 | @Test
71 | public void testCallMethodFromSuper() throws Exception {
72 |
73 | final Boss boss = new Boss();
74 | boss.callSayHello();
75 | assertThat(boss.said)
76 | .isEqualTo(Arrays.asList("Hello, I'm John", "I'm your boss"));
77 |
78 | final ParanoidPerson person = new ParanoidPerson();
79 | person.callSayHello();
80 | assertThat(person.said)
81 | .isEqualTo(Arrays.asList("Is somebody listening?", "Hello, I'm John"));
82 |
83 | final ParanoidBoss paranoidBoss = new ParanoidBoss();
84 | paranoidBoss.callSayHello();
85 | assertThat(paranoidBoss.said)
86 | .isEqualTo(Arrays.asList("Is somebody listening?", "Hello, I'm John",
87 | "I'm your boss"));
88 |
89 | final GoodBoss goodBoss = new GoodBoss();
90 | goodBoss.callSayHello();
91 |
92 | assertThat(goodBoss.said)
93 | .isEqualTo(Arrays.asList("Hello, I'm John", "I'm your boss", "How are you?"));
94 | }
95 |
96 | @Test
97 | public void testNormalInheritance() throws Exception {
98 |
99 | final Boss boss = new Boss();
100 | boss.sayHello();
101 | assertThat(boss.said)
102 | .isEqualTo(Arrays.asList("Hello, I'm John", "I'm your boss"));
103 |
104 | final ParanoidPerson person = new ParanoidPerson();
105 | person.sayHello();
106 | assertThat(person.said)
107 | .isEqualTo(Arrays.asList("Is somebody listening?", "Hello, I'm John"));
108 |
109 | final ParanoidBoss paranoidBoss = new ParanoidBoss();
110 | paranoidBoss.sayHello();
111 | assertThat(paranoidBoss.said)
112 | .isEqualTo(Arrays.asList("Is somebody listening?", "Hello, I'm John",
113 | "I'm your boss"));
114 |
115 | final GoodBoss goodBoss = new GoodBoss();
116 | goodBoss.sayHello();
117 |
118 | assertThat(goodBoss.said)
119 | .isEqualTo(Arrays.asList("Hello, I'm John", "I'm your boss", "How are you?"));
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/fragment/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/fragment/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | jcenter()
4 | google()
5 | }
6 | dependencies {
7 | classpath 'com.novoda:bintray-release:0.8.1'
8 | }
9 | }
10 |
11 |
12 | apply plugin: 'com.android.library'
13 | apply plugin: 'com.novoda.bintray-release'
14 |
15 | android {
16 | compileSdkVersion COMPILE_SDK_VERSION
17 |
18 | defaultConfig {
19 | minSdkVersion MIN_SDK_VERSION
20 | targetSdkVersion TARGET_SDK_VERSION
21 | versionCode VERSION_CODE
22 | versionName VERSION_NAME
23 | }
24 | buildTypes {
25 | release {
26 | minifyEnabled false
27 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
28 | }
29 | }
30 |
31 | dexOptions {
32 | preDexLibraries = !ciBuild
33 | javaMaxHeapSize "2g"
34 | }
35 |
36 | lintOptions {
37 | abortOnError false
38 | }
39 |
40 | testOptions {
41 | unitTests.returnDefaultValues = true
42 | }
43 | }
44 |
45 | dependencies {
46 | compile project(':core')
47 | testCompile 'junit:junit:4.12'
48 | compile "com.android.support:support-fragment:$supportLibraryVersion"
49 |
50 | testCompile 'org.mockito:mockito-core:1.10.19'
51 | testCompile 'org.assertj:assertj-core:2.4.1'
52 | }
53 |
54 | // usage:
55 | // ./gradlew clean build bintrayUpload -PbintrayUser=BINTRAY_USERNAME -PbintrayKey=BINTRAY_KEY -PdryRun=true
56 | publish {
57 | userOrg = 'passsy'
58 | groupId = 'com.pascalwelsch.compositeandroid'
59 | artifactId = 'fragment'
60 | uploadName = 'CompositeActivity'
61 | publishVersion = VERSION_NAME
62 | desc = 'CompositeAndroid - Composition over inheritance for Android'
63 | website = 'https://github.com/passsy/CompositeAndroid'
64 | }
--------------------------------------------------------------------------------
/fragment/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.4.1_1/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/fragment/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/fragment/src/main/java/com/pascalwelsch/compositeandroid/fragment/DialogFragmentPlugin.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.fragment;
2 |
3 | import android.app.Dialog;
4 | import android.content.Context;
5 | import android.content.DialogInterface;
6 | import android.os.Bundle;
7 | import android.support.annotation.NonNull;
8 | import android.support.annotation.Nullable;
9 | import android.support.v4.app.DialogFragment;
10 | import android.support.v4.app.FragmentManager;
11 | import android.support.v4.app.FragmentTransaction;
12 | import android.view.LayoutInflater;
13 | import com.pascalwelsch.compositeandroid.core.CallFun0;
14 | import com.pascalwelsch.compositeandroid.core.CallFun1;
15 | import com.pascalwelsch.compositeandroid.core.CallFun2;
16 | import com.pascalwelsch.compositeandroid.core.CallVoid0;
17 | import com.pascalwelsch.compositeandroid.core.CallVoid1;
18 | import com.pascalwelsch.compositeandroid.core.CallVoid2;
19 |
20 |
21 | /**
22 | * This code was auto-generated by the CompositeAndroid
23 | * generator
24 | *
25 | * @author Pascal Welsch
26 | */
27 | @SuppressWarnings("unused")
28 | public class DialogFragmentPlugin extends FragmentPlugin {
29 |
30 |
31 | public void onAttach(Context context) {
32 | verifyMethodCalledFromDelegate("onAttach(Context)");
33 | ((CallVoid1) mSuperListeners.pop()).call(context);
34 | }
35 |
36 | void onAttach(final CallVoid1 superCall, Context context) {
37 | synchronized (mSuperListeners) {
38 | mSuperListeners.push(superCall);
39 | onAttach(context);
40 | }
41 | }
42 |
43 |
44 | public void onCreate(@Nullable Bundle savedInstanceState) {
45 | verifyMethodCalledFromDelegate("onCreate(Bundle)");
46 | ((CallVoid1) mSuperListeners.pop()).call(savedInstanceState);
47 | }
48 |
49 | void onCreate(final CallVoid1 superCall, @Nullable Bundle savedInstanceState) {
50 | synchronized (mSuperListeners) {
51 | mSuperListeners.push(superCall);
52 | onCreate(savedInstanceState);
53 | }
54 | }
55 |
56 |
57 | public void onActivityCreated(@Nullable Bundle savedInstanceState) {
58 | verifyMethodCalledFromDelegate("onActivityCreated(Bundle)");
59 | ((CallVoid1) mSuperListeners.pop()).call(savedInstanceState);
60 | }
61 |
62 | void onActivityCreated(final CallVoid1 superCall, @Nullable Bundle savedInstanceState) {
63 | synchronized (mSuperListeners) {
64 | mSuperListeners.push(superCall);
65 | onActivityCreated(savedInstanceState);
66 | }
67 | }
68 |
69 |
70 | public void onStart() {
71 | verifyMethodCalledFromDelegate("onStart()");
72 | ((CallVoid0) mSuperListeners.pop()).call();
73 | }
74 |
75 | void onStart(final CallVoid0 superCall) {
76 | synchronized (mSuperListeners) {
77 | mSuperListeners.push(superCall);
78 | onStart();
79 | }
80 | }
81 |
82 |
83 | public void onStop() {
84 | verifyMethodCalledFromDelegate("onStop()");
85 | ((CallVoid0) mSuperListeners.pop()).call();
86 | }
87 |
88 | void onStop(final CallVoid0 superCall) {
89 | synchronized (mSuperListeners) {
90 | mSuperListeners.push(superCall);
91 | onStop();
92 | }
93 | }
94 |
95 |
96 | public void onSaveInstanceState(@NonNull Bundle outState) {
97 | verifyMethodCalledFromDelegate("onSaveInstanceState(Bundle)");
98 | ((CallVoid1) mSuperListeners.pop()).call(outState);
99 | }
100 |
101 | void onSaveInstanceState(final CallVoid1 superCall, @NonNull Bundle outState) {
102 | synchronized (mSuperListeners) {
103 | mSuperListeners.push(superCall);
104 | onSaveInstanceState(outState);
105 | }
106 | }
107 |
108 |
109 | public void onDestroyView() {
110 | verifyMethodCalledFromDelegate("onDestroyView()");
111 | ((CallVoid0) mSuperListeners.pop()).call();
112 | }
113 |
114 | void onDestroyView(final CallVoid0 superCall) {
115 | synchronized (mSuperListeners) {
116 | mSuperListeners.push(superCall);
117 | onDestroyView();
118 | }
119 | }
120 |
121 |
122 | public void onDetach() {
123 | verifyMethodCalledFromDelegate("onDetach()");
124 | ((CallVoid0) mSuperListeners.pop()).call();
125 | }
126 |
127 | void onDetach(final CallVoid0 superCall) {
128 | synchronized (mSuperListeners) {
129 | mSuperListeners.push(superCall);
130 | onDetach();
131 | }
132 | }
133 |
134 |
135 | public void dismiss() {
136 | verifyMethodCalledFromDelegate("dismiss()");
137 | ((CallVoid0) mSuperListeners.pop()).call();
138 | }
139 |
140 | public void dismissAllowingStateLoss() {
141 | verifyMethodCalledFromDelegate("dismissAllowingStateLoss()");
142 | ((CallVoid0) mSuperListeners.pop()).call();
143 | }
144 |
145 | public Dialog getDialog() {
146 | verifyMethodCalledFromDelegate("getDialog()");
147 | return ((CallFun0) mSuperListeners.pop()).call();
148 | }
149 |
150 | public DialogFragment getDialogFragment() {
151 | return (DialogFragment) getOriginal();
152 | }
153 |
154 | public boolean getShowsDialog() {
155 | verifyMethodCalledFromDelegate("getShowsDialog()");
156 | return ((CallFun0) mSuperListeners.pop()).call();
157 | }
158 |
159 | public void setShowsDialog(boolean showsDialog) {
160 | verifyMethodCalledFromDelegate("setShowsDialog(Boolean)");
161 | ((CallVoid1) mSuperListeners.pop()).call(showsDialog);
162 | }
163 |
164 | public int getTheme() {
165 | verifyMethodCalledFromDelegate("getTheme()");
166 | return ((CallFun0) mSuperListeners.pop()).call();
167 | }
168 |
169 | public boolean isCancelable() {
170 | verifyMethodCalledFromDelegate("isCancelable()");
171 | return ((CallFun0) mSuperListeners.pop()).call();
172 | }
173 |
174 | public void setCancelable(boolean cancelable) {
175 | verifyMethodCalledFromDelegate("setCancelable(Boolean)");
176 | ((CallVoid1) mSuperListeners.pop()).call(cancelable);
177 | }
178 |
179 | public void onCancel(DialogInterface dialog) {
180 | verifyMethodCalledFromDelegate("onCancel(DialogInterface)");
181 | ((CallVoid1) mSuperListeners.pop()).call(dialog);
182 | }
183 |
184 | public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
185 | verifyMethodCalledFromDelegate("onCreateDialog(Bundle)");
186 | return ((CallFun1) mSuperListeners.pop()).call(savedInstanceState);
187 | }
188 |
189 | public void onDismiss(DialogInterface dialog) {
190 | verifyMethodCalledFromDelegate("onDismiss(DialogInterface)");
191 | ((CallVoid1) mSuperListeners.pop()).call(dialog);
192 | }
193 |
194 | public LayoutInflater onGetLayoutInflater(@Nullable Bundle savedInstanceState) {
195 | verifyMethodCalledFromDelegate("onGetLayoutInflater(Bundle)");
196 | return ((CallFun1) mSuperListeners.pop()).call(savedInstanceState);
197 | }
198 |
199 | public void setStyle(int style, int theme) {
200 | verifyMethodCalledFromDelegate("setStyle(Integer, Integer)");
201 | ((CallVoid2) mSuperListeners.pop()).call(style, theme);
202 | }
203 |
204 | public void setupDialog(Dialog dialog, int style) {
205 | verifyMethodCalledFromDelegate("setupDialog(Dialog, Integer)");
206 | ((CallVoid2) mSuperListeners.pop()).call(dialog, style);
207 | }
208 |
209 | public void show(FragmentManager manager, String tag) {
210 | verifyMethodCalledFromDelegate("show(FragmentManager, String)");
211 | ((CallVoid2) mSuperListeners.pop()).call(manager, tag);
212 | }
213 |
214 | public int show(FragmentTransaction transaction, String tag) {
215 | verifyMethodCalledFromDelegate("show(FragmentTransaction, String)");
216 | return ((CallFun2) mSuperListeners.pop()).call(transaction, tag);
217 | }
218 |
219 | public void showNow(FragmentManager manager, String tag) {
220 | verifyMethodCalledFromDelegate("showNow(FragmentManager, String)");
221 | ((CallVoid2) mSuperListeners.pop()).call(manager, tag);
222 | }
223 |
224 | void dismiss(final CallVoid0 superCall) {
225 | synchronized (mSuperListeners) {
226 | mSuperListeners.push(superCall);
227 | dismiss();
228 | }
229 | }
230 |
231 | void dismissAllowingStateLoss(final CallVoid0 superCall) {
232 | synchronized (mSuperListeners) {
233 | mSuperListeners.push(superCall);
234 | dismissAllowingStateLoss();
235 | }
236 | }
237 |
238 | Dialog getDialog(final CallFun0 superCall) {
239 | synchronized (mSuperListeners) {
240 | mSuperListeners.push(superCall);
241 | return getDialog();
242 | }
243 | }
244 |
245 | boolean getShowsDialog(final CallFun0 superCall) {
246 | synchronized (mSuperListeners) {
247 | mSuperListeners.push(superCall);
248 | return getShowsDialog();
249 | }
250 | }
251 |
252 | int getTheme(final CallFun0 superCall) {
253 | synchronized (mSuperListeners) {
254 | mSuperListeners.push(superCall);
255 | return getTheme();
256 | }
257 | }
258 |
259 | boolean isCancelable(final CallFun0 superCall) {
260 | synchronized (mSuperListeners) {
261 | mSuperListeners.push(superCall);
262 | return isCancelable();
263 | }
264 | }
265 |
266 | void onCancel(final CallVoid1 superCall, DialogInterface dialog) {
267 | synchronized (mSuperListeners) {
268 | mSuperListeners.push(superCall);
269 | onCancel(dialog);
270 | }
271 | }
272 |
273 | Dialog onCreateDialog(final CallFun1 superCall, @Nullable Bundle savedInstanceState) {
274 | synchronized (mSuperListeners) {
275 | mSuperListeners.push(superCall);
276 | return onCreateDialog(savedInstanceState);
277 | }
278 | }
279 |
280 | void onDismiss(final CallVoid1 superCall, DialogInterface dialog) {
281 | synchronized (mSuperListeners) {
282 | mSuperListeners.push(superCall);
283 | onDismiss(dialog);
284 | }
285 | }
286 |
287 | LayoutInflater onGetLayoutInflater(final CallFun1 superCall,
288 | @Nullable Bundle savedInstanceState) {
289 | synchronized (mSuperListeners) {
290 | mSuperListeners.push(superCall);
291 | return onGetLayoutInflater(savedInstanceState);
292 | }
293 | }
294 |
295 | void setCancelable(final CallVoid1 superCall, boolean cancelable) {
296 | synchronized (mSuperListeners) {
297 | mSuperListeners.push(superCall);
298 | setCancelable(cancelable);
299 | }
300 | }
301 |
302 | void setShowsDialog(final CallVoid1 superCall, boolean showsDialog) {
303 | synchronized (mSuperListeners) {
304 | mSuperListeners.push(superCall);
305 | setShowsDialog(showsDialog);
306 | }
307 | }
308 |
309 | void setStyle(final CallVoid2 superCall, int style, int theme) {
310 | synchronized (mSuperListeners) {
311 | mSuperListeners.push(superCall);
312 | setStyle(style, theme);
313 | }
314 | }
315 |
316 | void setupDialog(final CallVoid2 superCall, Dialog dialog, int style) {
317 | synchronized (mSuperListeners) {
318 | mSuperListeners.push(superCall);
319 | setupDialog(dialog, style);
320 | }
321 | }
322 |
323 | void show(final CallVoid2 superCall, FragmentManager manager, String tag) {
324 | synchronized (mSuperListeners) {
325 | mSuperListeners.push(superCall);
326 | show(manager, tag);
327 | }
328 | }
329 |
330 | int show(final CallFun2 superCall, FragmentTransaction transaction,
331 | String tag) {
332 | synchronized (mSuperListeners) {
333 | mSuperListeners.push(superCall);
334 | return show(transaction, tag);
335 | }
336 | }
337 |
338 | void showNow(final CallVoid2 superCall, FragmentManager manager, String tag) {
339 | synchronized (mSuperListeners) {
340 | mSuperListeners.push(superCall);
341 | showNow(manager, tag);
342 | }
343 | }
344 |
345 |
346 | }
--------------------------------------------------------------------------------
/fragment/src/main/java/com/pascalwelsch/compositeandroid/fragment/ICompositeDialogFragment.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.fragment;
2 |
3 | import android.app.Dialog;
4 | import android.content.Context;
5 | import android.content.DialogInterface;
6 | import android.os.Bundle;
7 | import android.support.annotation.NonNull;
8 | import android.support.annotation.Nullable;
9 | import android.support.v4.app.FragmentManager;
10 | import android.support.v4.app.FragmentTransaction;
11 | import android.view.LayoutInflater;
12 |
13 |
14 | /**
15 | * This code was auto-generated by the CompositeAndroid
16 | * generator
17 | *
18 | * @author Pascal Welsch
19 | */
20 | public interface ICompositeDialogFragment extends ICompositeFragment {
21 |
22 |
23 | void onAttach(Context context);
24 |
25 | void onCreate(@Nullable Bundle savedInstanceState);
26 |
27 | void onActivityCreated(@Nullable Bundle savedInstanceState);
28 |
29 | void onStart();
30 |
31 | void onStop();
32 |
33 | void onSaveInstanceState(@NonNull Bundle outState);
34 |
35 | void onDestroyView();
36 |
37 | void onDetach();
38 |
39 | void dismiss();
40 |
41 | void dismissAllowingStateLoss();
42 |
43 | Dialog getDialog();
44 |
45 | boolean getShowsDialog();
46 |
47 | void setShowsDialog(boolean showsDialog);
48 |
49 | int getTheme();
50 |
51 | boolean isCancelable();
52 |
53 | void setCancelable(boolean cancelable);
54 |
55 | void onCancel(DialogInterface dialog);
56 |
57 | Dialog onCreateDialog(@Nullable Bundle savedInstanceState);
58 |
59 | void onDismiss(DialogInterface dialog);
60 |
61 | LayoutInflater onGetLayoutInflater(@Nullable Bundle savedInstanceState);
62 |
63 | void setStyle(int style, int theme);
64 |
65 | void setupDialog(Dialog dialog, int style);
66 |
67 | void show(FragmentManager manager, String tag);
68 |
69 | int show(FragmentTransaction transaction, String tag);
70 |
71 | void showNow(FragmentManager manager, String tag);
72 |
73 | void super_dismiss();
74 |
75 | void super_dismissAllowingStateLoss();
76 |
77 | Dialog super_getDialog();
78 |
79 | boolean super_getShowsDialog();
80 |
81 | int super_getTheme();
82 |
83 | boolean super_isCancelable();
84 |
85 | void super_onActivityCreated(@Nullable Bundle savedInstanceState);
86 |
87 | void super_onAttach(Context context);
88 |
89 | void super_onCancel(DialogInterface dialog);
90 |
91 | void super_onCreate(@Nullable Bundle savedInstanceState);
92 |
93 | Dialog super_onCreateDialog(@Nullable Bundle savedInstanceState);
94 |
95 | void super_onDestroyView();
96 |
97 | void super_onDetach();
98 |
99 | void super_onDismiss(DialogInterface dialog);
100 |
101 | LayoutInflater super_onGetLayoutInflater(@Nullable Bundle savedInstanceState);
102 |
103 | void super_onSaveInstanceState(@NonNull Bundle outState);
104 |
105 | void super_onStart();
106 |
107 | void super_onStop();
108 |
109 | void super_setCancelable(boolean cancelable);
110 |
111 | void super_setShowsDialog(boolean showsDialog);
112 |
113 | void super_setStyle(int style, int theme);
114 |
115 | void super_setupDialog(Dialog dialog, int style);
116 |
117 | void super_show(FragmentManager manager, String tag);
118 |
119 | int super_show(FragmentTransaction transaction, String tag);
120 |
121 | void super_showNow(FragmentManager manager, String tag);
122 | }
123 |
--------------------------------------------------------------------------------
/fragment/src/main/java/com/pascalwelsch/compositeandroid/fragment/ICompositeFragment.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.fragment;
2 |
3 | import android.animation.Animator;
4 | import android.app.Activity;
5 | import android.arch.lifecycle.LiveData;
6 | import android.arch.lifecycle.ViewModelStore;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.content.IntentSender;
10 | import android.content.IntentSender.SendIntentException;
11 | import android.content.res.Configuration;
12 | import android.os.Bundle;
13 | import android.support.annotation.NonNull;
14 | import android.support.annotation.Nullable;
15 | import android.support.v4.app.Fragment;
16 | import android.support.v4.app.LoaderManager;
17 | import android.support.v4.app.SharedElementCallback;
18 | import android.util.AttributeSet;
19 | import android.view.ContextMenu;
20 | import android.view.ContextMenu.ContextMenuInfo;
21 | import android.view.LayoutInflater;
22 | import android.view.Menu;
23 | import android.view.MenuInflater;
24 | import android.view.MenuItem;
25 | import android.view.View;
26 | import android.view.ViewGroup;
27 | import android.view.animation.Animation;
28 | import java.io.FileDescriptor;
29 | import java.io.PrintWriter;
30 |
31 | /**
32 | * This code was auto-generated by the CompositeAndroid
33 | * generator
34 | *
35 | * @author Pascal Welsch
36 | */
37 | public interface ICompositeFragment {
38 |
39 |
40 | void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args);
41 |
42 | boolean getAllowEnterTransitionOverlap();
43 |
44 | void setAllowEnterTransitionOverlap(boolean allow);
45 |
46 | boolean getAllowReturnTransitionOverlap();
47 |
48 | void setAllowReturnTransitionOverlap(boolean allow);
49 |
50 | Context getContext();
51 |
52 | Object getEnterTransition();
53 |
54 | void setEnterTransition(@Nullable Object transition);
55 |
56 | Object getExitTransition();
57 |
58 | void setExitTransition(@Nullable Object transition);
59 |
60 | LayoutInflater getLayoutInflater(@Nullable Bundle savedFragmentState);
61 |
62 | android.arch.lifecycle.Lifecycle getLifecycle();
63 |
64 | LoaderManager getLoaderManager();
65 |
66 | Object getReenterTransition();
67 |
68 | void setReenterTransition(@Nullable Object transition);
69 |
70 | Object getReturnTransition();
71 |
72 | void setReturnTransition(@Nullable Object transition);
73 |
74 | Object getSharedElementEnterTransition();
75 |
76 | void setSharedElementEnterTransition(@Nullable Object transition);
77 |
78 | Object getSharedElementReturnTransition();
79 |
80 | void setSharedElementReturnTransition(@Nullable Object transition);
81 |
82 | boolean getUserVisibleHint();
83 |
84 | void setUserVisibleHint(boolean isVisibleToUser);
85 |
86 | View getView();
87 |
88 | android.arch.lifecycle.LifecycleOwner getViewLifecycleOwner();
89 |
90 | LiveData getViewLifecycleOwnerLiveData();
91 |
92 | ViewModelStore getViewModelStore();
93 |
94 | void onActivityCreated(@Nullable Bundle savedInstanceState);
95 |
96 | void onActivityResult(int requestCode, int resultCode, Intent data);
97 |
98 | void onAttach(Context context);
99 |
100 | void onAttach(Activity activity);
101 |
102 | void onAttachFragment(Fragment childFragment);
103 |
104 | void onConfigurationChanged(Configuration newConfig);
105 |
106 | boolean onContextItemSelected(MenuItem item);
107 |
108 | void onCreate(@Nullable Bundle savedInstanceState);
109 |
110 | Animation onCreateAnimation(int transit, boolean enter, int nextAnim);
111 |
112 | Animator onCreateAnimator(int transit, boolean enter, int nextAnim);
113 |
114 | void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo);
115 |
116 | void onCreateOptionsMenu(Menu menu, MenuInflater inflater);
117 |
118 | View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
119 | @Nullable Bundle savedInstanceState);
120 |
121 | void onDestroy();
122 |
123 | void onDestroyOptionsMenu();
124 |
125 | void onDestroyView();
126 |
127 | void onDetach();
128 |
129 | LayoutInflater onGetLayoutInflater(@Nullable Bundle savedInstanceState);
130 |
131 | void onHiddenChanged(boolean hidden);
132 |
133 | void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState);
134 |
135 | void onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState);
136 |
137 | void onLowMemory();
138 |
139 | void onMultiWindowModeChanged(boolean isInMultiWindowMode);
140 |
141 | boolean onOptionsItemSelected(MenuItem item);
142 |
143 | void onOptionsMenuClosed(Menu menu);
144 |
145 | void onPause();
146 |
147 | void onPictureInPictureModeChanged(boolean isInPictureInPictureMode);
148 |
149 | void onPrepareOptionsMenu(Menu menu);
150 |
151 | void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults);
152 |
153 | void onResume();
154 |
155 | void onSaveInstanceState(@NonNull Bundle outState);
156 |
157 | void onStart();
158 |
159 | void onStop();
160 |
161 | void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState);
162 |
163 | void onViewStateRestored(@Nullable Bundle savedInstanceState);
164 |
165 | void postponeEnterTransition();
166 |
167 | void registerForContextMenu(View view);
168 |
169 | void setArguments(@Nullable Bundle args);
170 |
171 | void setEnterSharedElementCallback(SharedElementCallback callback);
172 |
173 | void setExitSharedElementCallback(SharedElementCallback callback);
174 |
175 | void setHasOptionsMenu(boolean hasMenu);
176 |
177 | void setInitialSavedState(@Nullable Fragment.SavedState state);
178 |
179 | void setMenuVisibility(boolean menuVisible);
180 |
181 | void setRetainInstance(boolean retain);
182 |
183 | void setTargetFragment(@Nullable Fragment fragment, int requestCode);
184 |
185 | boolean shouldShowRequestPermissionRationale(@NonNull String permission);
186 |
187 | void startActivity(Intent intent);
188 |
189 | void startActivity(Intent intent, @Nullable Bundle options);
190 |
191 | void startActivityForResult(Intent intent, int requestCode);
192 |
193 | void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options);
194 |
195 | void startIntentSenderForResult(IntentSender intent, int requestCode, @Nullable Intent fillInIntent,
196 | int flagsMask, int flagsValues, int extraFlags, Bundle options) throws SendIntentException;
197 |
198 | void startPostponedEnterTransition();
199 |
200 | void super_dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args);
201 |
202 | boolean super_getAllowEnterTransitionOverlap();
203 |
204 | boolean super_getAllowReturnTransitionOverlap();
205 |
206 | Context super_getContext();
207 |
208 | Object super_getEnterTransition();
209 |
210 | Object super_getExitTransition();
211 |
212 | LayoutInflater super_getLayoutInflater(@Nullable Bundle savedFragmentState);
213 |
214 | android.arch.lifecycle.Lifecycle super_getLifecycle();
215 |
216 | LoaderManager super_getLoaderManager();
217 |
218 | Object super_getReenterTransition();
219 |
220 | Object super_getReturnTransition();
221 |
222 | Object super_getSharedElementEnterTransition();
223 |
224 | Object super_getSharedElementReturnTransition();
225 |
226 | boolean super_getUserVisibleHint();
227 |
228 | View super_getView();
229 |
230 | android.arch.lifecycle.LifecycleOwner super_getViewLifecycleOwner();
231 |
232 | LiveData super_getViewLifecycleOwnerLiveData();
233 |
234 | ViewModelStore super_getViewModelStore();
235 |
236 | void super_onActivityCreated(@Nullable Bundle savedInstanceState);
237 |
238 | void super_onActivityResult(int requestCode, int resultCode, Intent data);
239 |
240 | void super_onAttach(Context context);
241 |
242 | void super_onAttach(Activity activity);
243 |
244 | void super_onAttachFragment(Fragment childFragment);
245 |
246 | void super_onConfigurationChanged(Configuration newConfig);
247 |
248 | boolean super_onContextItemSelected(MenuItem item);
249 |
250 | void super_onCreate(@Nullable Bundle savedInstanceState);
251 |
252 | Animation super_onCreateAnimation(int transit, boolean enter, int nextAnim);
253 |
254 | Animator super_onCreateAnimator(int transit, boolean enter, int nextAnim);
255 |
256 | void super_onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo);
257 |
258 | void super_onCreateOptionsMenu(Menu menu, MenuInflater inflater);
259 |
260 | View super_onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
261 | @Nullable Bundle savedInstanceState);
262 |
263 | void super_onDestroy();
264 |
265 | void super_onDestroyOptionsMenu();
266 |
267 | void super_onDestroyView();
268 |
269 | void super_onDetach();
270 |
271 | LayoutInflater super_onGetLayoutInflater(@Nullable Bundle savedInstanceState);
272 |
273 | void super_onHiddenChanged(boolean hidden);
274 |
275 | void super_onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState);
276 |
277 | void super_onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState);
278 |
279 | void super_onLowMemory();
280 |
281 | void super_onMultiWindowModeChanged(boolean isInMultiWindowMode);
282 |
283 | boolean super_onOptionsItemSelected(MenuItem item);
284 |
285 | void super_onOptionsMenuClosed(Menu menu);
286 |
287 | void super_onPause();
288 |
289 | void super_onPictureInPictureModeChanged(boolean isInPictureInPictureMode);
290 |
291 | void super_onPrepareOptionsMenu(Menu menu);
292 |
293 | void super_onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
294 | @NonNull int[] grantResults);
295 |
296 | void super_onResume();
297 |
298 | void super_onSaveInstanceState(@NonNull Bundle outState);
299 |
300 | void super_onStart();
301 |
302 | void super_onStop();
303 |
304 | void super_onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState);
305 |
306 | void super_onViewStateRestored(@Nullable Bundle savedInstanceState);
307 |
308 | void super_postponeEnterTransition();
309 |
310 | void super_registerForContextMenu(View view);
311 |
312 | void super_setAllowEnterTransitionOverlap(boolean allow);
313 |
314 | void super_setAllowReturnTransitionOverlap(boolean allow);
315 |
316 | void super_setArguments(@Nullable Bundle args);
317 |
318 | void super_setEnterSharedElementCallback(SharedElementCallback callback);
319 |
320 | void super_setEnterTransition(@Nullable Object transition);
321 |
322 | void super_setExitSharedElementCallback(SharedElementCallback callback);
323 |
324 | void super_setExitTransition(@Nullable Object transition);
325 |
326 | void super_setHasOptionsMenu(boolean hasMenu);
327 |
328 | void super_setInitialSavedState(@Nullable Fragment.SavedState state);
329 |
330 | void super_setMenuVisibility(boolean menuVisible);
331 |
332 | void super_setReenterTransition(@Nullable Object transition);
333 |
334 | void super_setRetainInstance(boolean retain);
335 |
336 | void super_setReturnTransition(@Nullable Object transition);
337 |
338 | void super_setSharedElementEnterTransition(@Nullable Object transition);
339 |
340 | void super_setSharedElementReturnTransition(@Nullable Object transition);
341 |
342 | void super_setTargetFragment(@Nullable Fragment fragment, int requestCode);
343 |
344 | void super_setUserVisibleHint(boolean isVisibleToUser);
345 |
346 | boolean super_shouldShowRequestPermissionRationale(@NonNull String permission);
347 |
348 | void super_startActivity(Intent intent);
349 |
350 | void super_startActivity(Intent intent, @Nullable Bundle options);
351 |
352 | void super_startActivityForResult(Intent intent, int requestCode);
353 |
354 | void super_startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options);
355 |
356 | void super_startIntentSenderForResult(IntentSender intent, int requestCode, @Nullable Intent fillInIntent,
357 | int flagsMask, int flagsValues, int extraFlags, Bundle options) throws SendIntentException;
358 |
359 | void super_startPostponedEnterTransition();
360 |
361 | String super_toString();
362 |
363 | void super_unregisterForContextMenu(View view);
364 |
365 | String toString();
366 |
367 | void unregisterForContextMenu(View view);
368 | }
369 |
--------------------------------------------------------------------------------
/fragment/src/test/java/com/pascalwelsch/compositeandroid/fragment/FragmentPluginTest.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.fragment;
2 |
3 | import static org.assertj.core.api.Java6Assertions.*;
4 |
5 | import android.os.Bundle;
6 | import android.support.annotation.Nullable;
7 | import org.junit.*;
8 |
9 | public class FragmentPluginTest {
10 |
11 | public static class TestFragmentPlugin extends FragmentPlugin {
12 |
13 | private boolean mGotCalled;
14 |
15 | @Override
16 | public void onCreate(@Nullable final Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 | mGotCalled = true;
19 | }
20 | }
21 |
22 | public static class TestDialogFragmentPlugin extends DialogFragmentPlugin {
23 |
24 | private boolean mGotCalled;
25 |
26 | private boolean mShowsCalled;
27 |
28 | @Override
29 | public void onCreate(@Nullable final Bundle savedInstanceState) {
30 | super.onCreate(savedInstanceState);
31 | mGotCalled = true;
32 | }
33 |
34 | @Override
35 | public boolean getShowsDialog() {
36 | final boolean showsDialog = super.getShowsDialog();
37 | mShowsCalled = true;
38 | return showsDialog;
39 | }
40 | }
41 |
42 | public static class TestDialogFragment extends CompositeDialogFragment {
43 |
44 | private final TestDialogFragmentPlugin mDialogPlugin;
45 |
46 | private final TestFragmentPlugin mPlugin;
47 |
48 | public TestDialogFragment() {
49 | mPlugin = new TestFragmentPlugin();
50 | addPlugin(mPlugin);
51 |
52 | mDialogPlugin = new TestDialogFragmentPlugin();
53 | addPlugin(mDialogPlugin);
54 | }
55 | }
56 |
57 | public static class TestFragment extends CompositeFragment {
58 |
59 | private final TestFragmentPlugin mPlugin;
60 |
61 | public TestFragment() {
62 | mPlugin = new TestFragmentPlugin();
63 | addPlugin(mPlugin);
64 | }
65 | }
66 |
67 | @Test
68 | public void testCallBaseMethod() throws Exception {
69 | final TestFragment testFragment = new TestFragment();
70 | assertThat(testFragment.mPlugin.mGotCalled).isFalse();
71 | testFragment.onCreate(null);
72 | assertThat(testFragment.mPlugin.mGotCalled).isTrue();
73 |
74 | final TestDialogFragment testDialogFragment = new TestDialogFragment();
75 | assertThat(testDialogFragment.mPlugin.mGotCalled).isFalse();
76 | assertThat(testDialogFragment.mDialogPlugin.mGotCalled).isFalse();
77 | testDialogFragment.onCreate(null);
78 | assertThat(testDialogFragment.mPlugin.mGotCalled).isTrue();
79 | assertThat(testDialogFragment.mDialogPlugin.mGotCalled).isTrue();
80 | }
81 |
82 | @Test
83 | public void testCallDialogFragmentMethod() throws Exception {
84 |
85 | final TestDialogFragment testDialogFragment = new TestDialogFragment();
86 | assertThat(testDialogFragment.mDialogPlugin.mShowsCalled).isFalse();
87 | testDialogFragment.getShowsDialog();
88 | assertThat(testDialogFragment.mDialogPlugin.mShowsCalled).isTrue();
89 |
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/generator/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | .DS_Store
3 |
4 | # Built application files
5 | *.apk
6 | *.ap_
7 |
8 | # Files for the ART/Dalvik VM
9 | *.dex
10 |
11 | # Java class files
12 | *.class
13 |
14 | # Generated files
15 | bin/
16 | gen/
17 | out/
18 |
19 | # Gradle files
20 | .gradle/
21 | build/
22 |
23 | # Local configuration file (sdk path, etc)
24 | local.properties
25 |
26 | # Log Files
27 | *.log
28 |
29 | # Android Studio Navigation editor temp files
30 | .navigation/
31 |
32 | # Android Studio captures folder
33 | captures/
34 |
35 | # Intellij
36 | *.iml
37 | **/.idea/*
--------------------------------------------------------------------------------
/generator/README.md:
--------------------------------------------------------------------------------
1 | # Composite Generator
2 |
3 | Manually maintaining all methods of `AppCompatActivity` and the `SupportFragment` is impossible. Autogenerated code is the solution.
4 |
5 | This generator parses plain `.java` files.
6 | Even easier: It parses java files I generated with Android Studio.
7 | By extending the `Activity` and override all possible methods (keeping the JavaDoc) Android Studio generates a simple to parse file.
8 | This is by far the easiest solution. Using reflection doesn't work because reading the variable names and JavaDoc isn't possible.
9 |
10 | After generating the input files with this method the generator parses the `.java` file using RegEx.
11 | The RegEx for parsing methods of a java file isn't perfect but as long as it works it's fine.
12 | Once it breaks it can be adjusted.
13 | It doesn't parse the parts perfectly.
14 | I.e. the method visiblity doesn't get parsed or the method body can only be a single statement (which is the generated `super` call).
15 |
16 | Once parsed writing the sourcecode is easy.
17 | I considered using [javapoet](https://github.com/square/javapoet) but it didn't allow me to write plain text into a file.
18 | Parsing the content out of a JavaDoc to put it into the javapoet javadoc method was too hard.
19 | With this easy solution I copy the javadoc for every method as I read it in the input file.
20 |
21 | The output is readable and valid but not perfectly formatted.
22 | I reformat the code using Android Studio and everything is done.
23 | As the project grows the formatting takes longer and longer but the generator output is valid and it's possible to work with it while developing.
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/generator/build.gradle:
--------------------------------------------------------------------------------
1 | group 'com.pascalwelsch.compositeandroid'
2 | version '1.0-SNAPSHOT'
3 |
4 | buildscript {
5 | ext.kotlin_version = '1.2.50'
6 |
7 | repositories {
8 | mavenCentral()
9 | jcenter()
10 | }
11 | dependencies {
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 | }
14 | }
15 |
16 | apply plugin: 'kotlin'
17 |
18 | allprojects {
19 | repositories {
20 | mavenCentral()
21 | jcenter()
22 | }
23 | }
24 |
25 | dependencies {
26 | compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
27 | compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
28 | }
29 | repositories {
30 | mavenCentral()
31 | }
--------------------------------------------------------------------------------
/generator/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/passsy/CompositeAndroid/80dec3eed5a72a0f18e1dd880d646fa3af002d93/generator/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/generator/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Aug 16 12:46:19 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
7 |
--------------------------------------------------------------------------------
/generator/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn ( ) {
37 | echo "$*"
38 | }
39 |
40 | die ( ) {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
165 | if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then
166 | cd "$(dirname "$0")"
167 | fi
168 |
169 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
170 |
--------------------------------------------------------------------------------
/generator/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/generator/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'generator'
2 |
3 |
--------------------------------------------------------------------------------
/generator/src/main/java/Main.java:
--------------------------------------------------------------------------------
1 | public class Main {
2 |
3 | public static void main(String[] args) {
4 | System.out.println("Hello Java");
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/generator/src/main/kotlin/Generator.kt:
--------------------------------------------------------------------------------
1 | import types.generateActivity
2 | import types.generateFragments
3 |
4 | const val inPath = "../blueprints/src/main/java/com/pascalwelsch/compositeandroid/blueprints/"
5 |
6 | val autoGeneratedJavadocHeader = """
7 | /**
8 | * This code was auto-generated by the CompositeAndroid generator
9 | *
10 | * @author Pascal Welsch
11 | */
12 | """.trim()
13 |
14 | fun main(args: Array) {
15 | generateActivity()
16 | generateFragments()
17 | }
--------------------------------------------------------------------------------
/generator/src/main/kotlin/parse/AnalyzedJavaFile.kt:
--------------------------------------------------------------------------------
1 | package parse
2 |
3 | data class AnalyzedJavaFile(
4 | val imports: String,
5 | var methods: List
6 | )
7 |
8 | data class AnalyzedJavaMethod(
9 | val name: String,
10 | val visibility: String,
11 | val genericReturnType: String?,
12 | val returnType: String,
13 | val javadoc: String? = null,
14 | private val rawAnnotations: String? = null,
15 | val rawParameters: String?,
16 | val exceptions: String? = null) {
17 |
18 | val parameterTypes: List by lazy {
19 | val paramTypes = mutableListOf();
20 | if (rawParameters != null && !rawParameters.trim().isEmpty()) {
21 | rawParameters.split(',').forEach {
22 | it.trim()
23 | val split = it.split(' ')
24 | val type = split.elementAt(split.size - 2).trim()
25 | paramTypes.add(type.toBoxedType())
26 | }
27 | }
28 | paramTypes.toList()
29 | }
30 |
31 | val parameterNames: List by lazy {
32 | val paramNames = mutableListOf();
33 | if (rawParameters != null && !rawParameters.trim().isEmpty()) {
34 | rawParameters.split(',').forEach {
35 | it.trim()
36 | val split = it.split(' ')
37 | paramNames.add(split.last())
38 | }
39 | }
40 | paramNames.toList()
41 | }
42 |
43 | val annotations: List by lazy {
44 | rawAnnotations?.let {
45 | it.split("\n")
46 | .map { it.trim() }
47 | .filter { it.isNotEmpty() }
48 | } ?: emptyList()
49 | }
50 |
51 | val boxedReturnType: String by lazy {
52 | returnType.toBoxedType()
53 | }
54 |
55 | private fun String.toBoxedType(): String {
56 | return when (this) {
57 | "boolean" -> "Boolean"
58 | "int" -> "Integer"
59 | "void" -> "Void"
60 | // add others when needed
61 | else -> this
62 | }
63 | }
64 |
65 |
66 | val exceptionType: String? by lazy {
67 | var result: String? = null
68 | if (!exceptions.isNullOrBlank()) {
69 | result = exceptions!!.substringAfterLast("throws").trim().split(" ")[0]
70 | }
71 | result
72 | }
73 |
74 | val throws: Boolean = exceptionType != null
75 | val signature: String = "#$name($rawParameters):$returnType"
76 |
77 | val rawParametersBoxed: List by lazy {
78 | if (rawParameters != null && !rawParameters.trim().isEmpty()) {
79 | rawParameters.split(',').map {
80 | it.trim()
81 | val split = it.split(' ')
82 | val type = split.elementAt(split.size - 2).trim()
83 | val name = split.elementAt(split.size - 1).trim()
84 | "final ${type.toBoxedType()} $name"
85 | }.toList()
86 | } else {
87 | emptyList()
88 | }
89 | }
90 | }
--------------------------------------------------------------------------------
/generator/src/main/kotlin/parse/JavaFileParser.kt:
--------------------------------------------------------------------------------
1 | package parse
2 |
3 | import java.io.File
4 |
5 |
6 | fun parseJavaFile(file: File): AnalyzedJavaFile {
7 | val source = file.readText()
8 |
9 | val importMatches = "(?:import.*;)"
10 | .toRegex(RegexOption.MULTILINE)
11 | .findAll(source)
12 |
13 | val builder = StringBuilder();
14 | for (match in importMatches.iterator()) {
15 | val groups = match.groups
16 | if (groups.size > 0) {
17 | builder.append(groups[0]?.value?.trim())
18 | builder.append("\n")
19 | }
20 | }
21 | val imports = builder.toString()
22 |
23 |
24 | // findAll runs out of memory
25 | val methodMatches = mutableListOf()
26 | var pos = 0
27 | while (true) {
28 | // for regex101 https://regex101.com/r/D7gTv3/2
29 | // (?:\n*?((?:\h*?\/\*\*\h*[\w@]*\n*){1}(?:\h*\*.*\n*)*?(?:\h*\*\/){1})\n)*((?:^\h*\@.*\n)*?)?\h*((?:\w* )*)(.* )?([\w\.<>]*(?:\[\])?) (\w*)\(((?:\n*[^\)]*)*)\)([^\{]*)\{((?:.*\n)(?:\n*(?: .*\n))*)\ \}\n*
30 | val methodsMatch = "(?:\n*?((?:\\h*?\\/\\*\\*\\h*[\\w@]*\n*){1}(?:\\h*\\*.*\n*)*?(?:\\h*\\*\\/){1})\n)*((?:^\\h*\\@.*\n)*?)?\\h*((?:\\w* )*)(.* )?([\\w\\.<>]*(?:\\[\\])?) (\\w*)\\(((?:\n*[^\\)]*)*)\\)([^\\{]*)\\{((?:.*\n)(?:\n*(?: .*\n))*)\\ \\}\n*"
31 | .toRegex(RegexOption.MULTILINE)
32 | .find(source, pos)
33 |
34 | if (methodsMatch == null) {
35 | break
36 | }
37 | methodMatches.add(methodsMatch)
38 | pos = methodsMatch.range.last
39 | }
40 |
41 |
42 | val methods = mutableListOf()
43 |
44 | for (match in methodMatches) {
45 | val groups = match.groups
46 |
47 | val javadoc = groups[1]?.value
48 | val annotations = groups[2]?.value
49 | val visibility = groups[3]?.value ?: ""
50 | val genericTypeDefinition = groups[4]?.value ?: ""
51 | val returnType = groups[5]?.value!!
52 | val name = groups[6]?.value!!
53 | val parameters = groups[7]?.value?.replace("\n", " ")?.replace(",\\h+".toRegex(), ", ")
54 | val throws = groups[8]?.value?.replace("\n", " ")?.replace(",\\h+".toRegex(), ", ")?.trim()
55 |
56 | val paramNames = mutableListOf()
57 | val paramType = mutableListOf()
58 | if (parameters != null) {
59 | if (!parameters.trim().isEmpty()) {
60 | parameters.split(',').forEach {
61 | it.trim()
62 | val split = it.split(' ')
63 | paramNames.add(split.last())
64 | paramType.add(split.elementAt(split.size - 2))
65 | }
66 | }
67 | }
68 |
69 | methods.add(AnalyzedJavaMethod(name, visibility, genericTypeDefinition, returnType,
70 | javadoc, annotations, parameters, throws))
71 | }
72 |
73 |
74 | return AnalyzedJavaFile(imports, methods)
75 | }
76 |
--------------------------------------------------------------------------------
/generator/src/main/kotlin/types/Activity.kt:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import inPath
4 | import parse.parseJavaFile
5 | import writer.writeComposite
6 | import writer.writeDelegate
7 | import writer.writeInterface
8 | import writer.writePlugin
9 | import java.io.File
10 |
11 |
12 | private val outPath = "../activity/src/main/java/com/pascalwelsch/compositeandroid/"
13 |
14 | fun main(args: Array) {
15 | generateActivity()
16 | }
17 |
18 | fun generateActivity() {
19 | var activity = parseJavaFile(File("$inPath/BlueprintActivity.java"))
20 |
21 | // filter methods with special implementations
22 | val filteredMethods = activity.methods
23 | .filterNot { it.name.contains("NonConfigurationInstance") }
24 | activity = activity.copy(methods = filteredMethods)
25 |
26 | writeComposite(outPath,
27 | activity,
28 | "activity",
29 | "CompositeActivity",
30 | "AppCompatActivity implements ICompositeActivity",
31 | delegateClassName = "ActivityDelegate",
32 | pluginClassName = "ActivityPlugin",
33 | transform = replaceExtraData,
34 | additionalImports = """
35 | |import android.support.v4.app.SupportActivity;
36 | |import android.support.v7.app.ActionBarDrawerToggle;
37 | |import android.support.v7.app.ActionBarDrawerToggle.Delegate;
38 | |import android.support.v4.app.SupportActivity.ExtraData;
39 | """.replaceIndentByMargin(),
40 | addCodeToClass = activity_custom_nonConfigurationInstance_handling)
41 |
42 | writeDelegate(outPath,
43 | activity,
44 | "activity",
45 | "ActivityDelegate",
46 | "ICompositeActivity",
47 | "ActivityPlugin",
48 | extends = "AbstractDelegate",
49 | transform = replaceExtraData,
50 | additionalImports = """
51 | |import java.util.ListIterator;
52 | |import android.support.v4.app.SupportActivity;
53 | |import android.support.v7.app.ActionBarDrawerToggle;
54 | |import android.support.v7.app.ActionBarDrawerToggle.Delegate;
55 | |import android.support.v4.app.SupportActivity.ExtraData;
56 | """.replaceIndentByMargin(),
57 | addCodeToClass = delegate_custom_nonConfigurationInstance_handling)
58 |
59 | writePlugin(outPath,
60 | "Activity",
61 | activity,
62 | "activity",
63 | "ActivityPlugin",
64 | extends = "AbstractPlugin",
65 | transform = replaceExtraData,
66 | additionalImports = """
67 | |import android.support.v4.app.SupportActivity;
68 | |import android.support.v4.app.SupportActivity.ExtraData;
69 | """.replaceIndentByMargin(),
70 | addCodeToClass = plugin_custom_nonConfigurationInstance_handling)
71 |
72 | writeInterface(outPath,
73 | activity,
74 | "activity",
75 | "ICompositeActivity",
76 | "LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, View.OnCreateContextMenuListener, ComponentCallbacks2, ActivityCompat.OnRequestPermissionsResultCallback, AppCompatCallback, ActionBarDrawerToggle.DelegateProvider",
77 | addCodeToClass = interface_custom_nonConfigurationInstance_handling,
78 | transform = replaceExtraData,
79 | additionalImports = """
80 | |import android.content.*;
81 | |import android.support.v4.app.ActivityCompat;
82 | |import android.support.v4.app.Fragment;
83 | |import android.support.v4.app.FragmentManager;
84 | |import android.support.v4.app.LoaderManager;
85 | |import android.support.v4.app.SharedElementCallback;
86 | |import android.support.v4.app.SupportActivity;
87 | |import android.support.v7.app.*;
88 | |import android.support.v7.app.ActionBarDrawerToggle.Delegate;
89 | |import android.support.v4.app.SupportActivity.ExtraData;
90 | """.replaceIndentByMargin())
91 | }
92 | val replaceExtraData: (String) -> String = {
93 | it.replace(" ExtraData", " SupportActivity.ExtraData")
94 | .replace("(ExtraData)", "(SupportActivity.ExtraData)")
95 | .replace(") {
17 | generateFragments()
18 | }
19 |
20 | fun generateFragments() {
21 |
22 | val fragment = parseJavaFile(File("$inPath/BlueprintFragment.java"))
23 |
24 | generateFragment(fragment)
25 |
26 | generateDialogFragment(fragment)
27 | }
28 |
29 |
30 | val replaceAmbitiousTypesWithFullPackageNames: (String) -> String = {
31 | it.replace(" SavedState", " Fragment.SavedState")
32 | .replace("(SavedState)", "(Fragment.SavedState)")
33 | .replace(" String)? = null,
16 | superClassPluginNames: List = listOf(),
17 | superClassDelegateName: String = "",
18 | delegateClassName: String,
19 | pluginClassName: String,
20 | superClassInputFile: AnalyzedJavaFile? = null,
21 | addCodeToClass: String? = null) {
22 |
23 | val allPluginTypes = mutableListOf(pluginClassName)
24 | allPluginTypes.addAll(superClassPluginNames)
25 |
26 |
27 | fun addPlugins(): String {
28 | return allPluginTypes
29 | .distinct()
30 | .map {
31 | """
32 | |
33 | |public Removable addPlugin(final $it plugin) {
34 | | return delegate.addPlugin(plugin);
35 | |}
36 | |
37 | |public List add${it}s(@NonNull final $it... plugins) {
38 | | final List removables = new ArrayList<>(plugins.length);
39 | | for (final $it plugin : plugins) {
40 | | removables.add(delegate.addPlugin(plugin));
41 | | }
42 | | return removables;
43 | |}
44 | |
45 | |public List add${it}s(@NonNull final Iterable extends $it> plugins) {
46 | | final List removables = new ArrayList<>();
47 | | for (final $it plugin : plugins) {
48 | | removables.add(delegate.addPlugin(plugin));
49 | | }
50 | | return removables;
51 | |}
52 | """.replaceIndentByMargin(" ")
53 | }
54 | .joinToString("\n")
55 | }
56 |
57 | val sb = StringBuilder("""
58 | |package com.pascalwelsch.compositeandroid.$javaPackage;
59 | |
60 | |import android.annotation.SuppressLint;
61 | |
62 | |import com.pascalwelsch.compositeandroid.core.*;
63 | |
64 | |${javaFile.imports}
65 | |
66 | |$additionalImports
67 | |import java.util.*;
68 | |
69 | |@SuppressWarnings({"unused", "deprecation", "JavadocReference", "WrongConstant", "RestrictedApi"})
70 | |@SuppressLint({"MissingSuperCall", "NewApi"})
71 | |
72 | |$autoGeneratedJavadocHeader
73 | |public class $javaClassName extends $extends {
74 | |
75 | | protected $delegateClassName delegate = new $delegateClassName(this);
76 | |
77 | |${addPlugins()}
78 | |
79 | """.replaceIndentByMargin())
80 |
81 | val allMethods = mutableListOf()
82 | if (superClassInputFile != null) {
83 | allMethods.addAll(superClassInputFile.methods)
84 | }
85 | allMethods.addAll(javaFile.methods)
86 | val distinctMethods = allMethods.distinctBy { "${it.name} ${it.parameterTypes}" }.toMutableList()
87 |
88 | for (method in distinctMethods) with(method) {
89 | // override method, calling delegate
90 | sb.appendln(callDelegate())
91 |
92 | // super method
93 | sb.appendln(callSuper())
94 | }
95 |
96 | addCodeToClass?.let { sb.appendln(it) }
97 |
98 | sb.appendln("}")
99 |
100 | var output = sb.toString()
101 | if (transform != null) {
102 | output = transform(output)
103 | }
104 |
105 | val out = File("$outPath${javaPackage.replace('.', '/')}/$javaClassName.java")
106 | out.parentFile.mkdirs()
107 | out.printWriter().use { it.write(output) }
108 | System.out.println("wrote ${out.absolutePath}")
109 | }
110 |
111 | private fun AnalyzedJavaMethod.callSuper(): String {
112 |
113 | val returnWhenNotVoid = if (returnType != "void") "return " else ""
114 | val formattedAnnotations = annotations
115 | .filter { !it.contains("Override") }
116 | .joinToString("\n")
117 |
118 | return """
119 | |${javadoc?.trim() ?: ""}
120 | |$formattedAnnotations
121 | |@Override
122 | |public $genericReturnType$returnType super_$name($rawParameters) $exceptions{
123 | | ${returnWhenNotVoid}super.$name(${parameterNames.joinToString()});
124 | |}
125 | """.replaceIndentByMargin(" ")
126 | }
127 |
128 | private fun AnalyzedJavaMethod.callDelegate(): String {
129 |
130 | val returnWhenNotVoid = if (returnType != "void") "return " else ""
131 | return """
132 | |
133 | |${javadoc?.trim() ?: ""}
134 | |${annotations.joinToString("\n")}
135 | |public $genericReturnType$returnType $name($rawParameters) $exceptions{
136 | |${"""
137 | | ${returnWhenNotVoid}delegate.$name(${parameterNames.joinToString()});
138 | """.replaceIndentByMargin().let { it: String ->
139 | if (throws) """
140 | | try {
141 | | $it
142 | | } catch(SuppressedException e){
143 | | throw ($exceptionType) e.getCause();
144 | | }
145 | """.replaceIndentByMargin() else it
146 | }}
147 | |}
148 | """.replaceIndentByMargin(" ")
149 | }
150 |
151 |
--------------------------------------------------------------------------------
/generator/src/main/kotlin/writer/DelegateWriter.kt:
--------------------------------------------------------------------------------
1 | package writer
2 |
3 | import autoGeneratedJavadocHeader
4 | import parse.AnalyzedJavaFile
5 | import parse.AnalyzedJavaMethod
6 | import java.io.File
7 |
8 |
9 | fun writeDelegate(outPath: String,
10 | javaFile: AnalyzedJavaFile,
11 | javaPackage: String,
12 | javaClassName: String,
13 | compositeName: String,
14 | pluginName: String,
15 | extends: String,
16 | additionalImports: String? = null,
17 | transform: ((String) -> String)? = null,
18 | superClassPluginName: String = "",
19 | superClassDelegateName: String = "",
20 | superClassInputFile: AnalyzedJavaFile? = null,
21 | addCodeToClass: String? = null
22 | ) {
23 |
24 |
25 | val methodsSb = StringBuilder()
26 |
27 |
28 | val allMethods = mutableListOf()
29 | if (superClassInputFile != null) {
30 | allMethods.addAll(superClassInputFile.methods)
31 | }
32 | allMethods.addAll(javaFile.methods)
33 | val distinctMethods = allMethods.distinctBy { "${it.name} ${it.parameterTypes}" }.toMutableList()
34 |
35 | for (method in distinctMethods) {
36 |
37 | val isSameMethod: (AnalyzedJavaMethod) -> Boolean = { method.name == it.name && method.parameterTypes == it.parameterTypes }
38 | val definedInSuperComposite = superClassInputFile?.methods
39 | ?.filter(isSameMethod)?.isNotEmpty() ?: false
40 | val definedInComposite = javaFile.methods.filter(isSameMethod).isNotEmpty()
41 | val isVoid = method.returnType == "void"
42 |
43 |
44 | //methodsSb.appendln(
45 | // "//${method.signature} inSuperComposite: $definedInSuperComposite, inComposite: $definedInComposite")
46 | methodsSb.appendln(when {
47 | definedInComposite && !definedInSuperComposite && isVoid ->
48 | method.hook(pluginName)
49 | definedInComposite && !definedInSuperComposite && !isVoid ->
50 | method.callFunction(pluginName)
51 |
52 | definedInSuperComposite && isVoid ->
53 | method.forwardToDelegate(superClassDelegateName)
54 | definedInSuperComposite && !isVoid ->
55 | method.forwardToDelegateWithReturn(superClassDelegateName)
56 |
57 | else -> "// TODO ${method.signature} inSuperComposite: $definedInSuperComposite, inComposite: $definedInComposite"
58 | })
59 | }
60 |
61 | fun superDelegateInitialization(): String {
62 | return if (superClassDelegateName.isEmpty()) "" else
63 | "m$superClassDelegateName = new $superClassDelegateName(${compositeName.toLowerCase()});"
64 | }
65 |
66 | fun superDelegateDeclaration(): String {
67 | return if (superClassDelegateName.isEmpty()) "" else
68 | "private final $superClassDelegateName m$superClassDelegateName;"
69 | }
70 |
71 | fun addSuperDelegatePlugin(): String {
72 | return if (superClassDelegateName.isEmpty()) "" else """
73 | |public Removable addPlugin(final $superClassPluginName plugin) {
74 | | return m$superClassDelegateName.addPlugin(plugin);
75 | |}
76 | """.replaceIndentByMargin()
77 | }
78 |
79 | fun addPlugin(): String {
80 | return if (superClassDelegateName.isEmpty()) "" else """
81 | |@Override
82 | |public Removable addPlugin(final $pluginName plugin) {
83 | | final Removable removable = super.addPlugin(plugin);
84 | | final Removable superRemovable = m$superClassDelegateName.addPlugin(plugin);
85 | | return new Removable() {
86 | | @Override
87 | | public void remove() {
88 | | removable.remove();
89 | | superRemovable.remove();
90 | | }
91 | | };
92 | |}
93 | """.replaceIndentByMargin()
94 | }
95 |
96 | var activityDelegate = """
97 | |package com.pascalwelsch.compositeandroid.$javaPackage;
98 | |
99 | |import com.pascalwelsch.compositeandroid.core.*;
100 | |
101 | |${javaFile.imports}
102 | |
103 | |${additionalImports ?: ""}
104 | |
105 | |$autoGeneratedJavadocHeader
106 | |public class $javaClassName extends $extends {
107 | |
108 | | ${superDelegateDeclaration()}
109 | |
110 | | public $javaClassName(final $compositeName ${compositeName.toLowerCase()}) {
111 | | super(${compositeName.toLowerCase()});
112 | | ${superDelegateInitialization()}
113 | | }
114 | |
115 | |${addSuperDelegatePlugin().prependIndent()}
116 | |${addPlugin().prependIndent()}
117 | |
118 | |${methodsSb.toString()}
119 | |
120 | |${addCodeToClass ?: ""}
121 | |
122 | |}
123 | """.replaceIndentByMargin()
124 |
125 | if (transform != null) {
126 | activityDelegate = transform(activityDelegate)
127 | }
128 |
129 | val out = File("$outPath${javaPackage.replace('.', '/')}/$javaClassName.java")
130 | out.parentFile.mkdirs()
131 | out.printWriter().use { it.write(activityDelegate) }
132 | System.out.println("wrote ${out.absolutePath}")
133 | }
134 |
135 | fun AnalyzedJavaMethod.forwardToDelegate(delegateName: String): String {
136 | return """
137 | |
138 | |public void $name($rawParameters) $exceptions {
139 | | m$delegateName.$name(${parameterNames.joinToString()});
140 | |}
141 | """.replaceIndentByMargin(" ")
142 | }
143 |
144 | fun AnalyzedJavaMethod.forwardToDelegateWithReturn(delegateName: String): String {
145 | return """
146 | |
147 | |public $genericReturnType$returnType $name($rawParameters) $exceptions {
148 | | return m$delegateName.$name(${parameterNames.joinToString()});
149 | |}
150 | """.replaceIndentByMargin(" ")
151 | }
152 |
153 |
154 | /*fun AnalyzedJavaMethod.hook(originalGetterName: String = "getOriginal()",
155 | pluginType: String = "Plugin"): String {
156 |
157 |
158 | val typedArgs = parameterTypes.mapIndexed { i, type -> "($type) args[$i]" }
159 |
160 | val varargs = if (parameterTypes.size == 1
161 | && parameterTypes[0].contains("[]")) {
162 | "new Object[]{${parameterNames[0]}}"
163 | } else parameterNames.joinToString()
164 |
165 |
166 | val sb = StringBuilder()
167 | sb.appendln(" public void $name($rawParameters) $exceptions {")
168 | sb.appendln(
169 | " callHook(\"$name(${parameterTypes.joinToString()})\", new PluginCallVoid<$pluginType>() {")
170 | sb.appendln(" @Override")
171 | sb.appendln(
172 | " public void call(final NamedSuperCall superCall, final $pluginType plugin, final Object... args) {")
173 | if (throws) sb.appendln(" try {")
174 | sb.appendln(" plugin.$name(${listOf("superCall").plus(
175 | typedArgs).joinToString()});")
176 | if (throws) {
177 | sb.appendln(" } catch ($exceptionType e) {")
178 | sb.appendln(" throw new SuppressedException(e);")
179 | sb.appendln(" }")
180 | }
181 | sb.appendln(" }")
182 | sb.appendln(" }, new SuperCallVoid() {")
183 | sb.appendln(" @Override")
184 | sb.appendln(" public void call(final Object... args) {")
185 | if (throws) sb.appendln(" try {")
186 | sb.appendln(" $originalGetterName.super_$name(${typedArgs.joinToString()});")
187 | if (throws) {
188 | sb.appendln(" } catch ($exceptionType e) {")
189 | sb.appendln(" throw new SuppressedException(e);")
190 | sb.appendln(" }")
191 | }
192 | sb.appendln(" }")
193 | sb.appendln(
194 | " }${if (parameterNames.size > 0) ", " else ""}$varargs);")
195 | sb.appendln(" }")
196 |
197 | return sb.toString();
198 | }*/
199 |
200 | fun AnalyzedJavaMethod.hook(pluginType: String = "Plugin"): String {
201 |
202 | val varargs = if (parameterNames.size == 1 && parameterNames[0].contains("[]")) {
203 | "new Object[]{${parameterNames[0]}}"
204 | } else parameterNames.joinToString()
205 |
206 | val genericTypeCallType = if (parameterTypes.isEmpty()) "" else "<${parameterTypes.joinToString()}>"
207 |
208 | return """
209 | |public void $name($rawParameters) $exceptions {
210 | | if (mPlugins.isEmpty()) {
211 | |${"getOriginal().super_$name($varargs);"
212 | .wrapWithTryCatch(exceptionType).prependIndent(" ")}
213 | | return;
214 | | }
215 | |
216 | | final ListIterator<$pluginType> iterator = mPlugins.listIterator(mPlugins.size());
217 | |
218 | | final CallVoid${parameterNames.size}$genericTypeCallType superCall = new CallVoid${parameterNames.size}$genericTypeCallType("$name(${parameterTypes.joinToString()})") {
219 | |
220 | | @Override
221 | | public void call(${rawParametersBoxed.joinToString()}) {
222 | | if (iterator.hasPrevious()) {
223 | |${"iterator.previous().$name(this${if (parameterNames.size > 0) ", " else ""}$varargs);"
224 | .wrapWithTryCatch(exceptionType).prependIndent(" ")}
225 | | } else {
226 | |${"getOriginal().super_$name($varargs);"
227 | .wrapWithTryCatch(exceptionType).prependIndent(" ")}
228 | | }
229 | | }
230 | | };
231 | | superCall.call($varargs);
232 | |}
233 | """.replaceIndentByMargin(" ")
234 | }
235 |
236 | fun AnalyzedJavaMethod.callFunction(pluginType: String = "Plugin"): String {
237 | val typedArgs = parameterTypes.mapIndexed { i, type -> "($type) args[$i]" }
238 |
239 | val varargs = if (parameterNames.size == 1 && parameterNames[0].contains("[]")) {
240 | "new Object[]{${parameterNames[0]}}"
241 | } else parameterNames.joinToString()
242 |
243 | val genericTypes = mutableListOf().apply {
244 | add(boxedReturnType)
245 | addAll(parameterTypes)
246 | }
247 | val genericTypeCallType = "<${genericTypes.joinToString()}>"
248 |
249 | return """
250 | |public $genericReturnType$returnType $name($rawParameters) $exceptions {
251 | | if (mPlugins.isEmpty()) {
252 | |${"return getOriginal().super_$name($varargs);"
253 | .wrapWithTryCatch(exceptionType).prependIndent(" ")}
254 | | }
255 | |
256 | | final ListIterator<$pluginType> iterator = mPlugins.listIterator(mPlugins.size());
257 | |
258 | | final CallFun${parameterNames.size}$genericTypeCallType superCall = new CallFun${parameterNames.size}$genericTypeCallType("$name(${parameterTypes.joinToString()})") {
259 | |
260 | | @Override
261 | | public $boxedReturnType call(${rawParametersBoxed.joinToString()}) {
262 | | if (iterator.hasPrevious()) {
263 | |${"return iterator.previous().$name(this${if (parameterNames.size > 0) ", " else ""}$varargs);"
264 | .wrapWithTryCatch(exceptionType).prependIndent(" ")}
265 | | } else {
266 | |${"return getOriginal().super_$name($varargs);"
267 | .wrapWithTryCatch(exceptionType).prependIndent(" ")}
268 | | }
269 | | }
270 | | };
271 | | return superCall.call($varargs);
272 | |}
273 | """.replaceIndentByMargin(" ")
274 | }
275 |
276 | fun String.wrapWithTryCatch(exceptionType: String?): String =
277 | if (exceptionType == null) this else """
278 | |try{
279 | | $this
280 | |} catch ($exceptionType e) {
281 | | throw new SuppressedException(e);
282 | |}
283 | """.replaceIndentByMargin()
284 |
285 |
286 | fun AnalyzedJavaMethod.notImplemented(): String {
287 | val sb = StringBuilder()
288 | sb.appendln(" public $genericReturnType$returnType $name($rawParameters) {")
289 | sb.appendln(" //TODO not implemented")
290 | sb.appendln(" return null;")
291 | sb.appendln(" }")
292 | return sb.toString();
293 | }
294 |
295 |
--------------------------------------------------------------------------------
/generator/src/main/kotlin/writer/InterfaceWriter.kt:
--------------------------------------------------------------------------------
1 | package writer
2 |
3 | import autoGeneratedJavadocHeader
4 | import parse.AnalyzedJavaFile
5 | import parse.AnalyzedJavaMethod
6 | import java.io.File
7 |
8 |
9 | fun writeInterface(outPath: String,
10 | javaFile: AnalyzedJavaFile,
11 | javaPackage: String,
12 | javaClassName: String,
13 | extends: String? = null,
14 | additionalImports: String = "",
15 | transform: ((String) -> String)? = null,
16 | superClassPluginNames: List = listOf(),
17 | superClassDelegateName: String = "",
18 | superClassInfputFile: AnalyzedJavaFile? = null,
19 | addCodeToClass: String? = null) {
20 |
21 | val sb = StringBuilder("""
22 | |package com.pascalwelsch.compositeandroid.$javaPackage;
23 | |
24 | |import com.pascalwelsch.compositeandroid.core.*;
25 | |
26 | |${javaFile.imports}
27 | |
28 | |$additionalImports
29 | |
30 | |$autoGeneratedJavadocHeader
31 | |public interface $javaClassName ${extends?.let { "extends $extends " } ?: ""}{
32 | |
33 | |
34 | """.replaceIndentByMargin())
35 |
36 | for (method in javaFile.methods) with(method) {
37 | sb.appendln(toInterface())
38 | sb.appendln(toSuperInterface())
39 | }
40 |
41 | addCodeToClass?.let { sb.appendln(it) }
42 |
43 | sb.appendln("}")
44 |
45 | var output = sb.toString()
46 | if (transform != null) {
47 | output = transform(output)
48 | }
49 |
50 | val out = File("$outPath${javaPackage.replace('.', '/')}/$javaClassName.java")
51 | out.parentFile.mkdirs()
52 | out.printWriter().use { it.write(output) }
53 | System.out.println("wrote ${out.absolutePath}")
54 | }
55 |
56 |
57 | private fun AnalyzedJavaMethod.toInterface(): String {
58 | return """
59 | |
60 | |$genericReturnType$returnType $name($rawParameters) $exceptions;
61 | """.replaceIndentByMargin(" ")
62 | }
63 |
64 | private fun AnalyzedJavaMethod.toSuperInterface(): String {
65 | return """
66 | |
67 | |$genericReturnType$returnType super_$name($rawParameters) $exceptions;
68 | """.replaceIndentByMargin(" ")
69 | }
--------------------------------------------------------------------------------
/generator/src/main/kotlin/writer/PluginWriter.kt:
--------------------------------------------------------------------------------
1 | package writer
2 |
3 | import autoGeneratedJavadocHeader
4 | import parse.AnalyzedJavaFile
5 | import parse.AnalyzedJavaMethod
6 | import java.io.File
7 |
8 | fun writePlugin(outPath: String,
9 | normalClassName: String,
10 | javaFile: AnalyzedJavaFile,
11 | javaPackage: String,
12 | javaClassName: String,
13 | extends: String,
14 | additionalImports: String? = null,
15 | transform: ((String) -> String)? = null,
16 | superClassInputFile: AnalyzedJavaFile? = null,
17 | addCodeToClass: String? = null) {
18 |
19 | val sb = StringBuilder()
20 | for (method in javaFile.methods) with(method) {
21 | when (returnType) {
22 | "void" -> sb.append(method.callListener())
23 | else -> sb.append(method.returnSuperListener())
24 | }
25 | }
26 | val methods = sb.toString()
27 |
28 |
29 | val niceGetter: String = """
30 | |public $normalClassName get$normalClassName() {
31 | | return ($normalClassName) getOriginal();
32 | |}
33 | """.replaceIndentByMargin(" ")
34 |
35 |
36 | var code = """
37 | |package com.pascalwelsch.compositeandroid.$javaPackage;
38 | |
39 | |import com.pascalwelsch.compositeandroid.core.*;
40 | |
41 | |${javaFile.imports}
42 | |
43 | |${additionalImports ?: ""}
44 | |
45 | |$autoGeneratedJavadocHeader
46 | |@SuppressWarnings("unused")
47 | |public class $javaClassName extends $extends {
48 | |$methods
49 | |
50 | |$niceGetter
51 | |
52 | |${addCodeToClass ?: ""}
53 | |}
54 | """.replaceIndentByMargin()
55 |
56 | if (transform != null) {
57 | code = transform(code)
58 | }
59 |
60 | val out = File("$outPath${javaPackage.replace('.', '/')}/$javaClassName.java")
61 | out.parentFile.mkdirs()
62 | out.printWriter().use { it.write(code) }
63 | System.out.println("wrote ${out.absolutePath}")
64 | }
65 |
66 | fun AnalyzedJavaMethod.returnSuperListener(): String {
67 | val genericTypes = mutableListOf().apply {
68 | add(boxedReturnType)
69 | addAll(parameterTypes)
70 | }
71 |
72 | return """
73 | ${javadoc?.trim() ?: ""}
74 | public $genericReturnType$returnType $name($rawParameters) $exceptions{
75 | verifyMethodCalledFromDelegate("$name(${parameterTypes.joinToString()})");
76 | return ((CallFun${parameterNames.size}<${genericTypes.joinToString()}>) mSuperListeners.pop()).call(${parameterNames.joinToString()});
77 | }
78 |
79 | $genericReturnType$returnType $name(final CallFun${parameterNames.size}<${genericTypes.joinToString()}> superCall ${if (parameterNames.isNotEmpty()) ", " else ""}$rawParameters) $exceptions{
80 | synchronized (mSuperListeners) {
81 | mSuperListeners.push(superCall);
82 | return $name(${parameterNames.joinToString()});
83 | }
84 | }
85 | """
86 | }
87 |
88 | fun AnalyzedJavaMethod.callListener(): String {
89 | val genericTypeCallType = if(parameterTypes.isEmpty()) "" else "<${parameterTypes.joinToString()}>"
90 |
91 | return """
92 | ${javadoc?.trim() ?: ""}
93 | public void $name($rawParameters) $exceptions{
94 | verifyMethodCalledFromDelegate("$name(${parameterTypes.joinToString()})");
95 | ((CallVoid${parameterNames.size}$genericTypeCallType) mSuperListeners.pop()).call(${parameterNames.joinToString()});
96 | }
97 |
98 | void $name(final CallVoid${parameterNames.size}$genericTypeCallType superCall ${if (parameterNames.isNotEmpty()) ", " else ""}$rawParameters) $exceptions{
99 | synchronized (mSuperListeners) {
100 | mSuperListeners.push(superCall);
101 | $name(${parameterNames.joinToString()});
102 | }
103 | }
104 | """
105 | }
106 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | org.gradle.jvmargs=-Xmx3072m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/passsy/CompositeAndroid/80dec3eed5a72a0f18e1dd880d646fa3af002d93/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Aug 16 12:32:04 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn ( ) {
37 | echo "$*"
38 | }
39 |
40 | die ( ) {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save ( ) {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/sample/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion COMPILE_SDK_VERSION
5 |
6 | defaultConfig {
7 | applicationId "com.pascalwelsch.compositeandroid"
8 | minSdkVersion MIN_SDK_VERSION
9 | targetSdkVersion TARGET_SDK_VERSION
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 |
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 |
21 | lintOptions {
22 | abortOnError false
23 | }
24 |
25 | dexOptions {
26 | preDexLibraries = !ciBuild
27 | javaMaxHeapSize "2g"
28 | }
29 | }
30 |
31 | dependencies {
32 | compile project(':activity')
33 | compile project(':fragment')
34 |
35 | testCompile 'junit:junit:4.12'
36 | compile "com.android.support:appcompat-v7:$supportLibraryVersion"
37 | }
38 |
--------------------------------------------------------------------------------
/sample/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/23.0.2/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/pascalwelsch/compositeandroid/ActivityTracking.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid;
2 |
3 | import com.pascalwelsch.compositeandroid.activity.ActivityPlugin;
4 |
5 | import android.util.Log;
6 |
7 | public class ActivityTracking extends ActivityPlugin {
8 |
9 | private static final String TAG = ActivityTracking.class.getSimpleName();
10 |
11 | @Override
12 | public void onResume() {
13 | super.onResume();
14 | Log.v(TAG, "#1 onResume()");
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/pascalwelsch/compositeandroid/ActivityTracking2.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid;
2 |
3 | import com.pascalwelsch.compositeandroid.activity.ActivityPlugin;
4 |
5 | import android.util.Log;
6 |
7 | public class ActivityTracking2 extends ActivityPlugin {
8 |
9 | private static final String TAG = ActivityTracking2.class.getSimpleName();
10 |
11 | @Override
12 | public void onResume() {
13 | super.onResume();
14 | Log.v(TAG, "#2 onResume()");
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/pascalwelsch/compositeandroid/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid;
2 |
3 | import com.pascalwelsch.compositeandroid.activity.CompositeActivity;
4 | import com.pascalwelsch.compositeandroid.fragment.TestFragment;
5 |
6 | import android.os.Bundle;
7 | import android.view.View;
8 |
9 | public class MainActivity extends CompositeActivity {
10 |
11 | public MainActivity() {
12 | addPlugin(new ActivityTracking());
13 | addPlugin(new ActivityTracking2());
14 | }
15 |
16 | @Override
17 | public void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.activity_main);
20 |
21 | final View btn = findViewById(R.id.button);
22 | assert btn != null;
23 | btn.setOnClickListener(new View.OnClickListener() {
24 | @Override
25 | public void onClick(final View v) {
26 | new TestFragment().show(getSupportFragmentManager(), "dialog");
27 | }
28 | });
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/pascalwelsch/compositeandroid/fragment/DialogTracking.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.fragment;
2 |
3 | import android.support.v4.app.FragmentManager;
4 | import android.util.Log;
5 |
6 | public class DialogTracking extends DialogFragmentPlugin {
7 |
8 | private static final String TAG = DialogTracking.class.getSimpleName();
9 |
10 | private String mName;
11 |
12 | public DialogTracking(final String name) {
13 |
14 | mName = name;
15 | }
16 |
17 | @Override
18 | public void onResume() {
19 | super.onResume();
20 |
21 | Log.v(TAG, mName + " onResume()");
22 | }
23 |
24 | @Override
25 | public void onStart() {
26 | super.onStart();
27 |
28 | Log.v(TAG, mName + " onStart()");
29 | }
30 |
31 | @Override
32 | public void onStop() {
33 | super.onStop();
34 | Log.v(TAG, mName + " onStop()");
35 | }
36 |
37 | @Override
38 | public void show(final FragmentManager manager, final String tag) {
39 | super.show(manager, tag);
40 | Log.v(TAG, mName + "show(fm, tag)");
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/pascalwelsch/compositeandroid/fragment/FragmentTracking.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.fragment;
2 |
3 | import android.util.Log;
4 |
5 | public class FragmentTracking extends FragmentPlugin {
6 |
7 | private static final String TAG = FragmentTracking.class.getSimpleName();
8 |
9 | @Override
10 | public void onResume() {
11 | super.onResume();
12 |
13 | Log.v(TAG, "#1 onResume()");
14 | }
15 |
16 | @Override
17 | public void onStart() {
18 | super.onStart();
19 |
20 | Log.v(TAG, "#1 onStart()");
21 | }
22 |
23 | @Override
24 | public void onStop() {
25 | super.onStop();
26 | Log.v(TAG, "#1 onStop()");
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/pascalwelsch/compositeandroid/fragment/TestFragment.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.fragment;
2 |
3 | import android.support.annotation.Nullable;
4 | import android.util.Log;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | public class TestFragment extends CompositeDialogFragment {
9 |
10 | private static final String TAG = TestFragment.class.getSimpleName();
11 |
12 | public TestFragment() {
13 | addPlugin(new FragmentTracking());
14 | addPlugin(new DialogTracking("single added"));
15 |
16 | addDialogFragmentPlugins(
17 | new DialogTracking("multiple dialog plugins #1"),
18 | new DialogTracking("multiple dialog plugins #2"));
19 | addFragmentPlugins(new FragmentTracking(), new DialogTracking("multiple fragment plugins"));
20 | }
21 |
22 | @Nullable
23 | @Override
24 | public View getView() {
25 | final TextView textView = new TextView(getContext());
26 | textView.setText("hello world");
27 | return textView;
28 | }
29 |
30 | @Override
31 | public void onResume() {
32 | super.onResume();
33 | Log.v(TAG, "fragment onResume()");
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/pascalwelsch/compositeandroid/performance/CompositePerformanceTestActivity.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.performance;
2 |
3 | import com.pascalwelsch.compositeandroid.R;
4 | import com.pascalwelsch.compositeandroid.activity.ActivityPlugin;
5 | import com.pascalwelsch.compositeandroid.activity.CompositeActivity;
6 |
7 | import android.annotation.SuppressLint;
8 | import android.content.res.Resources;
9 | import android.os.Bundle;
10 | import android.support.annotation.Nullable;
11 | import android.support.v7.app.AppCompatDelegate;
12 | import android.util.Log;
13 | import android.view.View;
14 |
15 | import java.io.File;
16 | import java.util.ArrayList;
17 | import java.util.List;
18 |
19 | public class CompositePerformanceTestActivity extends CompositeActivity {
20 |
21 | private static class PerformanceTestPlugin extends ActivityPlugin {
22 |
23 | private final String mName;
24 |
25 | public PerformanceTestPlugin(final String name) {
26 |
27 | mName = name;
28 | }
29 |
30 | @Override
31 | public void onSaveInstanceState(final Bundle outState) {
32 | super.onSaveInstanceState(outState);
33 | outState.putString(mName, mName);
34 | }
35 | }
36 |
37 | private static class PerformanceTestPluginOverridden extends PerformanceTestPlugin {
38 |
39 | public PerformanceTestPluginOverridden(final String name) {
40 | super(name);
41 | }
42 |
43 | @Override
44 | public Resources getResources() {
45 | return super.getResources();
46 | }
47 |
48 | @Override
49 | public File getCacheDir() {
50 | return super.getCacheDir();
51 | }
52 | }
53 |
54 | private static final String TAG = CompositePerformanceTestActivity.class.getSimpleName();
55 |
56 | public static final int ITERATIONS = 50000;
57 |
58 | private final String mName = "0";
59 |
60 | public CompositePerformanceTestActivity() {
61 | addPlugin(new PerformanceTestPluginOverridden("1"));
62 | addPlugin(new PerformanceTestPluginOverridden("2"));
63 | addPlugin(new PerformanceTestPluginOverridden("3"));
64 | addPlugin(new PerformanceTestPluginOverridden("4"));
65 | addPlugin(new PerformanceTestPluginOverridden("5"));
66 | /*addPlugin(new PerformanceTestPlugin("1"));
67 | addPlugin(new PerformanceTestPlugin("2"));
68 | addPlugin(new PerformanceTestPlugin("3"));
69 | addPlugin(new PerformanceTestPlugin("4"));
70 | addPlugin(new PerformanceTestPlugin("5"));*/
71 | }
72 |
73 |
74 | public File getFastCacheDir() {
75 | return super_getCacheDir();
76 | }
77 |
78 | public Resources getFastResources() {
79 | return super_getResources();
80 | }
81 |
82 | @Override
83 | public void onCreate(@Nullable final Bundle savedInstanceState) {
84 | super.onCreate(savedInstanceState);
85 | setContentView(R.layout.activity_performance);
86 | final View start = findViewById(R.id.start);
87 | assert start != null;
88 | start.setOnClickListener(new View.OnClickListener() {
89 | @Override
90 | public void onClick(final View v) {
91 | runPerformanceTest();
92 | }
93 | });
94 | }
95 |
96 | @Override
97 | public void onSaveInstanceState(final Bundle outState) {
98 | super.onSaveInstanceState(outState);
99 | outState.putString(mName, mName);
100 | }
101 |
102 | private void runPerformanceTest() {
103 |
104 | String number = "5 plugins: ";
105 |
106 | System.gc();
107 | final int iterations = 1;
108 | float duration = 0;
109 |
110 | duration = 0;
111 | for (int i = 0; i < iterations; i++) {
112 | duration += testGetCacheDir();
113 | }
114 | duration /= iterations;
115 | Log.v(TAG, number + "getCacheDir() in " + duration + "ms");
116 |
117 | duration = 0;
118 | for (int i = 0; i < iterations; i++) {
119 | duration += testFastGetCacheDir();
120 | }
121 | duration /= iterations;
122 | Log.v(TAG, number + "getCacheDir() in " + duration + "ms (direct)");
123 |
124 | duration = 0;
125 | for (int i = 0; i < iterations; i++) {
126 | duration += testGetResources();
127 | }
128 | duration /= iterations;
129 | Log.v(TAG, number + "getResources() in " + duration + "ms");
130 |
131 | duration = 0;
132 | for (int i = 0; i < iterations; i++) {
133 | duration += testFastGetResources();
134 | }
135 | duration /= iterations;
136 | Log.v(TAG, number + "getResources() in " + duration + "ms (direct)");
137 |
138 |
139 | duration = 0;
140 | for (int i = 0; i < iterations; i++) {
141 | duration += testOnSaveInstanceState();
142 | }
143 | duration /= iterations;
144 | Log.v(TAG, number + "onSaveInstanceState() in " + duration + "ms");
145 |
146 |
147 | duration = 0;
148 | for (int i = 0; i < iterations; i++) {
149 | duration += testFastOnSaveInstanceState();
150 | }
151 | duration /= iterations;
152 | Log.v(TAG, number + "onSaveInstanceState() in " + duration + "ms (direct)");
153 | }
154 |
155 | private long testGetCacheDir() {
156 | final List results = new ArrayList<>();
157 | final long start = System.currentTimeMillis();
158 | for (long i = 0; i < ITERATIONS; i++) {
159 | final File file = getCacheDir();
160 | results.add(file);
161 | }
162 | return System.currentTimeMillis() - start;
163 | }
164 |
165 | private long testFastGetCacheDir() {
166 | final List results = new ArrayList<>();
167 | final long start = System.currentTimeMillis();
168 | for (long i = 0; i < ITERATIONS; i++) {
169 | final File file = getFastCacheDir();
170 | results.add(file);
171 | }
172 | return System.currentTimeMillis() - start;
173 | }
174 |
175 | private long testGetDelegate() {
176 | final List results = new ArrayList<>();
177 | final long start = System.currentTimeMillis();
178 | for (long i = 0; i < ITERATIONS; i++) {
179 | final AppCompatDelegate delegate = getDelegate();
180 | results.add(delegate);
181 | }
182 | return System.currentTimeMillis() - start;
183 | }
184 |
185 | private long testGetFile() {
186 | final List results = new ArrayList<>();
187 | final long start = System.currentTimeMillis();
188 | for (long i = 0; i < ITERATIONS; i++) {
189 | final File file = getFilesDir();
190 | results.add(file);
191 | }
192 | return System.currentTimeMillis() - start;
193 | }
194 |
195 | private long testGetResources() {
196 | final List results = new ArrayList<>();
197 | final long start = System.currentTimeMillis();
198 | for (long i = 0; i < ITERATIONS; i++) {
199 | final Resources resources = getResources();
200 | results.add(resources);
201 | }
202 | return System.currentTimeMillis() - start;
203 | }
204 |
205 | private long testFastGetResources() {
206 | final List results = new ArrayList<>();
207 | final long start = System.currentTimeMillis();
208 | for (long i = 0; i < ITERATIONS; i++) {
209 | final Resources resources = getFastResources();
210 | results.add(resources);
211 | }
212 | return System.currentTimeMillis() - start;
213 | }
214 |
215 | @SuppressLint("Assert")
216 | private long testOnSaveInstanceState() {
217 | final List results = new ArrayList<>();
218 | final long start = System.currentTimeMillis();
219 | for (long i = 0; i < ITERATIONS; i++) {
220 | final Bundle outState = new Bundle();
221 | onSaveInstanceState(outState);
222 | results.add(outState);
223 | }
224 | final long end = System.currentTimeMillis();
225 |
226 | assert results.get(0).getString("1") == "1";
227 | assert results.get(0).getString("20") == "20";
228 | assert results.get(results.size()).getString("1") == "1";
229 | assert results.get(results.size()).getString("20") == "20";
230 |
231 | return end - start;
232 | }
233 |
234 | @SuppressLint("Assert")
235 | private long testFastOnSaveInstanceState() {
236 | final List results = new ArrayList<>();
237 | final long start = System.currentTimeMillis();
238 | for (long i = 0; i < ITERATIONS; i++) {
239 | final Bundle outState = new Bundle();
240 | super_onSaveInstanceState(outState);
241 | results.add(outState);
242 | }
243 | final long end = System.currentTimeMillis();
244 |
245 | assert results.get(0).getString("1") == "1";
246 | assert results.get(0).getString("20") == "20";
247 | assert results.get(results.size()).getString("1") == "1";
248 | assert results.get(results.size()).getString("20") == "20";
249 |
250 | return end - start;
251 | }
252 | }
253 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/pascalwelsch/compositeandroid/performance/PerformanceTestActivity.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.performance;
2 |
3 | import com.pascalwelsch.compositeandroid.R;
4 |
5 | import android.annotation.SuppressLint;
6 | import android.content.res.Resources;
7 | import android.os.Bundle;
8 | import android.support.annotation.Nullable;
9 | import android.support.v7.app.AppCompatDelegate;
10 | import android.util.Log;
11 | import android.view.View;
12 |
13 | import java.io.File;
14 | import java.util.ArrayList;
15 | import java.util.List;
16 |
17 | public class PerformanceTestActivity extends PerformanceTestActivity1 {
18 |
19 | private static final String TAG = PerformanceTestActivity.class.getSimpleName();
20 |
21 | public static final int ITERATIONS = 50000;
22 |
23 | private final String mName = "0";
24 |
25 | @Override
26 | public Resources getResources() {
27 | return super.getResources();
28 | }
29 |
30 | @Override
31 | protected void onCreate(@Nullable final Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | setContentView(R.layout.activity_performance);
34 | final View start = findViewById(R.id.start);
35 | assert start != null;
36 | start.setOnClickListener(new View.OnClickListener() {
37 | @Override
38 | public void onClick(final View v) {
39 | runPerformanceTest();
40 | }
41 | });
42 | }
43 |
44 | @Override
45 | protected void onSaveInstanceState(final Bundle outState) {
46 | super.onSaveInstanceState(outState);
47 | outState.putString(mName, mName);
48 | }
49 |
50 | private void runPerformanceTest() {
51 |
52 | String number = "5 stages: ";
53 |
54 | System.gc();
55 | final int iterations = 1;
56 | float duration = 0;
57 |
58 | duration = 0;
59 | for (int i = 0; i < iterations; i++) {
60 | duration += testGetCacheDir();
61 | }
62 | duration /= iterations;
63 | Log.v(TAG, number + "getCacheDir() in " + duration + "ms");
64 |
65 | duration = 0;
66 | for (int i = 0; i < iterations; i++) {
67 | duration += testGetResources();
68 | }
69 | duration /= iterations;
70 | Log.v(TAG, number + "getResources() in " + duration + "ms");
71 |
72 | duration = 0;
73 | for (int i = 0; i < iterations; i++) {
74 | duration += testGetDelegate();
75 | }
76 | duration /= iterations;
77 | Log.v(TAG, number + "getDelegate() in " + duration + "ms");
78 |
79 | duration = 0;
80 | for (int i = 0; i < iterations; i++) {
81 | duration += testGetFile();
82 | }
83 | duration /= iterations;
84 | Log.v(TAG, number + "getFile() in " + duration + "ms");
85 |
86 | duration = 0;
87 | for (int i = 0; i < iterations; i++) {
88 | duration += testOnSaveInstanceState();
89 | }
90 | duration /= iterations;
91 | Log.v(TAG, number + "onSaveInstanceState() in " + duration + "ms");
92 |
93 | }
94 |
95 | private long testGetCacheDir() {
96 | final List results = new ArrayList<>();
97 | final long start = System.currentTimeMillis();
98 | for (long i = 0; i < ITERATIONS; i++) {
99 | final File file = getCacheDir();
100 | results.add(file);
101 | }
102 | return System.currentTimeMillis() - start;
103 | }
104 |
105 | private long testGetDelegate() {
106 | final List results = new ArrayList<>();
107 | final long start = System.currentTimeMillis();
108 | for (long i = 0; i < ITERATIONS; i++) {
109 | final AppCompatDelegate delegate = getDelegate();
110 | results.add(delegate);
111 | }
112 | return System.currentTimeMillis() - start;
113 | }
114 |
115 | private long testGetFile() {
116 | final List results = new ArrayList<>();
117 | final long start = System.currentTimeMillis();
118 | for (long i = 0; i < ITERATIONS; i++) {
119 | final File file = getFilesDir();
120 | results.add(file);
121 | }
122 | return System.currentTimeMillis() - start;
123 | }
124 |
125 | private long testGetResources() {
126 | final List results = new ArrayList<>();
127 | final long start = System.currentTimeMillis();
128 | for (long i = 0; i < ITERATIONS; i++) {
129 | final Resources resources = getResources();
130 | results.add(resources);
131 | }
132 | return System.currentTimeMillis() - start;
133 | }
134 |
135 | @SuppressLint("Assert")
136 | private long testOnSaveInstanceState() {
137 | final List results = new ArrayList<>();
138 | final long start = System.currentTimeMillis();
139 | for (long i = 0; i < ITERATIONS; i++) {
140 | final Bundle outState = new Bundle();
141 | onSaveInstanceState(outState);
142 | results.add(outState);
143 | }
144 | final long end = System.currentTimeMillis();
145 |
146 | assert results.get(0).getString("1") == "1";
147 | assert results.get(0).getString("20") == "20";
148 | assert results.get(results.size()).getString("1") == "1";
149 | assert results.get(results.size()).getString("20") == "20";
150 |
151 | return end - start;
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/pascalwelsch/compositeandroid/performance/PerformanceTestActivity1.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.performance;
2 |
3 | import android.content.res.Resources;
4 | import android.os.Bundle;
5 |
6 | public class PerformanceTestActivity1 extends PerformanceTestActivity2 {
7 |
8 | private final String mName = "1";
9 |
10 | @Override
11 | public Resources getResources() {
12 | return super.getResources();
13 | }
14 |
15 | @Override
16 | protected void onSaveInstanceState(final Bundle outState) {
17 | super.onSaveInstanceState(outState);
18 | outState.putString(mName, mName);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/pascalwelsch/compositeandroid/performance/PerformanceTestActivity2.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.performance;
2 |
3 | import android.content.res.Resources;
4 | import android.os.Bundle;
5 |
6 | public class PerformanceTestActivity2 extends PerformanceTestActivity3 {
7 |
8 | private final String mName = "2";
9 |
10 | @Override
11 | public Resources getResources() {
12 | return super.getResources();
13 | }
14 |
15 | @Override
16 | protected void onSaveInstanceState(final Bundle outState) {
17 | super.onSaveInstanceState(outState);
18 | outState.putString(mName, mName);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/pascalwelsch/compositeandroid/performance/PerformanceTestActivity3.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.performance;
2 |
3 | import android.content.res.Resources;
4 | import android.os.Bundle;
5 |
6 | public class PerformanceTestActivity3 extends PerformanceTestActivity4 {
7 |
8 | private final String mName = "3";
9 |
10 | @Override
11 | public Resources getResources() {
12 | return super.getResources();
13 | }
14 |
15 | @Override
16 | protected void onSaveInstanceState(final Bundle outState) {
17 | super.onSaveInstanceState(outState);
18 | outState.putString(mName, mName);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/pascalwelsch/compositeandroid/performance/PerformanceTestActivity4.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.performance;
2 |
3 | import android.content.res.Resources;
4 | import android.os.Bundle;
5 |
6 | public class PerformanceTestActivity4 extends PerformanceTestActivity5 {
7 |
8 | private final String mName = "4";
9 |
10 | @Override
11 | public Resources getResources() {
12 | return super.getResources();
13 | }
14 |
15 | @Override
16 | protected void onSaveInstanceState(final Bundle outState) {
17 | super.onSaveInstanceState(outState);
18 | outState.putString(mName, mName);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/pascalwelsch/compositeandroid/performance/PerformanceTestActivity5.java:
--------------------------------------------------------------------------------
1 | package com.pascalwelsch.compositeandroid.performance;
2 |
3 | import android.content.res.Resources;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 |
7 | public class PerformanceTestActivity5 extends AppCompatActivity {
8 |
9 | private final String mName = "5";
10 |
11 | @Override
12 | public Resources getResources() {
13 | return super.getResources();
14 | }
15 |
16 | @Override
17 | protected void onSaveInstanceState(final Bundle outState) {
18 | super.onSaveInstanceState(outState);
19 | outState.putString(mName, mName);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
17 |
18 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_performance.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/passsy/CompositeAndroid/80dec3eed5a72a0f18e1dd880d646fa3af002d93/sample/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/passsy/CompositeAndroid/80dec3eed5a72a0f18e1dd880d646fa3af002d93/sample/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/passsy/CompositeAndroid/80dec3eed5a72a0f18e1dd880d646fa3af002d93/sample/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/passsy/CompositeAndroid/80dec3eed5a72a0f18e1dd880d646fa3af002d93/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/passsy/CompositeAndroid/80dec3eed5a72a0f18e1dd880d646fa3af002d93/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Composite Android
3 |
4 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':sample', ':activity', ':blueprints', ':fragment', ':core'
2 |
--------------------------------------------------------------------------------