1 package expo.modules.devlauncher.launcher.errors
2 
3 import android.app.Activity
4 import android.content.Context
5 import android.content.Intent
6 import android.os.Bundle
7 import androidx.fragment.app.FragmentActivity
8 import com.facebook.react.ReactActivity
9 import expo.modules.devlauncher.databinding.ErrorFragmentBinding
10 import expo.modules.devlauncher.koin.DevLauncherKoinComponent
11 import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
12 import kotlinx.coroutines.launch
13 import org.koin.core.component.inject
14 import java.lang.ref.WeakReference
15 
16 class DevLauncherErrorActivity :
17   FragmentActivity(), DevLauncherKoinComponent {
18 
19   private val controller: DevLauncherControllerInterface by inject()
20   private lateinit var binding: ErrorFragmentBinding
21   private val adapter = DevLauncherStackAdapter(this, null)
22 
onCreatenull23   override fun onCreate(savedInstanceState: Bundle?) {
24     super.onCreate(savedInstanceState)
25 
26     binding = ErrorFragmentBinding.inflate(layoutInflater)
27     binding.homeButton.setOnClickListener { this.launchHome() }
28     binding.reloadButton.setOnClickListener { this.reload() }
29 
30     synchronized(DevLauncherErrorActivity) {
31       val error = currentError
32       if (error != null) {
33         displayError(currentError!!)
34         currentError = null
35       } else {
36         finish()
37         return
38       }
39     }
40 
41     setContentView(binding.root)
42   }
43 
onResumenull44   override fun onResume() {
45     super.onResume()
46     openedErrorActivity = WeakReference(this)
47   }
48 
onPausenull49   override fun onPause() {
50     super.onPause()
51     openedErrorActivity = WeakReference(null)
52   }
53 
displayErrornull54   fun displayError(error: DevLauncherAppError) {
55     adapter.data = error
56 
57     binding.errorStack.let {
58       it.adapter = adapter
59       adapter.notifyDataSetChanged()
60     }
61     binding.errorDetails.text = error.message ?: "Unknown error"
62   }
63 
launchHomenull64   private fun launchHome() {
65     synchronized(DevLauncherErrorActivity) {
66       currentError = null
67     }
68 
69     controller.navigateToLauncher()
70   }
71 
reloadnull72   private fun reload() {
73     synchronized(DevLauncherErrorActivity) {
74       currentError = null
75     }
76 
77     val appUrl = controller.latestLoadedApp
78 
79     if (appUrl == null) {
80       controller.navigateToLauncher()
81       return
82     }
83 
84     controller.coroutineScope.launch {
85       controller
86         .loadApp(
87           appUrl,
88           controller.appHost.reactInstanceManager.currentReactContext?.currentActivity as? ReactActivity?
89         )
90     }
91   }
92 
onBackPressednull93   override fun onBackPressed() {}
94 
95   companion object {
96     private var openedErrorActivity = WeakReference<DevLauncherErrorActivity?>(null)
97     private var currentError: DevLauncherAppError? = null
98 
isVisiblenull99     fun isVisible(): Boolean {
100       val errorActivity = openedErrorActivity.get()
101       return !(errorActivity == null || errorActivity.isDestroyed || errorActivity.isFinishing)
102     }
103 
showErrorIfNotVisiblenull104     fun showErrorIfNotVisible(activity: Activity, error: DevLauncherAppError) {
105       val errorActivity = openedErrorActivity.get()
106       if (errorActivity == null || errorActivity.isDestroyed || errorActivity.isFinishing) {
107         synchronized(this) {
108           currentError = error
109         }
110 
111         activity.startActivity(
112           Intent(activity, DevLauncherErrorActivity::class.java)
113         )
114       }
115     }
116 
showErrornull117     fun showError(activity: Activity, error: DevLauncherAppError) {
118       val errorActivity = openedErrorActivity.get()
119       if (errorActivity == null || errorActivity.isDestroyed) {
120         synchronized(this) {
121           currentError = error
122         }
123 
124         activity.startActivity(
125           Intent(activity, DevLauncherErrorActivity::class.java)
126         )
127       } else {
128         errorActivity.displayError(error)
129       }
130     }
131 
showFatalErrornull132     fun showFatalError(context: Context, error: DevLauncherAppError) {
133       synchronized(this) {
134         currentError = error
135       }
136 
137       context.startActivity(
138         Intent(context, DevLauncherErrorActivity::class.java).apply {
139           addFlags(
140             Intent.FLAG_ACTIVITY_NEW_TASK or
141               Intent.FLAG_ACTIVITY_CLEAR_TASK or
142               Intent.FLAG_ACTIVITY_NO_ANIMATION
143           )
144         }
145       )
146     }
147   }
148 }
149