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