1 // Copyright 2015-present 650 Industries. All rights reserved. 2 package host.exp.exponent.experience 3 4 import android.Manifest 5 import android.content.Intent 6 import android.content.pm.PackageManager 7 import android.os.Bundle 8 import android.os.Debug 9 import android.view.View 10 import androidx.core.content.ContextCompat 11 import com.facebook.react.ReactRootView 12 import com.facebook.soloader.SoLoader 13 import com.squareup.leakcanary.LeakCanary 14 import de.greenrobot.event.EventBus 15 import expo.modules.barcodescanner.BarCodeScannerModule 16 import expo.modules.barcodescanner.BarCodeScannerPackage 17 import expo.modules.blur.BlurModule 18 import expo.modules.camera.CameraViewModule 19 import expo.modules.clipboard.ClipboardModule 20 import expo.modules.constants.ConstantsModule 21 import expo.modules.constants.ConstantsPackage 22 import expo.modules.core.interfaces.Package 23 import expo.modules.device.DeviceModule 24 import expo.modules.easclient.EASClientModule 25 import expo.modules.facedetector.FaceDetectorPackage 26 import expo.modules.filesystem.FileSystemModule 27 import expo.modules.filesystem.FileSystemPackage 28 import expo.modules.haptics.HapticsModule 29 import expo.modules.keepawake.KeepAwakeModule 30 import expo.modules.keepawake.KeepAwakePackage 31 import expo.modules.kotlin.ModulesProvider 32 import expo.modules.kotlin.modules.Module 33 import expo.modules.lineargradient.LinearGradientModule 34 import expo.modules.notifications.NotificationsPackage 35 import expo.modules.splashscreen.SplashScreenImageResizeMode 36 import expo.modules.splashscreen.SplashScreenModule 37 import expo.modules.splashscreen.SplashScreenPackage 38 import expo.modules.splashscreen.singletons.SplashScreen 39 import expo.modules.taskManager.TaskManagerPackage 40 import expo.modules.webbrowser.WebBrowserModule 41 import host.exp.exponent.Constants 42 import host.exp.exponent.ExponentManifest 43 import host.exp.exponent.RNObject 44 import host.exp.exponent.di.NativeModuleDepsProvider 45 import host.exp.exponent.kernel.ExperienceKey 46 import host.exp.exponent.kernel.Kernel.KernelStartedRunningEvent 47 import host.exp.exponent.utils.ExperienceActivityUtils 48 import host.exp.exponent.utils.ExperienceRTLManager 49 import host.exp.expoview.BuildConfig 50 import org.json.JSONException 51 import javax.inject.Inject 52 53 open class HomeActivity : BaseExperienceActivity() { 54 @Inject 55 lateinit var exponentManifest: ExponentManifest 56 57 //region Activity Lifecycle onCreatenull58 override fun onCreate(savedInstanceState: Bundle?) { 59 super.onCreate(savedInstanceState) 60 NativeModuleDepsProvider.instance.inject(HomeActivity::class.java, this) 61 62 sdkVersion = RNObject.UNVERSIONED 63 manifest = exponentManifest.getKernelManifestAndAssetRequestHeaders().manifest 64 experienceKey = try { 65 ExperienceKey.fromManifest(manifest!!) 66 } catch (e: JSONException) { 67 ExperienceKey("") 68 } 69 70 // @sjchmiela, @lukmccall: We are consciously not overriding UI mode in Home, because it has no effect. 71 // `ExpoAppearanceModule` with which `ExperienceActivityUtils#overrideUiMode` is compatible 72 // is disabled in Home as of end of 2020, to fix some issues with dev menu, see: 73 // https://github.com/expo/expo/blob/eb9bd274472e646a730fd535a4bcf360039cbd49/android/expoview/src/main/java/versioned/host/exp/exponent/ExponentPackage.java#L200-L207 74 // ExperienceActivityUtils.overrideUiMode(mExponentManifest.getKernelManifest(), this); 75 ExperienceActivityUtils.configureStatusBar(exponentManifest.getKernelManifestAndAssetRequestHeaders().manifest, this) 76 77 EventBus.getDefault().registerSticky(this) 78 kernel.startJSKernel(this) 79 80 ExperienceRTLManager.setSupportsRTL(this, false) 81 82 SplashScreen.show(this, SplashScreenImageResizeMode.NATIVE, ReactRootView::class.java, true) 83 84 tryInstallLeakCanary(true) 85 } 86 shouldCreateLoadingViewnull87 override fun shouldCreateLoadingView(): Boolean { 88 // Home app shouldn't show LoadingView as it indicates state when the app's manifest is being 89 // downloaded and Splash info is not yet available and this is not the case for Home app 90 // (Splash info is known from the start). 91 return false 92 } 93 onResumenull94 override fun onResume() { 95 super.onResume() 96 SoLoader.init(this, false) 97 } 98 //endregion Activity Lifecycle 99 /** 100 * This method has been split out from onDestroy lifecycle method to [ReactNativeActivity.destroyReactInstanceManager] 101 * and overridden here as we want to prevent destroying react instance manager when HomeActivity gets destroyed. 102 * It needs to continue to live since it is needed for DevMenu to work as expected (it relies on ExponentKernelModule from that react context). 103 */ destroyReactInstanceManagernull104 override fun destroyReactInstanceManager() {} 105 tryInstallLeakCanarynull106 private fun tryInstallLeakCanary(shouldAskForPermissions: Boolean) { 107 if (BuildConfig.DEBUG && Constants.ENABLE_LEAK_CANARY) { 108 // Leak canary needs WRITE_EXTERNAL_STORAGE permission 109 if (shouldAskForPermissions && ContextCompat.checkSelfPermission( 110 this, 111 Manifest.permission.WRITE_EXTERNAL_STORAGE 112 ) != PackageManager.PERMISSION_GRANTED 113 ) { 114 requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 1248919246) 115 } else { 116 LeakCanary.install(application) 117 } 118 } 119 } 120 onRequestPermissionsResultnull121 override fun onRequestPermissionsResult( 122 requestCode: Int, 123 permissions: Array<String>, 124 grantResults: IntArray 125 ) { 126 super.onRequestPermissionsResult(requestCode, permissions, grantResults) 127 tryInstallLeakCanary(false) 128 } 129 onEventMainThreadnull130 fun onEventMainThread(event: KernelStartedRunningEvent?) { 131 reactInstanceManager.assign(kernel.reactInstanceManager) 132 reactRootView.assign(kernel.reactRootView) 133 reactInstanceManager.onHostResume(this, this) 134 setReactRootView((reactRootView.get() as View)) 135 finishLoading() 136 137 if (Constants.DEBUG_COLD_START_METHOD_TRACING) { 138 Debug.stopMethodTracing() 139 } 140 } 141 onErrornull142 override fun onError(intent: Intent) { 143 intent.putExtra(ErrorActivity.IS_HOME_KEY, true) 144 kernel.setHasError() 145 } 146 147 companion object : ModulesProvider { homeExpoPackagesnull148 fun homeExpoPackages(): List<Package> { 149 return listOf( 150 ConstantsPackage(), 151 FileSystemPackage(), 152 BarCodeScannerPackage(), 153 KeepAwakePackage(), 154 FaceDetectorPackage(), 155 NotificationsPackage(), // home doesn't use notifications, but we want the singleton modules created 156 TaskManagerPackage(), // load expo-task-manager to restore tasks once the client is opened 157 SplashScreenPackage() 158 ) 159 } 160 getModulesListnull161 override fun getModulesList(): List<Class<out Module>> { 162 return listOf( 163 BarCodeScannerModule::class.java, 164 BlurModule::class.java, 165 CameraViewModule::class.java, 166 ClipboardModule::class.java, 167 ConstantsModule::class.java, 168 DeviceModule::class.java, 169 EASClientModule::class.java, 170 FileSystemModule::class.java, 171 HapticsModule::class.java, 172 KeepAwakeModule::class.java, 173 LinearGradientModule::class.java, 174 SplashScreenModule::class.java, 175 WebBrowserModule::class.java, 176 ) 177 } 178 } 179 } 180