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