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.haptics.HapticsModule 30 import expo.modules.keepawake.KeepAwakeModule 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 BarCodeScannerPackage(), 160 KeepAwakePackage(), 161 FaceDetectorPackage(), 162 NotificationsPackage(), // home doesn't use notifications, but we want the singleton modules created 163 TaskManagerPackage(), // load expo-task-manager to restore tasks once the client is opened 164 SplashScreenPackage() 165 ) 166 } 167 168 override fun getModulesList(): List<Class<out Module>> { 169 return listOf( 170 BarCodeScannerModule::class.java, 171 BlurModule::class.java, 172 CameraViewModule::class.java, 173 ClipboardModule::class.java, 174 ConstantsModule::class.java, 175 DeviceModule::class.java, 176 EASClientModule::class.java, 177 FileSystemModule::class.java, 178 HapticsModule::class.java, 179 KeepAwakeModule::class.java, 180 LinearGradientModule::class.java, 181 SplashScreenModule::class.java, 182 WebBrowserModule::class.java, 183 ) 184 } 185 } 186 } 187