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