diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a82b9f9a2..27122663e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ android:extractNativeLibs="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" + android:name=".MainApplication" android:networkSecurityConfig="@xml/network_security_config" android:theme="@style/AppTheme"> { @NonNull @Override public Boolean create(@NonNull Context context) { + Log.d(this.getClass().getCanonicalName(), "create"); + SoLoader.init(context, /* native exopackage */ false); // Register our uncaught exception handler. diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiReactNativeHost.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiReactNativeHost.java new file mode 100644 index 000000000..2237151a1 --- /dev/null +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiReactNativeHost.java @@ -0,0 +1,41 @@ +package org.jitsi.meet.sdk; + +import android.app.Application; + +import com.facebook.react.ReactInstanceManager; +import com.facebook.react.ReactNativeHost; +import com.facebook.react.ReactPackage; + +import java.util.List; + +/** + * This is the minimal implementation of ReactNativeHost that will make things like the + * Detox testing framework believe we are a "greenfield" app. + * + * Generally speaking, apps using the SDK (other than the Jitsi Meet app itself) should not + * need to use this because the + */ +public class JitsiReactNativeHost extends ReactNativeHost { + public JitsiReactNativeHost(Application application) { + super(application); + } + + @Override + public boolean getUseDeveloperSupport() { + // Unused since we override `createReactInstanceManager`. + return false; + } + + @Override + protected List getPackages() { + // Unused since we override `createReactInstanceManager`. + return null; + } + + @Override + protected ReactInstanceManager createReactInstanceManager() { + ReactInstanceManagerHolder.initReactInstanceManager(this.getApplication()); + + return ReactInstanceManagerHolder.getReactInstanceManager(); + } +} diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java index b84e364a8..ea6007aed 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java @@ -17,6 +17,8 @@ package org.jitsi.meet.sdk; import android.app.Activity; +import android.app.Application; +import android.util.Log; import androidx.annotation.Nullable; @@ -99,6 +101,69 @@ class ReactInstanceManagerHolder { ); } + static List getReactNativePackages() { + List packages + = new ArrayList<>(Arrays.asList( + new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(), + new com.ocetnik.timer.BackgroundTimerPackage(), + new com.calendarevents.RNCalendarEventsPackage(), + new com.corbt.keepawake.KCKeepAwakePackage(), + new com.facebook.react.shell.MainReactPackage(), + new com.reactnativecommunity.clipboard.ClipboardPackage(), + new com.reactnativecommunity.netinfo.NetInfoPackage(), + new com.reactnativepagerview.PagerViewPackage(), + new com.oblador.performance.PerformancePackage(), + new com.reactnativecommunity.slider.ReactSliderPackage(), + new com.brentvatne.react.ReactVideoPackage(), + new com.swmansion.reanimated.ReanimatedPackage(), + new org.reactnative.maskedview.RNCMaskedViewPackage(), + new com.reactnativecommunity.webview.RNCWebViewPackage(), + new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(), + new com.learnium.RNDeviceInfo.RNDeviceInfo(), + new com.swmansion.gesturehandler.react.RNGestureHandlerPackage(), + new org.linusu.RNGetRandomValuesPackage(), + new com.rnimmersive.RNImmersivePackage(), + new com.swmansion.rnscreens.RNScreensPackage(), + new com.zmxv.RNSound.RNSoundPackage(), + new com.th3rdwave.safeareacontext.SafeAreaContextPackage(), + new com.horcrux.svg.SvgPackage(), + new ReactPackageAdapter() { + @Override + public List createNativeModules(ReactApplicationContext reactContext) { + return ReactInstanceManagerHolder.createNativeModules(reactContext); + } + @Override + public List createViewManagers(ReactApplicationContext reactContext) { + return ReactInstanceManagerHolder.createViewManagers(reactContext); + } + })); + + // AmplitudeReactNativePackage + try { + Class amplitudePackageClass = Class.forName("com.amplitude.reactnative.AmplitudeReactNativePackage"); + Constructor constructor = amplitudePackageClass.getConstructor(); + packages.add((ReactPackage)constructor.newInstance()); + } catch (Exception e) { + // Ignore any error, the module is not compiled when LIBRE_BUILD is enabled. + } + + // RNGoogleSignInPackage + try { + Class googlePackageClass = Class.forName("com.reactnativegooglesignin.RNGoogleSigninPackage"); + Constructor constructor = googlePackageClass.getConstructor(); + packages.add((ReactPackage)constructor.newInstance()); + } catch (Exception e) { + // Ignore any error, the module is not compiled when LIBRE_BUILD is enabled. + } + + return packages; + } + + static JSCExecutorFactory getReactNativeJSFactory() { + // Keep on using JSC, the jury is out on Hermes. + return new JSCExecutorFactory("", ""); + } + /** * Helper function to send an event to JavaScript. * @@ -159,6 +224,35 @@ class ReactInstanceManagerHolder { return reactInstanceManager; } + /** + * Internal method to initialize the React Native instance manager. We + * create a single instance in order to load the JavaScript bundle a single + * time. All {@code ReactRootView} instances will be tied to the one and + * only {@code ReactInstanceManager}. + * + * This method is only meant to be called when integrating with {@code JitsiReactNativeHost}. + * + * @param app {@code Application} current running Application. + */ + static void initReactInstanceManager(Application app) { + if (reactInstanceManager != null) { + return; + } + + Log.d(ReactInstanceManagerHolder.class.getCanonicalName(), "initializing RN with Application"); + + reactInstanceManager + = ReactInstanceManager.builder() + .setApplication(app) + .setBundleAssetName("index.android.bundle") + .setJSMainModulePath("index.android") + .setJavaScriptExecutorFactory(getReactNativeJSFactory()) + .addPackages(getReactNativePackages()) + .setUseDeveloperSupport(BuildConfig.DEBUG) + .setInitialLifecycleState(LifecycleState.BEFORE_CREATE) + .build(); + } + /** * Internal method to initialize the React Native instance manager. We * create a single instance in order to load the JavaScript bundle a single @@ -172,63 +266,7 @@ class ReactInstanceManagerHolder { return; } - List packages - = new ArrayList<>(Arrays.asList( - new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(), - new com.ocetnik.timer.BackgroundTimerPackage(), - new com.calendarevents.RNCalendarEventsPackage(), - new com.corbt.keepawake.KCKeepAwakePackage(), - new com.facebook.react.shell.MainReactPackage(), - new com.reactnativecommunity.clipboard.ClipboardPackage(), - new com.reactnativecommunity.netinfo.NetInfoPackage(), - new com.reactnativepagerview.PagerViewPackage(), - new com.oblador.performance.PerformancePackage(), - new com.reactnativecommunity.slider.ReactSliderPackage(), - new com.brentvatne.react.ReactVideoPackage(), - new com.swmansion.reanimated.ReanimatedPackage(), - new org.reactnative.maskedview.RNCMaskedViewPackage(), - new com.reactnativecommunity.webview.RNCWebViewPackage(), - new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(), - new com.learnium.RNDeviceInfo.RNDeviceInfo(), - new com.swmansion.gesturehandler.react.RNGestureHandlerPackage(), - new org.linusu.RNGetRandomValuesPackage(), - new com.rnimmersive.RNImmersivePackage(), - new com.swmansion.rnscreens.RNScreensPackage(), - new com.zmxv.RNSound.RNSoundPackage(), - new com.th3rdwave.safeareacontext.SafeAreaContextPackage(), - new com.horcrux.svg.SvgPackage(), - new ReactPackageAdapter() { - @Override - public List createNativeModules(ReactApplicationContext reactContext) { - return ReactInstanceManagerHolder.createNativeModules(reactContext); - } - @Override - public List createViewManagers(ReactApplicationContext reactContext) { - return ReactInstanceManagerHolder.createViewManagers(reactContext); - } - })); - - // AmplitudeReactNativePackage - try { - Class amplitudePackageClass = Class.forName("com.amplitude.reactnative.AmplitudeReactNativePackage"); - Constructor constructor = amplitudePackageClass.getConstructor(); - packages.add((ReactPackage)constructor.newInstance()); - } catch (Exception e) { - // Ignore any error, the module is not compiled when LIBRE_BUILD is enabled. - } - - // RNGoogleSigninPackage - try { - Class googlePackageClass = Class.forName("com.reactnativegooglesignin.RNGoogleSigninPackage"); - Constructor constructor = googlePackageClass.getConstructor(); - packages.add((ReactPackage)constructor.newInstance()); - } catch (Exception e) { - // Ignore any error, the module is not compiled when LIBRE_BUILD is enabled. - } - - // Keep on using JSC, the jury is out on Hermes. - JSCExecutorFactory jsFactory - = new JSCExecutorFactory("", ""); + Log.d(ReactInstanceManagerHolder.class.getCanonicalName(), "initializing RN with Activity"); reactInstanceManager = ReactInstanceManager.builder() @@ -236,8 +274,8 @@ class ReactInstanceManagerHolder { .setCurrentActivity(activity) .setBundleAssetName("index.android.bundle") .setJSMainModulePath("index.android") - .setJavaScriptExecutorFactory(jsFactory) - .addPackages(packages) + .setJavaScriptExecutorFactory(getReactNativeJSFactory()) + .addPackages(getReactNativePackages()) .setUseDeveloperSupport(BuildConfig.DEBUG) .setInitialLifecycleState(LifecycleState.RESUMED) .build();