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