Skip to content

Commit d8d5239

Browse files
committed
Merge pull request androidannotations#879 from yDelouis/852_newReceiverAnnotation
New Receiver annotation
2 parents 72b0e15 + 3f38176 commit d8d5239

26 files changed

Lines changed: 1147 additions & 53 deletions
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* Copyright (C) 2010-2013 eBusiness Information, Excilys Group
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed To in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package org.androidannotations.annotations;
17+
18+
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Retention;
21+
import java.lang.annotation.RetentionPolicy;
22+
import java.lang.annotation.Target;
23+
24+
/**
25+
* This annotation is intended to be used on methods of an {@link EActivity}, {@link EFragment} or {@link EService}.
26+
* When this annotation is used, a {@link android.content.BroadcastReceiver}
27+
* will be created to receive the Intent corresponding to the given actions
28+
* and it will call the annotated method.
29+
* <p/>
30+
* The annotated method MUST return void and MAY have one parameter:
31+
* <ul>
32+
* <li>An {@link android.content.Intent}</li>
33+
* </ul>
34+
* <p/>
35+
* The annotation has three parameters:
36+
* <ul>
37+
* <li>
38+
* {@link #actions()}: One or several {@link java.lang.String} indicating the actions which will spark the method.
39+
* This parameter is MANDATORY
40+
* </li>
41+
* <li>
42+
* {@link #registerAt()}: The moment when the {@link android.content.BroadcastReceiver}
43+
* will be registered and unregistered. By default : OnCreate/OnDestroy.
44+
* The available values depend on the enclosing enhanced component.
45+
*
46+
* </li>
47+
* <li>
48+
* {@link #local()}: Specify whether {@link android.support.v4.content.LocalBroadcastManager} should be used.
49+
* To use {@link android.support.v4.content.LocalBroadcastManager}, you MUST have android support-v4 in your classpath.
50+
* Default value is false.
51+
* </li>
52+
* </ul>
53+
* <p/>
54+
* <blockquote>
55+
*
56+
* Example :
57+
*
58+
* <pre>
59+
* &#064;EActivity
60+
* public class MyActivity {
61+
*
62+
* &#064;Receiver(actions={{@link android.net.wifi.WifiManager.WIFI_STATE_CHANGED_ACTION}}
63+
* public void onWifiStateChanged(Intent intent);
64+
*
65+
* &#064;Receiver(actions={{@link android.net.wifi.WifiManager.WIFI_STATE_CHANGED_ACTION}, registerAt=RegisterAt.OnResumeOnPause}
66+
* public void onWifiStateChangedWithoutIntent();
67+
*
68+
* }
69+
* </pre>
70+
*
71+
*/
72+
@Retention(RetentionPolicy.CLASS)
73+
@Target(ElementType.METHOD)
74+
public @interface Receiver {
75+
76+
String[] actions();
77+
78+
RegisterAt registerAt() default RegisterAt.OnCreateOnDestroy;
79+
80+
boolean local() default false;
81+
82+
public enum RegisterAt {
83+
OnCreateOnDestroy,
84+
OnStartOnStop,
85+
OnResumeOnPause,
86+
OnAttachOnDetach
87+
}
88+
}

