[RN] Reduce maintenance

JitsiMeetViewListener currently has methods of one and the same pattern
so adding new methods i.e. events i.e. redux action types is a question
of repetition in the Java source code. Speed up the support of new
events by trying to deal with them in a generic way.

The same goes for JitsiMeetViewDelegate.
This commit is contained in:
Lyubo Marinov 2017-09-06 13:00:34 -05:00
parent 6982506acc
commit 1d8ee9d32f
2 changed files with 88 additions and 38 deletions

View File

@ -25,7 +25,12 @@ import com.facebook.react.bridge.ReadableMapKeySetIterator;
import org.jitsi.meet.sdk.JitsiMeetView;
import org.jitsi.meet.sdk.JitsiMeetViewListener;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
/**
* Module implementing a simple API to enable a proximity sensor-controlled
@ -35,10 +40,60 @@ import java.util.HashMap;
*/
class ExternalAPIModule extends ReactContextBaseJavaModule {
/**
* React Native module name.
* The {@code Method}s of {@code JitsiMeetViewListener} by event name i.e.
* redux action types.
*/
private static final Map<String, Method> JITSI_MEET_VIEW_LISTENER_METHODS
= new HashMap<>();
/**
* The name of this module to be used in the React Native bridge.
*/
private static final String MODULE_NAME = "ExternalAPI";
static {
// Figure out the mapping between the JitsiMeetViewListener methods
// and the events i.e. redux action types.
Pattern onPattern = Pattern.compile("^on[A-Z]+");
Pattern camelcasePattern = Pattern.compile("([a-z0-9]+)([A-Z0-9]+)");
for (Method method : JitsiMeetViewListener.class.getDeclaredMethods()) {
// * The method must be public (because it is declared by an
// interface).
// * The method must be/return void.
if (!Modifier.isPublic(method.getModifiers())
|| !Void.TYPE.equals(method.getReturnType())) {
continue;
}
// * The method name must start with "on" followed by a
// capital/uppercase letter (in agreement with the camelcase
// coding style customary to Java in general and the projects of
// the Jitsi community in particular).
String name = method.getName();
if (!onPattern.matcher(name).find()) {
continue;
}
// * The method must accept/have exactly 1 parameter of a type
// assignable from HashMap.
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 1
|| !parameterTypes[0].isAssignableFrom(HashMap.class)) {
continue;
}
// Convert the method name to an event name.
name
= camelcasePattern.matcher(name.substring(2))
.replaceAll("$1_$2")
.toUpperCase(Locale.ROOT);
JITSI_MEET_VIEW_LISTENER_METHODS.put(name, method);
}
}
/**
* Initializes a new module instance. There shall be a single instance of
* this module throughout the lifetime of the application.
@ -85,26 +140,14 @@ class ExternalAPIModule extends ReactContextBaseJavaModule {
return;
}
switch (name) {
case "CONFERENCE_FAILED":
listener.onConferenceFailed(toHashMap(data));
break;
Method method = JITSI_MEET_VIEW_LISTENER_METHODS.get(name);
case "CONFERENCE_JOINED":
listener.onConferenceJoined(toHashMap(data));
break;
case "CONFERENCE_LEFT":
listener.onConferenceLeft(toHashMap(data));
break;
case "CONFERENCE_WILL_JOIN":
listener.onConferenceWillJoin(toHashMap(data));
break;
case "CONFERENCE_WILL_LEAVE":
listener.onConferenceWillLeave(toHashMap(data));
break;
if (method != null) {
try {
method.invoke(listener, toHashMap(data));
} catch (ReflectiveOperationException roe) {
throw new RuntimeException(roe);
}
}
}

View File

@ -52,26 +52,33 @@ RCT_EXPORT_METHOD(sendEvent:(NSString *)name
return;
}
if ([name isEqualToString:@"CONFERENCE_FAILED"]
&& [delegate respondsToSelector:@selector(conferenceFailed:)]) {
[delegate conferenceFailed:data];
SEL sel = NSSelectorFromString([self methodNameFromEventName:name]);
} else if ([name isEqualToString:@"CONFERENCE_JOINED"]
&& [delegate respondsToSelector:@selector(conferenceJoined:)]) {
[delegate conferenceJoined:data];
} else if ([name isEqualToString:@"CONFERENCE_LEFT"]
&& [delegate respondsToSelector:@selector(conferenceLeft:)]) {
[delegate conferenceLeft:data];
} else if ([name isEqualToString:@"CONFERENCE_WILL_JOIN"]
&& [delegate respondsToSelector:@selector(conferenceWillJoin:)]) {
[delegate conferenceWillJoin:data];
} else if ([name isEqualToString:@"CONFERENCE_WILL_LEAVE"]
&& [delegate respondsToSelector:@selector(conferenceWillLeave:)]) {
[delegate conferenceWillLeave:data];
if (sel && [delegate respondsToSelector:sel]) {
[delegate performSelector:sel withObject:data];
}
}
/**
* Converts a specific event name i.e. redux action type description to a
* method name.
*
* @param eventName The event name to convert to a method name.
* @return A method name constructed from the specified {@code eventName}.
*/
- (NSString *)methodNameFromEventName:(NSString *)eventName {
NSMutableString *methodName
= [NSMutableString stringWithCapacity:eventName.length];
for (NSString *c in [eventName componentsSeparatedByString:@"_"]) {
if (c.length) {
[methodName appendString:
methodName.length ? c.capitalizedString : c.lowercaseString];
}
}
[methodName appendString:@":"];
return methodName;
}
@end