-
-
Save ztl8702/7e1d2ec455727ef8d4127dc8ef53ccbf to your computer and use it in GitHub Desktop.
JitsiMeetHolderImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.app.Activity; | |
import android.content.Intent; | |
import android.view.View; | |
/**redacted**/ | |
import java.util.Map; | |
import javax.inject.Inject; | |
import org.jitsi.meet.sdk.JitsiMeetView; | |
import org.jitsi.meet.sdk.JitsiMeetViewListener; | |
/** | |
* Implementation of {@link JitsiMeetHolder}. | |
*/ | |
public class JitsiMeetHolderImpl implements JitsiMeetHolder { | |
private static final String CONFIG_JITSI_MEET_URL = "JITSI_MEET_URL"; | |
private static final String CONFIG_JITSI_MEET_CONFERENCE_PREFIX | |
= "JITSI_MEET_CONFERENCE_PREFIX"; | |
private static final String CONFIG_JITSI_MEET_FAKE_CALL = "JITSI_MEET_FAKE_CALL"; | |
private JitsiMeetView view = null; | |
private Activity lastActivity; | |
private boolean lastActivityDead = false; | |
private String jitsiMeetServer; | |
private String jitstMeetConferencePrefix; | |
/** | |
* If this is set to true, do not actually join a Jitsi Conference. | |
* For debug. | |
*/ | |
private boolean fakeCall = false; | |
private boolean configLoaded = false; | |
private final LoggerInterface logger; | |
private final PubSubHub pubSubHub; | |
private String activeRoomId = null; | |
private boolean hasPendingStartRequest = false; | |
private boolean hasPendingStopRequest = false; | |
private JitsiStartArgs pendingStartArgs = null; | |
@Inject | |
public JitsiMeetHolderImpl(LoggerFactory loggerFactory, PubSubHub pubSubHub | |
) { | |
logger = loggerFactory.create(this.getClass().getSimpleName()); | |
this.pubSubHub = pubSubHub; | |
} | |
@Override | |
public void initialise(Activity activityContext) { | |
if (view == null) { | |
activityContext.runOnUiThread(() -> { | |
view = new JitsiMeetView(activityContext); | |
view.setListener(new JitsiMeetViewListener() { | |
@Override | |
public void onConferenceFailed(Map<String, Object> map) { | |
pubSubHub.publish(PubSubTopics.JITSI_FAILED, null); | |
} | |
@Override | |
public void onConferenceJoined(Map<String, Object> map) { | |
pubSubHub.publish(PubSubTopics.JITSI_JOINED, null); | |
} | |
@Override | |
public void onConferenceLeft(Map<String, Object> map) { | |
pubSubHub.publish(PubSubTopics.JITSI_LEFT, null); | |
} | |
@Override | |
public void onConferenceWillJoin(Map<String, Object> map) { | |
} | |
@Override | |
public void onConferenceWillLeave(Map<String, Object> map) { | |
} | |
@Override | |
public void onLoadConfigError(Map<String, Object> map) { | |
pubSubHub.publish(PubSubTopics.JITSI_FAILED, null); | |
} | |
}); | |
view.loadURLString(""); | |
}); | |
} | |
} | |
@Override | |
public synchronized void requestCallStart(JitsiStartArgs args) { | |
if (args == null) { | |
return; | |
} | |
if (lastActivityDead || lastActivity == null) { | |
logger.warn("Jitsi cannot start now because activity is not in foreground. " | |
+ "However the request has been noted."); | |
hasPendingStartRequest = true; | |
pendingStartArgs = args; | |
hasPendingStopRequest = false; | |
return; | |
} | |
doCallStart(args); | |
} | |
private void doCallStart(JitsiStartArgs args) { | |
hasPendingStartRequest = false; | |
if (view == null) { | |
logger.warn("Jitsi Meet not initialised"); | |
initialise(lastActivity); | |
} | |
pubSubHub.publish(PubSubTopics.JITSI_IDLE, null); | |
assertConfigLoaded(); | |
String jitsiConferenceUrl = | |
String.format("%s%s%s#config.startAudioOnly=true", | |
jitsiMeetServer, jitstMeetConferencePrefix, args.getRoom()); | |
logger.info("Joining Jitsi Conference at " + jitsiConferenceUrl); | |
if (activeRoomId != null && activeRoomId.equals(args.getRoom())) { | |
logger.info("The requested Jitsi Conference is already on."); | |
return; | |
} | |
activeRoomId = args.getRoom(); | |
if (fakeCall) { | |
logger.warn("Debug mode. Not actually joining Jitsi Conference."); | |
} else { | |
lastActivity.runOnUiThread(() -> view.loadURLString(jitsiConferenceUrl)); | |
} | |
} | |
@Override | |
public void requestCallStop() { | |
if (lastActivityDead || lastActivity == null) { | |
logger.warn("Jitsi cannot stop now because activity is not in foreground. " | |
+ "However the request has been noted."); | |
hasPendingStartRequest = false; | |
hasPendingStopRequest = true; | |
return; | |
} | |
doCallStop(); | |
} | |
private void doCallStop() { | |
hasPendingStartRequest = false; | |
if (view == null) { | |
logger.warn("requestCallStop: Jitsi Meet not initialsed."); | |
return; | |
} | |
logger.info("Stopping Jitsi call..."); | |
lastActivity.runOnUiThread(() -> { | |
if (view != null) { | |
view.dispose(); | |
view = null; | |
} | |
}); | |
activeRoomId = null; | |
pubSubHub.publish(PubSubTopics.JITSI_STOPPED, null); | |
} | |
@Override | |
public void destroy() { | |
if (view != null) { | |
logger.info("Releasing Jitsi Meet View."); | |
view.dispose(); | |
activeRoomId = null; | |
view = null; | |
if (lastActivityDead && lastActivity != null) { | |
logger.info("Releasing dead Activity since Jitsi has stopped."); | |
lastActivity = null; | |
} | |
} | |
} | |
@Override | |
public void onActivityResume(Activity activityContext) { | |
if (activityContext != null) { | |
lastActivity = activityContext; | |
JitsiMeetView.onHostResume(activityContext); | |
lastActivityDead = false; | |
if (hasPendingStartRequest) { | |
doCallStart(pendingStartArgs); | |
} else if (hasPendingStopRequest) { | |
doCallStop(); | |
} | |
} | |
} | |
@Override | |
public void onActivityStop(Activity activityContext) { | |
if (activityContext == lastActivity) { | |
if (view != null) { | |
// Jitsi is ongoing. We don't release lastActivity. | |
// This is a hacky approach to keep Jitsi Meet (i.e. the ReactInstance) running | |
// while the app in the background. | |
// It prevents the GC from collecting the old Activity object, until a new Activity | |
// has been created. | |
// May cause memory leaks. But should not be too bad for now. | |
lastActivityDead = true; | |
logger.info("Suppressing Activity Stop event to keep Jitsi running..."); | |
} else { | |
JitsiMeetView.onHostPause(lastActivity); | |
lastActivity = null; | |
} | |
} | |
} | |
@Override | |
public void onApplicationTerminate() { | |
if (lastActivity != null) { | |
destroy(); | |
JitsiMeetView.onHostDestroy(lastActivity); | |
lastActivity = null; | |
} | |
} | |
@Override | |
public void onNewIntent(Intent intent) { | |
JitsiMeetView.onNewIntent(intent); | |
} | |
@Override | |
public void onUserLeaveHint() { | |
if (view != null) { | |
logger.info("User leave with ongoing Jitsi call. Triggering Picture-in-Picture..."); | |
// We don't need Picture-in-Picture to keep application alive, | |
// since we have a foreground service. | |
//view.onUserLeaveHint(); | |
} else { | |
logger.info("User leave. But we don't have an ongoing Jitsi call. Ignoring..."); | |
} | |
} | |
@Override | |
public View getJitsiMeetView() { | |
return null; | |
} | |
@Override | |
public void loadConfig() { | |
if (!configLoaded) { | |
logger.info("Loading configuration..."); | |
ConfigurationManager configMan = ConfigurationManager.getInstance(); | |
jitsiMeetServer = configMan.getProperty(CONFIG_JITSI_MEET_URL); | |
jitstMeetConferencePrefix = configMan.getProperty(CONFIG_JITSI_MEET_CONFERENCE_PREFIX); | |
fakeCall = configMan.getBooleanPropery(CONFIG_JITSI_MEET_FAKE_CALL); | |
configLoaded = true; | |
} else { | |
logger.warn("Configuration is already loaded."); | |
} | |
} | |
private void assertConfigLoaded() { | |
if (!configLoaded) { | |
throw new IllegalStateException("Configuration not loaded"); | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment