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