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