‎AndroidAnnotations/androidannotations/pom.xml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
<dependency>
3333
<groupId>com.google.android</groupId>
3434
<artifactId>android</artifactId>
35-
<version>1.6_r2</version>
35+
<version>4.1.1.4</version>
3636
<scope>provided</scope>
3737
</dependency>
3838
<dependency>

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/handler/AnnotationHandlers.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ public AnnotationHandlers(ProcessingEnvironment processingEnvironment) {
116116
add(new OnActivityResultHandler(processingEnvironment));
117117
add(new HierarchyViewerSupportHandler(processingEnvironment));
118118
add(new WindowFeatureHandler(processingEnvironment));
119+
add(new ReceiverHandler(processingEnvironment));
119120

120121
/* After injection methods must be after injections */
121122
add(new AfterInjectHandler(processingEnvironment));
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/**
2+
* Copyright (C) 2010-2013 eBusiness Information, Excilys Group
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed To in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package org.androidannotations.handler;
17+
18+
import com.sun.codemodel.*;
19+
import org.androidannotations.annotations.Receiver;
20+
import org.androidannotations.helper.ModelConstants;
21+
import org.androidannotations.holder.HasReceiverRegistration;
22+
import org.androidannotations.model.AnnotationElements;
23+
import org.androidannotations.process.IsValid;
24+
25+
import javax.annotation.processing.ProcessingEnvironment;
26+
import javax.lang.model.element.Element;
27+
import javax.lang.model.element.ExecutableElement;
28+
29+
import static com.sun.codemodel.JExpr._new;
30+
import static com.sun.codemodel.JMod.FINAL;
31+
import static com.sun.codemodel.JMod.PRIVATE;
32+
import static com.sun.codemodel.JMod.PUBLIC;
33+
34+
public class ReceiverHandler extends BaseAnnotationHandler<HasReceiverRegistration> {
35+
36+
public ReceiverHandler(ProcessingEnvironment processingEnvironment) {
37+
super(Receiver.class, processingEnvironment);
38+
}
39+
40+
@Override
41+
protected void validate(Element element, AnnotationElements validatedElements, IsValid valid) {
42+
validatorHelper.enclosingElementHasEActivityOrEFragmentOrEService(element, validatedElements, valid);
43+
44+
validatorHelper.isNotPrivate(element, valid);
45+
46+
validatorHelper.returnTypeIsVoid((ExecutableElement) element, valid);
47+
48+
validatorHelper.param.zeroOrOneIntentParameter((ExecutableElement) element, valid);
49+
50+
validatorHelper.hasNotMultipleAnnotatedMethodWithSameName(element.getEnclosingElement(), valid, Receiver.class);
51+
52+
validatorHelper.hasRightRegisterAtValueDependingOnEnclosingElement(element, valid);
53+
54+
validatorHelper.hasSupportV4JarIfLocal(element, valid);
55+
}
56+
57+
@Override
58+
public void process(Element element, HasReceiverRegistration holder) throws Exception {
59+
60+
String methodName = element.getSimpleName().toString();
61+
boolean hasIntentParam = !((ExecutableElement) element).getParameters().isEmpty();
62+
String receiverName = methodName+"Receiver"+ ModelConstants.GENERATION_SUFFIX;
63+
64+
Receiver annotation = element.getAnnotation(Receiver.class);
65+
String[] actions = annotation.actions();
66+
Receiver.RegisterAt registerAt = annotation.registerAt();
67+
boolean local = annotation.local();
68+
69+
JFieldVar intentFilterField = holder.getIntentFilterField(actions);
70+
JFieldVar receiverField = createReceiverField(holder, receiverName, methodName, hasIntentParam);
71+
registerAndUnregisterReceiver(holder, registerAt, intentFilterField, receiverField, local);
72+
}
73+
74+
private JFieldVar createReceiverField(HasReceiverRegistration holder, String receiverName, String methodName, boolean hasIntentParam) {
75+
JDefinedClass anonymousReceiverClass = codeModel().anonymousClass(classes().BROADCAST_RECEIVER);
76+
JMethod onReceiveMethod = anonymousReceiverClass.method(PUBLIC, codeModel().VOID, "onReceive");
77+
onReceiveMethod.param(classes().CONTEXT, "context");
78+
JVar intentVar = onReceiveMethod.param(classes().INTENT, "intent");
79+
80+
JInvocation methodCall = onReceiveMethod.body().invoke(methodName);
81+
if (hasIntentParam) {
82+
methodCall.arg(intentVar);
83+
}
84+
85+
JExpression receiverInit = _new(anonymousReceiverClass);
86+
return holder.getGeneratedClass().field(PRIVATE | FINAL, classes().BROADCAST_RECEIVER, receiverName, receiverInit);
87+
}
88+
89+
private void registerAndUnregisterReceiver(HasReceiverRegistration holder, Receiver.RegisterAt registerAt, JFieldVar intentFilterField, JFieldVar receiverField, boolean local) {
90+
JBlock registerBlock = null, unregisterBlock = null;
91+
switch (registerAt) {
92+
case OnCreateOnDestroy:
93+
registerBlock = holder.getOnCreateAfterSuperBlock();
94+
unregisterBlock = holder.getOnDestroyBeforeSuperBlock();
95+
break;
96+
case OnStartOnStop:
97+
registerBlock = holder.getOnStartAfterSuperBlock();
98+
unregisterBlock = holder.getOnStopBeforeSuperBlock();
99+
break;
100+
case OnResumeOnPause:
101+
registerBlock = holder.getOnResumeAfterSuperBlock();
102+
unregisterBlock = holder.getOnPauseBeforeSuperBlock();
103+
break;
104+
case OnAttachOnDetach:
105+
registerBlock = holder.getOnAttachAfterSuperBlock();
106+
unregisterBlock = holder.getOnDetachBeforeSuperBlock();
107+
}
108+
109+
JExpression broadcastManager;
110+
if (local) {
111+
broadcastManager = classes().LOCAL_BROADCAST_MANAGER.staticInvoke("getInstance").arg(holder.getContextRef());
112+
} else {
113+
broadcastManager = holder.getContextRef();
114+
}
115+
116+
registerBlock.invoke(broadcastManager, "registerReceiver").arg(receiverField).arg(intentFilterField);
117+
unregisterBlock.invoke(broadcastManager, "unregisterReceiver").arg(receiverField);
118+
}
119+
}

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/CanonicalNameConstants.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public final class CanonicalNameConstants {
5252
public static final String LOG = "android.util.Log";
5353
public static final String PARCELABLE = "android.os.Parcelable";
5454
public static final String INTENT = "android.content.Intent";
55+
public static final String INTENT_FILTER = "android.content.IntentFilter";
5556
public static final String COMPONENT_NAME = "android.content.ComponentName";
5657
public static final String BUNDLE = "android.os.Bundle";
5758
public static final String APPLICATION = "android.app.Application";
@@ -95,6 +96,7 @@ public final class CanonicalNameConstants {
9596
public static final String SERVICE = "android.app.Service";
9697
public static final String INTENT_SERVICE = "android.app.IntentService";
9798
public static final String BROADCAST_RECEIVER = "android.content.BroadcastReceiver";
99+
public static final String LOCAL_BROADCAST_MANAGER = "android.support.v4.content.LocalBroadcastManager";
98100
public static final String CONTENT_PROVIDER = "android.content.ContentProvider";
99101
public static final String SQLITE_DATABASE = "android.database.sqlite.SQLiteDatabase";
100102
public static final String KEY_STORE = "java.security.KeyStore";

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java‎

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,7 @@
2929
import static org.androidannotations.helper.ModelConstants.VALID_ENHANCED_VIEW_SUPPORT_ANNOTATIONS;
3030

3131
import java.lang.annotation.Annotation;
32-
import java.util.ArrayList;
33-
import java.util.Arrays;
34-
import java.util.Collection;
35-
import java.util.List;
36-
import java.util.Set;
37-
import java.util.TreeSet;
32+
import java.util.*;
3833

3934
import javax.lang.model.element.AnnotationMirror;
4035
import javax.lang.model.element.Element;
@@ -51,13 +46,7 @@
5146
import javax.lang.model.util.ElementFilter;
5247
import javax.lang.model.util.Elements;
5348

54-
import org.androidannotations.annotations.EActivity;
55-
import org.androidannotations.annotations.EBean;
56-
import org.androidannotations.annotations.EFragment;
57-
import org.androidannotations.annotations.EIntentService;
58-
import org.androidannotations.annotations.ServiceAction;
59-
import org.androidannotations.annotations.Trace;
60-
import org.androidannotations.annotations.ViewById;
49+
import org.androidannotations.annotations.*;
6150
import org.androidannotations.annotations.rest.Delete;
6251
import org.androidannotations.annotations.rest.Get;
6352
import org.androidannotations.annotations.rest.Head;
@@ -100,6 +89,11 @@ public class ValidatorHelper {
10089

10190
private static final Collection<Integer> VALID_LOG_LEVELS = Arrays.asList(LOG_VERBOSE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR);
10291

92+
private static final List<Receiver.RegisterAt> VALID_ACTIVITY_REGISTER_AT = Arrays.asList(Receiver.RegisterAt.OnCreateOnDestroy, Receiver.RegisterAt.OnResumeOnPause, Receiver.RegisterAt.OnStartOnStop);
93+
private static final List<Receiver.RegisterAt> VALID_SERVICE_REGISTER_AT = Arrays.asList(Receiver.RegisterAt.OnCreateOnDestroy);
94+
private static final List<Receiver.RegisterAt> VALID_FRAGMENT_REGISTER_AT = Arrays.asList(Receiver.RegisterAt.OnCreateOnDestroy, Receiver.RegisterAt.OnResumeOnPause, Receiver.RegisterAt.OnStartOnStop, Receiver.RegisterAt.OnAttachOnDetach);
95+
96+
10397
protected final TargetAnnotationHelper annotationHelper;
10498

10599
public final ValidatorParameterHelper param;
@@ -196,6 +190,13 @@ public void enclosingElementHasEActivityOrEFragment(Element element, AnnotationE
196190
hasOneOfClassAnnotations(element, enclosingElement, validatedElements, validAnnotations, valid);
197191
}
198192

193+
public void enclosingElementHasEActivityOrEFragmentOrEService(Element element, AnnotationElements validatedElements, IsValid valid) {
194+
Element enclosingElement = element.getEnclosingElement();
195+
@SuppressWarnings("unchecked")
196+
List<Class<? extends Annotation>> validAnnotations = asList(EActivity.class, EFragment.class, EService.class);
197+
hasOneOfClassAnnotations(element, enclosingElement, validatedElements, validAnnotations, valid);
198+
}
199+
199200
public void enclosingElementHasEFragment(Element element, AnnotationElements validatedElements, IsValid valid) {
200201
Element enclosingElement = element.getEnclosingElement();
201202
hasClassAnnotation(element, enclosingElement, validatedElements, EFragment.class, valid);
@@ -599,16 +600,21 @@ public void extendsOneOfTypes(Element element, List<String> typeQualifiedNames,
599600
}
600601

601602
public void extendsType(Element element, String typeQualifiedName, IsValid valid) {
603+
if (!extendsType(element, typeQualifiedName)) {
604+
valid.invalidate();
605+
annotationHelper.printAnnotationError(element, "%s can only be used on an element that extends " + typeQualifiedName);
606+
}
607+
}
608+
609+
private boolean extendsType(Element element, String typeQualifiedName) {
602610
TypeMirror elementType = element.asType();
603611

604612
TypeElement typeElement = annotationHelper.typeElementFromQualifiedName(typeQualifiedName);
605613
if (typeElement != null) {
606614
TypeMirror expectedType = typeElement.asType();
607-
if (!annotationHelper.isSubtype(elementType, expectedType)) {
608-
valid.invalidate();
609-
annotationHelper.printAnnotationError(element, "%s can only be used on an element that extends " + typeQualifiedName);
610-
}
615+
return annotationHelper.isSubtype(elementType, expectedType);
611616
}
617+
return false;
612618
}
613619

614620
public void allowedType(Element element, IsValid valid, TypeMirror fieldTypeMirror, List<String> allowedTypes) {
@@ -1325,11 +1331,42 @@ public void hasNotMultipleAnnotatedMethodWithSameName(Element element, IsValid v
13251331
String enclosedElementName = enclosedElement.getSimpleName().toString();
13261332
if (actionNames.contains(enclosedElementName)) {
13271333
valid.invalidate();
1328-
annotationHelper.printError(enclosedElement, "The " + TargetAnnotationHelper.annotationName(ServiceAction.class) + " annotated method must have unique name even if the signature is not the same");
1334+
annotationHelper.printError(enclosedElement, "The " + TargetAnnotationHelper.annotationName(annotation) + " annotated method must have unique name even if the signature is not the same");
13291335
} else {
13301336
actionNames.add(enclosedElementName);
13311337
}
13321338
}
13331339
}
13341340

1341+
public void hasRightRegisterAtValueDependingOnEnclosingElement(Element element, IsValid valid) {
1342+
Element enclosingElement = element.getEnclosingElement();
1343+
Receiver.RegisterAt registerAt = element.getAnnotation(Receiver.class).registerAt();
1344+
1345+
Map<String, List<Receiver.RegisterAt>> validRegisterAts = new HashMap<String, List<Receiver.RegisterAt>>();
1346+
validRegisterAts.put(CanonicalNameConstants.ACTIVITY, VALID_ACTIVITY_REGISTER_AT);
1347+
validRegisterAts.put(CanonicalNameConstants.SERVICE, VALID_SERVICE_REGISTER_AT);
1348+
validRegisterAts.put(CanonicalNameConstants.FRAGMENT, VALID_FRAGMENT_REGISTER_AT);
1349+
1350+
for (Map.Entry<String, List<Receiver.RegisterAt>> validRegisterAt : validRegisterAts.entrySet()) {
1351+
String enclosingType = validRegisterAt.getKey();
1352+
Collection<Receiver.RegisterAt> validRegisterAtValues = validRegisterAt.getValue();
1353+
if(extendsType(enclosingElement, enclosingType) && !validRegisterAtValues.contains(registerAt)) {
1354+
valid.invalidate();
1355+
annotationHelper.printAnnotationError(element, "The parameter registerAt of @Receiver in "+enclosingType+" can only be one of the following values : "+validRegisterAtValues);
1356+
}
1357+
}
1358+
}
1359+
1360+
public void hasSupportV4JarIfLocal(Element element, IsValid valid) {
1361+
boolean local = element.getAnnotation(Receiver.class).local();
1362+
if (local) {
1363+
1364+
Elements elementUtils = annotationHelper.getElementUtils();
1365+
if (elementUtils.getTypeElement(CanonicalNameConstants.LOCAL_BROADCAST_MANAGER) == null) {
1366+
valid.invalidate();
1367+
annotationHelper.printAnnotationError(element, "To use the LocalBroadcastManager, you MUST include the android-support-v4 jar");
1368+
}
1369+
}
1370+
}
1371+
13351372
}

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorParameterHelper.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ public void zeroOrOneMenuItemParameter(ExecutableElement executableElement, IsVa
6363
zeroOrOneSpecificParameter(executableElement, ANDROID_SHERLOCK_MENU_ITEM_QUALIFIED_NAMES, valid);
6464
}
6565

66+
public void zeroOrOneIntentParameter(ExecutableElement executableElement, IsValid isValid) {
67+
zeroOrOneSpecificParameter(executableElement, CanonicalNameConstants.INTENT, isValid);
68+
}
69+
6670
public void zeroOrOneSpecificParameter(ExecutableElement executableElement, String parameterTypeQualifiedName, IsValid valid) {
6771
zeroOrOneSpecificParameter(executableElement, Arrays.asList(parameterTypeQualifiedName), valid);
6872
}

0 commit comments

Comments
 (0)