1 // Copyright 2015-present 650 Industries. All rights reserved. 2 package host.exp.exponent 3 4 import android.Manifest 5 import android.app.Activity 6 import android.app.ActivityManager.RecentTaskInfo 7 import android.content.Intent 8 import android.content.pm.PackageManager 9 import android.os.Build 10 import android.os.Bundle 11 import android.os.Handler 12 import androidx.core.content.ContextCompat 13 import host.exp.exponent.di.NativeModuleDepsProvider 14 import host.exp.exponent.kernel.Kernel 15 import host.exp.expoview.BuildConfig 16 import javax.inject.Inject 17 18 // This activity is transparent. It uses android:style/Theme.Translucent.NoTitleBar. 19 // Calls finish() once it is done processing Intent. 20 class LauncherActivity : Activity() { 21 @Inject 22 lateinit var kernel: Kernel 23 24 override fun onCreate(savedInstanceState: Bundle?) { 25 super.onCreate(savedInstanceState) 26 if (BuildConfig.DEBUG) { 27 // Need WRITE_EXTERNAL_STORAGE for method tracing 28 if (Constants.DEBUG_METHOD_TRACING) { 29 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 30 if (ContextCompat.checkSelfPermission( 31 this, 32 Manifest.permission.WRITE_EXTERNAL_STORAGE 33 ) != PackageManager.PERMISSION_GRANTED 34 ) { 35 requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 123) 36 } 37 } 38 } 39 } 40 NativeModuleDepsProvider.instance.inject(LauncherActivity::class.java, this) 41 42 // Kernel's JS needs to be started for the dev menu to work when the app is launched through the deep link. 43 kernel.startJSKernel(this) 44 kernel.handleIntent(this, intent) 45 46 // Start a service to keep our process awake. This isn't necessary most of the time, but 47 // if the user has "Don't keep activities" on it's possible for the process to exit in between 48 // finishing this activity and starting the BaseExperienceActivity. 49 startService(ExponentIntentService.getActionStayAwake(applicationContext)) 50 51 // Delay to prevent race condition where finish() is called before service starts. 52 Handler(mainLooper).postDelayed( 53 Runnable { 54 try { 55 // Crash with NoSuchFieldException instead of hard crashing at task.getTaskInfo().numActivities 56 RecentTaskInfo::class.java.getDeclaredField("numActivities") 57 for (task in kernel.tasks) { 58 if (task.taskInfo.id == taskId) { 59 if (task.taskInfo.numActivities == 1) { 60 finishAndRemoveTask() 61 return@Runnable 62 } else { 63 break 64 } 65 } 66 } 67 } catch (e: Exception) { 68 // just go straight to finish() 69 } 70 finish() 71 }, 72 100 73 ) 74 } 75 76 public override fun onNewIntent(intent: Intent) { 77 super.onNewIntent(intent) 78 79 // We shouldn't ever get here, since we call finish() in onCreate. Just want to be safe 80 // since this Activity is singleTask and there might be some edge case where this is called. 81 kernel.handleIntent(this, intent) 82 } 83 } 84