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