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