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