expo-dev-launcher 5.2.0-canary-20250713-8f814f8 → 5.2.0-canary-20250729-d8899ae

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/android/build.gradle +7 -3
  3. package/android/src/debug/AndroidManifest.xml +4 -2
  4. package/android/src/debug/java/expo/modules/devlauncher/DevLauncherController.kt +34 -23
  5. package/android/src/debug/java/expo/modules/devlauncher/DevLauncherPackageDelegate.kt +1 -3
  6. package/android/src/debug/java/expo/modules/devlauncher/compose/AuthActivity.kt +2 -2
  7. package/android/src/debug/java/expo/modules/devlauncher/compose/DevLauncherBottomTabsNavigator.kt +69 -20
  8. package/android/src/debug/java/expo/modules/devlauncher/compose/UpdatesModels.kt +14 -0
  9. package/android/src/debug/java/expo/modules/devlauncher/compose/models/BranchViewModel.kt +105 -0
  10. package/android/src/debug/java/expo/modules/devlauncher/compose/models/BranchesViewModel.kt +113 -0
  11. package/android/src/debug/java/expo/modules/devlauncher/compose/models/CrashReportModel.kt +22 -0
  12. package/android/src/debug/java/expo/modules/devlauncher/compose/models/ErrorViewModel.kt +50 -0
  13. package/android/src/debug/java/expo/modules/devlauncher/compose/models/HomeViewModel.kt +115 -0
  14. package/android/src/debug/java/expo/modules/devlauncher/compose/{ProfileViewModel.kt → models/ProfileViewModel.kt} +2 -1
  15. package/android/src/debug/java/expo/modules/devlauncher/compose/models/SettingsViewModel.kt +89 -0
  16. package/android/src/debug/java/expo/modules/devlauncher/compose/primitives/Accordion.kt +4 -4
  17. package/android/src/debug/java/expo/modules/devlauncher/compose/primitives/Asyncimage.kt +1 -2
  18. package/android/src/debug/java/expo/modules/devlauncher/compose/primitives/CircularProgressBar.kt +89 -0
  19. package/android/src/debug/java/expo/modules/devlauncher/compose/routes/CrashReport.kt +38 -0
  20. package/android/src/debug/java/expo/modules/devlauncher/compose/routes/Home.kt +32 -7
  21. package/android/src/debug/java/expo/modules/devlauncher/compose/routes/Profile.kt +2 -2
  22. package/android/src/debug/java/expo/modules/devlauncher/compose/routes/Routes.kt +20 -0
  23. package/android/src/debug/java/expo/modules/devlauncher/compose/routes/Settings.kt +7 -5
  24. package/android/src/debug/java/expo/modules/devlauncher/compose/routes/Updates.kt +81 -0
  25. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/BranchScreen.kt +234 -0
  26. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/BranchesScreen.kt +261 -0
  27. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/CrashReportScreen.kt +191 -0
  28. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/ErrorScreen.kt +79 -0
  29. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/HomeScreen.kt +251 -71
  30. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/NoUpdatesScreen.kt +72 -0
  31. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/SettingsScreen.kt +93 -11
  32. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/AccountAvatar.kt +3 -4
  33. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/AccountSelector.kt +8 -7
  34. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/ActionButton.kt +3 -1
  35. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/AppHeader.kt +38 -6
  36. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/BottomTabBar.kt +10 -6
  37. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/BottomTabButton.kt +6 -3
  38. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/DevelopmentServerHelp.kt +1 -5
  39. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/ProfileLayout.kt +4 -6
  40. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/RunningAppCard.kt +19 -27
  41. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/ScreenHeaderContainer.kt +2 -1
  42. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/SectionHeader.kt +0 -24
  43. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/ServerUrlInput.kt +79 -0
  44. package/android/src/debug/java/expo/modules/devlauncher/compose/utils/DateFormat.kt +40 -0
  45. package/android/src/debug/java/expo/modules/devlauncher/compose/utils/UpdatesUtils.kt +12 -0
  46. package/android/src/debug/java/expo/modules/devlauncher/compose/utils/WithIsLast.kt +32 -0
  47. package/android/src/debug/java/expo/modules/devlauncher/launcher/DevLauncherActivity.kt +11 -105
  48. package/android/src/debug/java/expo/modules/devlauncher/launcher/errors/DevLauncherErrorActivity.kt +27 -96
  49. package/android/src/debug/java/expo/modules/devlauncher/services/ApolloClientService.kt +53 -0
  50. package/android/src/debug/java/expo/modules/devlauncher/services/AppService.kt +82 -0
  51. package/android/src/debug/java/expo/modules/devlauncher/services/DependencyInjection.kt +17 -16
  52. package/android/src/debug/java/expo/modules/devlauncher/services/ErrorRegistryService.kt +13 -0
  53. package/android/src/debug/java/expo/modules/devlauncher/services/ImageLoaderService.kt +17 -5
  54. package/android/src/debug/java/expo/modules/devlauncher/services/PackagerService.kt +22 -5
  55. package/android/src/debug/java/expo/modules/devlauncher/services/SessionService.kt +1 -6
  56. package/android/src/debug/res/drawable-mdpi/qr_code.png +0 -0
  57. package/android/src/debug/res/drawable-night-mdpi/qr_code.png +0 -0
  58. package/android/src/debug/res/drawable-night-xhdpi/qr_code.png +0 -0
  59. package/android/src/debug/res/drawable-night-xxhdpi/qr_code.png +0 -0
  60. package/android/src/debug/res/drawable-xhdpi/qr_code.png +0 -0
  61. package/android/src/debug/res/drawable-xxhdpi/qr_code.png +0 -0
  62. package/android/src/main/AndroidManifest.xml +6 -1
  63. package/android/src/main/graphql/GetBranches.graphql +17 -5
  64. package/android/src/main/graphql/GetUpdates.graphql +0 -13
  65. package/android/src/main/java/com/facebook/react/devsupport/DevLauncherDevServerHelper.kt +3 -3
  66. package/android/src/main/java/expo/modules/devlauncher/launcher/DevLauncherControllerInterface.kt +0 -1
  67. package/android/src/main/java/expo/modules/devlauncher/launcher/DevLauncherRecentlyOpenedAppsRegistry.kt +10 -9
  68. package/android/src/main/java/expo/modules/devlauncher/launcher/errors/DevLauncherErrorRegistry.kt +7 -6
  69. package/android/src/main/java/expo/modules/devlauncher/splashscreen/DevLauncherSplashScreen.kt +0 -2
  70. package/android/src/main/res/values/styles.xml +0 -14
  71. package/android/src/release/java/expo/modules/devlauncher/DevLauncherController.kt +0 -3
  72. package/expo-module.config.json +0 -1
  73. package/ios/EXDevLauncherUtils.swift +0 -9
  74. package/package.json +4 -4
  75. package/android/src/debug/assets/expo_dev_launcher_android.bundle +0 -1418
  76. package/android/src/debug/java/expo/modules/devlauncher/compose/HomeViewModel.kt +0 -95
  77. package/android/src/debug/java/expo/modules/devlauncher/launcher/DevLauncherReactHost.kt +0 -104
  78. package/android/src/debug/java/expo/modules/devlauncher/launcher/DevLauncherReactNativeHost.kt +0 -65
  79. package/android/src/debug/java/expo/modules/devlauncher/modules/DevLauncherAuth.kt +0 -72
  80. package/android/src/debug/res/drawable-mdpi/__node_modules_reactnavigation_elements_lib_module_assets_backicon.png +0 -0
  81. package/android/src/debug/res/drawable-mdpi/__node_modules_reactnavigation_elements_lib_module_assets_backiconmask.png +0 -0
  82. package/android/src/debug/res/drawable-mdpi/__node_modules_reactnavigation_elements_lib_module_assets_clearicon.png +0 -0
  83. package/android/src/debug/res/drawable-mdpi/__node_modules_reactnavigation_elements_lib_module_assets_closeicon.png +0 -0
  84. package/android/src/debug/res/drawable-mdpi/__node_modules_reactnavigation_elements_lib_module_assets_searchicon.png +0 -0
  85. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_branchiconlight.png +0 -0
  86. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_checkiconlight.png +0 -0
  87. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_clipboardicon.png +0 -0
  88. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_debugicon.png +0 -0
  89. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_extensionsfilledactiveicon.png +0 -0
  90. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_extensionsfilledactiveiconlight.png +0 -0
  91. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_extensionsfilledinactiveicon.png +0 -0
  92. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_extensionsfilledinactiveiconlight.png +0 -0
  93. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_extensionsiconlight.png +0 -0
  94. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_homefilledactiveicon.png +0 -0
  95. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_homefilledactiveiconlight.png +0 -0
  96. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_homefilledinactiveiconlight.png +0 -0
  97. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_infoiconlight.png +0 -0
  98. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_inspectelementicon.png +0 -0
  99. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_loadingindicatoricon.png +0 -0
  100. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_logoicon.png +0 -0
  101. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_performanceicon.png +0 -0
  102. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_questionmarkicon.png +0 -0
  103. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_refreshicon.png +0 -0
  104. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_runicon.png +0 -0
  105. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_settingsfilledactiveicon.png +0 -0
  106. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_settingsfilledactiveiconlight.png +0 -0
  107. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_settingsfilledinactiveiconlight.png +0 -0
  108. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_showmenuatlaunchiconlight.png +0 -0
  109. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_threefingerlongpressiconlight.png +0 -0
  110. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_toolbaroverlayicon.png +0 -0
  111. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_updateiconlight.png +0 -0
  112. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_usericonlight.png +0 -0
  113. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_warningtriangleicon.png +0 -0
  114. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_warningtriangleiconlight.png +0 -0
  115. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_xiconlight.png +0 -0
  116. package/android/src/debug/res/drawable-xhdpi/__node_modules_reactnavigation_elements_lib_module_assets_backicon.png +0 -0
  117. package/android/src/debug/res/drawable-xhdpi/__node_modules_reactnavigation_elements_lib_module_assets_clearicon.png +0 -0
  118. package/android/src/debug/res/drawable-xhdpi/__node_modules_reactnavigation_elements_lib_module_assets_closeicon.png +0 -0
  119. package/android/src/debug/res/drawable-xhdpi/__node_modules_reactnavigation_elements_lib_module_assets_searchicon.png +0 -0
  120. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_branchiconlight.png +0 -0
  121. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_checkiconlight.png +0 -0
  122. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_clipboardicon.png +0 -0
  123. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_debugicon.png +0 -0
  124. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_extensionsfilledactiveicon.png +0 -0
  125. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_extensionsfilledactiveiconlight.png +0 -0
  126. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_extensionsfilledinactiveicon.png +0 -0
  127. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_extensionsfilledinactiveiconlight.png +0 -0
  128. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_extensionsiconlight.png +0 -0
  129. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_homefilledactiveicon.png +0 -0
  130. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_homefilledactiveiconlight.png +0 -0
  131. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_homefilledinactiveiconlight.png +0 -0
  132. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_infoiconlight.png +0 -0
  133. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_inspectelementicon.png +0 -0
  134. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_loadingindicatoricon.png +0 -0
  135. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_logoicon.png +0 -0
  136. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_performanceicon.png +0 -0
  137. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_questionmarkicon.png +0 -0
  138. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_refreshicon.png +0 -0
  139. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_runicon.png +0 -0
  140. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_settingsfilledactiveicon.png +0 -0
  141. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_settingsfilledactiveiconlight.png +0 -0
  142. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_settingsfilledinactiveiconlight.png +0 -0
  143. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_showmenuatlaunchiconlight.png +0 -0
  144. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_threefingerlongpressiconlight.png +0 -0
  145. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_toolbaroverlayicon.png +0 -0
  146. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_updateiconlight.png +0 -0
  147. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_usericonlight.png +0 -0
  148. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_warningtriangleicon.png +0 -0
  149. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_warningtriangleiconlight.png +0 -0
  150. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_xiconlight.png +0 -0
  151. package/android/src/debug/res/drawable-xxhdpi/__node_modules_reactnavigation_elements_lib_module_assets_backicon.png +0 -0
  152. package/android/src/debug/res/drawable-xxhdpi/__node_modules_reactnavigation_elements_lib_module_assets_clearicon.png +0 -0
  153. package/android/src/debug/res/drawable-xxhdpi/__node_modules_reactnavigation_elements_lib_module_assets_closeicon.png +0 -0
  154. package/android/src/debug/res/drawable-xxhdpi/__node_modules_reactnavigation_elements_lib_module_assets_searchicon.png +0 -0
  155. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_branchiconlight.png +0 -0
  156. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_checkiconlight.png +0 -0
  157. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_clipboardicon.png +0 -0
  158. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_debugicon.png +0 -0
  159. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_extensionsfilledactiveicon.png +0 -0
  160. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_extensionsfilledactiveiconlight.png +0 -0
  161. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_extensionsfilledinactiveicon.png +0 -0
  162. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_extensionsfilledinactiveiconlight.png +0 -0
  163. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_extensionsiconlight.png +0 -0
  164. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_homefilledactiveicon.png +0 -0
  165. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_homefilledactiveiconlight.png +0 -0
  166. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_homefilledinactiveiconlight.png +0 -0
  167. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_infoiconlight.png +0 -0
  168. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_inspectelementicon.png +0 -0
  169. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_loadingindicatoricon.png +0 -0
  170. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_logoicon.png +0 -0
  171. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_performanceicon.png +0 -0
  172. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_questionmarkicon.png +0 -0
  173. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_refreshicon.png +0 -0
  174. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_runicon.png +0 -0
  175. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_settingsfilledactiveicon.png +0 -0
  176. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_settingsfilledactiveiconlight.png +0 -0
  177. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_settingsfilledinactiveiconlight.png +0 -0
  178. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_showmenuatlaunchiconlight.png +0 -0
  179. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_threefingerlongpressiconlight.png +0 -0
  180. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_toolbaroverlayicon.png +0 -0
  181. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_updateiconlight.png +0 -0
  182. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_usericonlight.png +0 -0
  183. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_warningtriangleicon.png +0 -0
  184. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_warningtriangleiconlight.png +0 -0
  185. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_xiconlight.png +0 -0
  186. package/android/src/debug/res/drawable-xxxhdpi/__node_modules_reactnavigation_elements_lib_module_assets_backicon.png +0 -0
  187. package/android/src/debug/res/drawable-xxxhdpi/__node_modules_reactnavigation_elements_lib_module_assets_clearicon.png +0 -0
  188. package/android/src/debug/res/drawable-xxxhdpi/__node_modules_reactnavigation_elements_lib_module_assets_closeicon.png +0 -0
  189. package/android/src/debug/res/drawable-xxxhdpi/__node_modules_reactnavigation_elements_lib_module_assets_searchicon.png +0 -0
  190. package/android/src/main/java/expo/modules/devlauncher/modules/DevLauncherInternalModule.kt +0 -234
  191. package/android/src/main/res/drawable-mdpi/_expodevclientcomponents_assets_logoicon.png +0 -0
  192. package/android/src/main/res/drawable-xhdpi/_expodevclientcomponents_assets_logoicon.png +0 -0
  193. package/android/src/main/res/drawable-xxhdpi/_expodevclientcomponents_assets_logoicon.png +0 -0
  194. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_branchicon.png → branch_icon.png} +0 -0
  195. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_buildingicon.png → building_icon.png} +0 -0
  196. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_checkicon.png → check_icon.png} +0 -0
  197. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_chevronrighticon.png → chevron_right_icon.png} +0 -0
  198. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_extensionsicon.png → extensions_icon.png} +0 -0
  199. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_homefilledinactiveicon.png → home_icon.png} +0 -0
  200. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_infoicon.png → info_icon.png} +0 -0
  201. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_settingsfilledinactiveicon.png → settings_icon.png} +0 -0
  202. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_shakedeviceicon.png → shake_device_icon.png} +0 -0
  203. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_showmenuatlaunchicon.png → show_menu_at_launch_icon.png} +0 -0
  204. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_terminalicon.png → terminal_icon.png} +0 -0
  205. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_threefingerlongpressicon.png → three_finger_long_press_icon.png} +0 -0
  206. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_updateicon.png → update_icon.png} +0 -0
  207. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_usericon.png → user_icon.png} +0 -0
  208. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_xicon.png → x_icon.png} +0 -0
  209. /package/android/src/debug/res/{drawable-mdpi/_expodevclientcomponents_assets_shakedeviceiconlight.png → drawable-night-mdpi/shake_device_icon.png} +0 -0
  210. /package/android/src/debug/res/{drawable-xhdpi/_expodevclientcomponents_assets_shakedeviceiconlight.png → drawable-night-xhdpi/shake_device_icon.png} +0 -0
  211. /package/android/src/debug/res/{drawable-xxhdpi/_expodevclientcomponents_assets_shakedeviceiconlight.png → drawable-night-xxhdpi/shake_device_icon.png} +0 -0
  212. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_branchicon.png → branch_icon.png} +0 -0
  213. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_buildingicon.png → building_icon.png} +0 -0
  214. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_checkicon.png → check_icon.png} +0 -0
  215. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_chevronrighticon.png → chevron_right_icon.png} +0 -0
  216. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_extensionsicon.png → extensions_icon.png} +0 -0
  217. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_homefilledinactiveicon.png → home_icon.png} +0 -0
  218. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_infoicon.png → info_icon.png} +0 -0
  219. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_settingsfilledinactiveicon.png → settings_icon.png} +0 -0
  220. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_shakedeviceicon.png → shake_device_icon.png} +0 -0
  221. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_showmenuatlaunchicon.png → show_menu_at_launch_icon.png} +0 -0
  222. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_terminalicon.png → terminal_icon.png} +0 -0
  223. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_threefingerlongpressicon.png → three_finger_long_press_icon.png} +0 -0
  224. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_updateicon.png → update_icon.png} +0 -0
  225. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_usericon.png → user_icon.png} +0 -0
  226. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_xicon.png → x_icon.png} +0 -0
  227. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_branchicon.png → branch_icon.png} +0 -0
  228. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_buildingicon.png → building_icon.png} +0 -0
  229. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_checkicon.png → check_icon.png} +0 -0
  230. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_chevronrighticon.png → chevron_right_icon.png} +0 -0
  231. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_extensionsicon.png → extensions_icon.png} +0 -0
  232. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_homefilledinactiveicon.png → home_icon.png} +0 -0
  233. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_infoicon.png → info_icon.png} +0 -0
  234. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_settingsfilledinactiveicon.png → settings_icon.png} +0 -0
  235. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_shakedeviceicon.png → shake_device_icon.png} +0 -0
  236. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_showmenuatlaunchicon.png → show_menu_at_launch_icon.png} +0 -0
  237. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_terminalicon.png → terminal_icon.png} +0 -0
  238. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_threefingerlongpressicon.png → three_finger_long_press_icon.png} +0 -0
  239. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_updateicon.png → update_icon.png} +0 -0
  240. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_usericon.png → user_icon.png} +0 -0
  241. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_xicon.png → x_icon.png} +0 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,7 @@
7
7
  ### 🎉 New features
8
8
 
9
9
  - [iOS] Reimplement UI in SwiftUI. ([#37413](https://github.com/expo/expo/pull/37413) by [@alanjhughes](https://github.com/alanjhughes))
10
+ - [Android] Add floating action button option in the settings menu. ([#38247](https://github.com/expo/expo/pull/38247) by [@behenate](https://github.com/behenate))
10
11
 
11
12
  ### 🐛 Bug fixes
12
13
 
@@ -20,13 +20,13 @@ expoModule {
20
20
  }
21
21
 
22
22
  group = "host.exp.exponent"
23
- version = "5.2.0-canary-20250713-8f814f8"
23
+ version = "5.2.0-canary-20250729-d8899ae"
24
24
 
25
25
  android {
26
26
  namespace "expo.modules.devlauncher"
27
27
  defaultConfig {
28
28
  versionCode 9
29
- versionName "5.2.0-canary-20250713-8f814f8"
29
+ versionName "5.2.0-canary-20250729-d8899ae"
30
30
  }
31
31
 
32
32
  buildTypes {
@@ -97,10 +97,14 @@ dependencies {
97
97
  implementation "androidx.compose.ui:ui:$composeVersion"
98
98
  implementation "androidx.compose.ui:ui-tooling:$composeVersion"
99
99
  implementation "androidx.navigation:navigation-compose:2.9.0"
100
+ implementation "com.google.android.gms:play-services-code-scanner:16.1.0"
101
+ implementation "com.google.mlkit:barcode-scanning:17.3.0"
102
+
103
+ implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1")
100
104
 
101
105
  implementation("com.apollographql.apollo:apollo-runtime:4.3.1")
102
106
 
103
- implementation("com.composables:core:1.31.1")
107
+ implementation("com.composables:core:1.37.0")
104
108
 
105
109
  testImplementation 'androidx.test:core:1.4.0'
106
110
  testImplementation 'androidx.test:core-ktx:1.4.0'
@@ -1,15 +1,17 @@
1
1
  <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+
2
3
  <application>
3
4
  <activity
4
5
  android:name="expo.modules.devlauncher.launcher.DevLauncherActivity"
5
6
  android:exported="true"
6
7
  android:launchMode="singleTask"
7
- android:theme="@style/Theme.DevLauncher.LauncherActivity" />
8
+ android:theme="@style/Theme.AppCompat.DayNight.NoActionBar" />
8
9
 
9
10
  <activity
10
11
  android:name="expo.modules.devlauncher.compose.AuthActivity"
11
12
  android:exported="true"
12
- android:launchMode="singleTask">
13
+ android:launchMode="singleTask"
14
+ android:theme="@style/Theme.AppCompat.DayNight.NoActionBar">
13
15
  <intent-filter>
14
16
  <action android:name="android.intent.action.VIEW" />
15
17
 
@@ -5,7 +5,9 @@ import android.content.Context
5
5
  import android.content.Intent
6
6
  import android.net.Uri
7
7
  import android.os.Bundle
8
+ import android.util.Log
8
9
  import androidx.annotation.UiThread
10
+ import androidx.core.net.toUri
9
11
  import com.facebook.react.ReactActivity
10
12
  import com.facebook.react.ReactActivityDelegate
11
13
  import com.facebook.react.ReactApplication
@@ -30,8 +32,6 @@ import expo.modules.devlauncher.launcher.DevLauncherIntentRegistryInterface
30
32
  import expo.modules.devlauncher.launcher.DevLauncherLifecycle
31
33
  import expo.modules.devlauncher.launcher.DevLauncherNetworkInterceptor
32
34
  import expo.modules.devlauncher.launcher.DevLauncherReactActivityDelegateSupplier
33
- import expo.modules.devlauncher.launcher.DevLauncherReactHost
34
- import expo.modules.devlauncher.launcher.DevLauncherReactNativeHost
35
35
  import expo.modules.devlauncher.launcher.DevLauncherRecentlyOpenedAppsRegistry
36
36
  import expo.modules.devlauncher.launcher.errors.DevLauncherAppError
37
37
  import expo.modules.devlauncher.launcher.errors.DevLauncherErrorActivity
@@ -40,6 +40,7 @@ import expo.modules.devlauncher.launcher.loaders.DevLauncherAppLoaderFactoryInte
40
40
  import expo.modules.devlauncher.launcher.manifest.DevLauncherManifestParser
41
41
  import expo.modules.devlauncher.react.activitydelegates.DevLauncherReactActivityNOPDelegate
42
42
  import expo.modules.devlauncher.react.activitydelegates.DevLauncherReactActivityRedirectDelegate
43
+ import expo.modules.devlauncher.services.DependencyInjection
43
44
  import expo.modules.devlauncher.tests.DevLauncherTestInterceptor
44
45
  import expo.modules.devmenu.DevMenuManager
45
46
  import expo.modules.manifests.core.Manifest
@@ -52,10 +53,6 @@ import org.koin.core.component.get
52
53
  import org.koin.core.component.inject
53
54
  import org.koin.dsl.module
54
55
 
55
- // Use this to load from a development server for the development client launcher UI
56
- // private val DEV_LAUNCHER_HOST = "10.0.0.175:8090";
57
- private val DEV_LAUNCHER_HOST: String? = null
58
-
59
56
  private const val NEW_ACTIVITY_FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK or
60
57
  Intent.FLAG_ACTIVITY_CLEAR_TASK or
61
58
  Intent.FLAG_ACTIVITY_NO_ANIMATION
@@ -74,27 +71,29 @@ class DevLauncherController private constructor() :
74
71
  var devMenuManager: DevMenuManager = DevMenuManager
75
72
  override var updatesInterface: UpdatesInterface?
76
73
  get() = internalUpdatesInterface
77
- set(value) = DevLauncherKoinContext.app.koin.loadModules(
78
- listOf(
79
- module {
80
- single { value }
81
- }
74
+ set(value) = run {
75
+ if (value != null) {
76
+ DependencyInjection.appService.setUpUpdateInterface(value, context)
77
+ }
78
+
79
+ DevLauncherKoinContext.app.koin.loadModules(
80
+ listOf(
81
+ module {
82
+ single { value }
83
+ }
84
+ )
82
85
  )
83
- )
84
- override val coroutineScope = CoroutineScope(Dispatchers.Default)
86
+ }
85
87
 
86
- override val devClientHost by lazy {
87
- ReactHostWrapper(
88
- reactNativeHost = DevLauncherReactNativeHost(context as Application, DEV_LAUNCHER_HOST),
89
- reactHostProvider = { DevLauncherReactHost.create(context as Application, DEV_LAUNCHER_HOST) }
90
- )
91
- }
88
+ override val coroutineScope = CoroutineScope(Dispatchers.Default)
92
89
 
93
90
  private val recentlyOpedAppsRegistry = DevLauncherRecentlyOpenedAppsRegistry(context)
94
91
  override var manifest: Manifest? = null
95
92
  private set
93
+
96
94
  override var manifestURL: Uri? = null
97
95
  private set
96
+
98
97
  override var latestLoadedApp: Uri? = null
99
98
  override var useDeveloperSupport = true
100
99
  var canLaunchDevMenuOnStart = false
@@ -144,7 +143,7 @@ class DevLauncherController private constructor() :
144
143
  // default to the EXPO_UPDATE_URL value configured in AndroidManifest.xml when project url is unspecified for an EAS update
145
144
  if (isEASUpdate && projectUrl == null) {
146
145
  val projectUrlString = getMetadataValue(context, "expo.modules.updates.EXPO_UPDATE_URL")
147
- parsedProjectUrl = Uri.parse(projectUrlString)
146
+ parsedProjectUrl = projectUrlString.toUri()
148
147
  }
149
148
 
150
149
  val manifestParser = DevLauncherManifestParser(httpClient, parsedUrl, installationIDHelper.getOrCreateInstallationID(context))
@@ -206,7 +205,8 @@ class DevLauncherController private constructor() :
206
205
  }
207
206
  }
208
207
 
209
- override fun getRecentlyOpenedApps(): List<DevLauncherAppEntry> = recentlyOpedAppsRegistry.getRecentlyOpenedApps()
208
+ override fun getRecentlyOpenedApps(): List<DevLauncherAppEntry> =
209
+ recentlyOpedAppsRegistry.getRecentlyOpenedApps()
210
210
 
211
211
  override fun clearRecentlyOpenedApps() {
212
212
  recentlyOpedAppsRegistry.clearRegistry()
@@ -270,8 +270,8 @@ class DevLauncherController private constructor() :
270
270
  if (shouldTryToLaunchLastOpenedBundle && lastOpenedApp != null) {
271
271
  coroutineScope.launch {
272
272
  try {
273
- loadApp(Uri.parse(lastOpenedApp.url), activityToBeInvalidated)
274
- } catch (e: Throwable) {
273
+ loadApp(lastOpenedApp.url.toUri(), activityToBeInvalidated)
274
+ } catch (_: Throwable) {
275
275
  navigateToLauncher()
276
276
  }
277
277
  }
@@ -415,6 +415,17 @@ class DevLauncherController private constructor() :
415
415
 
416
416
  @JvmStatic
417
417
  internal fun initialize(context: Context, reactHost: ReactHostWrapper) {
418
+ try {
419
+ val splashScreenManagerClass = Class.forName("expo.modules.splashscreen.SplashScreenManager")
420
+ val splashScreenManager = splashScreenManagerClass
421
+ .kotlin
422
+ .objectInstance
423
+ splashScreenManagerClass.getMethod("hide")
424
+ .invoke(splashScreenManager)
425
+ } catch (e: Throwable) {
426
+ Log.e("DevLauncherController", "Failed to hide splash screen", e)
427
+ }
428
+
418
429
  val testInterceptor = DevLauncherKoinContext.app.koin.get<DevLauncherTestInterceptor>()
419
430
  if (!testInterceptor.allowReinitialization()) {
420
431
  check(!wasInitialized()) { "DevelopmentClientController was initialized." }
@@ -13,7 +13,6 @@ import expo.modules.core.interfaces.ReactActivityHandler
13
13
  import expo.modules.core.interfaces.ReactActivityLifecycleListener
14
14
  import expo.modules.devlauncher.launcher.DevLauncherReactActivityDelegateSupplier
15
15
  import expo.modules.devlauncher.modules.DevLauncherModule
16
- import expo.modules.devlauncher.modules.DevLauncherAuth
17
16
  import expo.modules.core.interfaces.ReactNativeHostHandler
18
17
  import expo.modules.devlauncher.modules.DevLauncherDevMenuExtension
19
18
  import expo.modules.devlauncher.react.DevLauncherReactNativeHostHandler
@@ -22,8 +21,7 @@ object DevLauncherPackageDelegate {
22
21
  fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> =
23
22
  listOf(
24
23
  DevLauncherModule(reactContext),
25
- DevLauncherDevMenuExtension(reactContext),
26
- DevLauncherAuth(reactContext)
24
+ DevLauncherDevMenuExtension(reactContext)
27
25
  )
28
26
 
29
27
  fun createApplicationLifecycleListeners(context: Context?): List<ApplicationLifecycleListener> =
@@ -35,10 +35,10 @@ sealed interface AuthResult {
35
35
 
36
36
  class AuthActivity : AppCompatActivity() {
37
37
  class Contract : ActivityResultContract<AuthRequestType, AuthResult>() {
38
- override fun createIntent(context: Context, type: AuthRequestType): Intent {
38
+ override fun createIntent(context: Context, input: AuthRequestType): Intent {
39
39
  return Intent(context, AuthActivity::class.java).apply {
40
40
  action = ACTION_VIEW
41
- putExtra(AUTH_REQUEST_TYPE_KEY, type.type)
41
+ putExtra(AUTH_REQUEST_TYPE_KEY, input.type)
42
42
  }
43
43
  }
44
44
 
@@ -1,39 +1,40 @@
1
1
  package expo.modules.devlauncher.compose
2
2
 
3
+ import androidx.compose.animation.AnimatedContentTransitionScope
3
4
  import androidx.compose.animation.EnterTransition
4
5
  import androidx.compose.animation.ExitTransition
6
+ import androidx.compose.animation.core.tween
5
7
  import androidx.compose.foundation.background
6
8
  import androidx.compose.foundation.layout.Box
7
9
  import androidx.compose.foundation.layout.fillMaxSize
8
- import androidx.compose.foundation.rememberScrollState
9
- import androidx.compose.foundation.verticalScroll
10
10
  import androidx.compose.runtime.Composable
11
+ import androidx.compose.runtime.remember
11
12
  import androidx.compose.ui.Modifier
12
13
  import androidx.compose.ui.graphics.painter.Painter
13
14
  import androidx.navigation.compose.NavHost
14
15
  import androidx.navigation.compose.composable
15
16
  import androidx.navigation.compose.rememberNavController
16
17
  import expo.modules.devlauncher.compose.primitives.DefaultScaffold
17
- import expo.modules.devlauncher.compose.routes.Home
18
+ import expo.modules.devlauncher.compose.routes.CrashReport
19
+ import expo.modules.devlauncher.compose.routes.CrashReportRoute
18
20
  import expo.modules.devlauncher.compose.routes.HomeRoute
19
21
  import expo.modules.devlauncher.compose.routes.ProfileRoute
20
- import expo.modules.devlauncher.compose.routes.Settings
22
+ import expo.modules.devlauncher.compose.routes.Routes
21
23
  import expo.modules.devlauncher.compose.routes.SettingsRoute
24
+ import expo.modules.devlauncher.compose.routes.UpdatesRoute
22
25
  import expo.modules.devlauncher.compose.ui.BottomTabBar
23
26
  import expo.modules.devlauncher.compose.ui.Full
24
27
  import expo.modules.devlauncher.compose.ui.rememberBottomSheetState
25
28
  import expo.modules.devmenu.compose.theme.Theme
29
+ import kotlinx.serialization.Serializable
26
30
 
27
31
  @Composable
28
32
  fun DefaultScreenContainer(
29
33
  content: @Composable () -> Unit
30
34
  ) {
31
- val scrollState = rememberScrollState()
32
-
33
35
  Box(
34
36
  modifier = Modifier
35
37
  .fillMaxSize()
36
- .verticalScroll(scrollState)
37
38
  .background(Theme.colors.background.secondary)
38
39
  ) {
39
40
  content()
@@ -46,30 +47,78 @@ data class Tab(
46
47
  val screen: Any
47
48
  )
48
49
 
50
+ @Serializable
51
+ object Main
52
+
49
53
  @Composable
50
54
  fun DevLauncherBottomTabsNavigator() {
51
- val navController = rememberNavController()
55
+ val mainNavController = rememberNavController()
56
+ val bottomTabsNavController = rememberNavController()
52
57
  val bottomSheetState = rememberBottomSheetState()
53
58
 
54
- DefaultScaffold(bottomTab = {
55
- BottomTabBar(navController)
56
- }) {
57
- NavHost(
58
- navController = navController,
59
- startDestination = Home,
59
+ val navigateToProfile = remember {
60
+ { bottomSheetState.targetDetent = Full }
61
+ }
62
+
63
+ NavHost(
64
+ navController = mainNavController,
65
+ startDestination = Main
66
+ ) {
67
+ composable<Main>(
60
68
  enterTransition = {
61
- EnterTransition.None
69
+ slideIntoContainer(
70
+ AnimatedContentTransitionScope.SlideDirection.Right,
71
+ animationSpec = tween(700)
72
+ )
62
73
  },
63
74
  exitTransition = {
64
- ExitTransition.None
75
+ slideOutOfContainer(
76
+ AnimatedContentTransitionScope.SlideDirection.Left,
77
+ animationSpec = tween(700)
78
+ )
65
79
  }
66
80
  ) {
67
- composable<Home> {
68
- HomeRoute(onProfileClick = { bottomSheetState.jumpTo(Full) })
81
+ DefaultScaffold(bottomTab = {
82
+ BottomTabBar(bottomTabsNavController)
83
+ }) {
84
+ NavHost(
85
+ navController = bottomTabsNavController,
86
+ startDestination = Routes.Home,
87
+ enterTransition = {
88
+ EnterTransition.None
89
+ },
90
+ exitTransition = {
91
+ ExitTransition.None
92
+ }
93
+ ) {
94
+ composable<Routes.Home> {
95
+ HomeRoute(navController = mainNavController, onProfileClick = navigateToProfile)
96
+ }
97
+ composable<Routes.Updates> {
98
+ UpdatesRoute(onProfileClick = navigateToProfile)
99
+ }
100
+ composable<Routes.Settings> {
101
+ SettingsRoute()
102
+ }
103
+ }
69
104
  }
70
- composable<Settings> {
71
- SettingsRoute()
105
+ }
106
+
107
+ composable<CrashReport>(
108
+ enterTransition = {
109
+ slideIntoContainer(
110
+ AnimatedContentTransitionScope.SlideDirection.Left,
111
+ animationSpec = tween(700)
112
+ )
113
+ },
114
+ exitTransition = {
115
+ slideOutOfContainer(
116
+ AnimatedContentTransitionScope.SlideDirection.Right,
117
+ animationSpec = tween(700)
118
+ )
72
119
  }
120
+ ) {
121
+ CrashReportRoute()
73
122
  }
74
123
  }
75
124
 
@@ -0,0 +1,14 @@
1
+ package expo.modules.devlauncher.compose
2
+
3
+ data class Update(
4
+ val id: String,
5
+ val name: String,
6
+ val createdAt: String?,
7
+ val isCompatible: Boolean = false,
8
+ val permalink: String
9
+ )
10
+
11
+ data class Branch(
12
+ val name: String,
13
+ val compatibleUpdate: Update? = null
14
+ )
@@ -0,0 +1,105 @@
1
+ package expo.modules.devlauncher.compose.models
2
+
3
+ import androidx.compose.runtime.mutableStateOf
4
+ import androidx.lifecycle.ViewModel
5
+ import androidx.lifecycle.viewModelScope
6
+ import expo.modules.devlauncher.DevLauncherController
7
+ import expo.modules.devlauncher.compose.Update
8
+ import expo.modules.devlauncher.compose.utils.formatUpdateUrl
9
+ import expo.modules.devlauncher.services.ApolloClientService
10
+ import expo.modules.devlauncher.services.AppService
11
+ import expo.modules.devlauncher.services.ApplicationInfo
12
+ import expo.modules.devlauncher.services.inject
13
+ import kotlinx.coroutines.flow.MutableStateFlow
14
+ import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
15
+ import kotlinx.coroutines.flow.onStart
16
+ import kotlinx.coroutines.flow.stateIn
17
+ import kotlinx.coroutines.launch
18
+
19
+ sealed interface BranchAction {
20
+ object LoadMoreUpdates : BranchAction
21
+ class OpenUpdate(val update: Update) : BranchAction
22
+ }
23
+
24
+ data class BranchState(
25
+ val updates: List<Update> = emptyList(),
26
+ val isLoading: Boolean = false
27
+ )
28
+
29
+ class BranchViewModel(private val branchName: String) : ViewModel() {
30
+ private val appService = inject<AppService>()
31
+ private val apolloClientService = inject<ApolloClientService>()
32
+ private val launcher = inject<DevLauncherController>()
33
+
34
+ private val hasMore = mutableStateOf(true)
35
+
36
+ private var _state = MutableStateFlow(
37
+ BranchState(
38
+ updates = emptyList(),
39
+ isLoading = true
40
+ )
41
+ )
42
+
43
+ val state = _state.onStart {
44
+ hasMore.value = true
45
+ loadMoreUpdates()
46
+ }.stateIn(
47
+ scope = viewModelScope,
48
+ started = WhileSubscribed(5_000),
49
+ initialValue = _state.value
50
+ )
51
+
52
+ private suspend fun loadMoreUpdates() {
53
+ if (!hasMore.value) {
54
+ return
55
+ }
56
+
57
+ val updateConfiguration = appService.applicationInfo as? ApplicationInfo.Updates
58
+ if (updateConfiguration == null) {
59
+ return
60
+ }
61
+
62
+ _state.value = _state.value.copy(isLoading = true)
63
+
64
+ val appId = updateConfiguration.appId
65
+ val runtimeVersion = updateConfiguration.runtimeVersion
66
+ val limit = 50
67
+
68
+ val updates = apolloClientService.fetchUpdates(
69
+ appId = appId,
70
+ branchName = branchName,
71
+ limit = limit,
72
+ offset = _state.value.updates.size
73
+ )
74
+
75
+ val uiUpdates = updates.data?.app?.byId?.updateBranchByName?.updates?.map { update ->
76
+ Update(
77
+ id = update.id,
78
+ name = update.message ?: "No message",
79
+ createdAt = update.createdAt as? String,
80
+ isCompatible = update.runtimeVersion == runtimeVersion,
81
+ permalink = update.manifestPermalink
82
+ )
83
+ } ?: emptyList()
84
+
85
+ hasMore.value = uiUpdates.size == limit
86
+
87
+ _state.value = _state.value.copy(
88
+ updates = _state.value.updates + uiUpdates,
89
+ isLoading = false
90
+ )
91
+ }
92
+
93
+ fun onAction(action: BranchAction) {
94
+ when (action) {
95
+ BranchAction.LoadMoreUpdates -> viewModelScope.launch { loadMoreUpdates() }
96
+ is BranchAction.OpenUpdate -> {
97
+ launcher.coroutineScope.launch {
98
+ launcher.loadApp(
99
+ formatUpdateUrl(action.update.permalink, action.update.name)
100
+ )
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,113 @@
1
+ package expo.modules.devlauncher.compose.models
2
+
3
+ import androidx.compose.runtime.mutableStateOf
4
+ import androidx.lifecycle.ViewModel
5
+ import androidx.lifecycle.viewModelScope
6
+ import expo.modules.devlauncher.compose.Branch
7
+ import expo.modules.devlauncher.compose.Update
8
+ import expo.modules.devlauncher.services.ApolloClientService
9
+ import expo.modules.devlauncher.services.AppService
10
+ import expo.modules.devlauncher.services.ApplicationInfo
11
+ import expo.modules.devlauncher.services.inject
12
+ import kotlinx.coroutines.flow.MutableStateFlow
13
+ import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
14
+ import kotlinx.coroutines.flow.onStart
15
+ import kotlinx.coroutines.flow.stateIn
16
+ import kotlinx.coroutines.launch
17
+
18
+ sealed interface BranchesAction {
19
+ object LoadMoreBranches : BranchesAction
20
+ class OpenBranch(val branchName: String) : BranchesAction
21
+ }
22
+
23
+ data class BranchesState(
24
+ val branches: List<Branch> = emptyList(),
25
+ val isLoading: Boolean = false
26
+ )
27
+
28
+ class BranchesViewModel : ViewModel() {
29
+ private val apolloClientService = inject<ApolloClientService>()
30
+ private val appService = inject<AppService>()
31
+
32
+ private val hasMore = mutableStateOf(true)
33
+
34
+ val areUpdatesConfigured get() = appService.applicationInfo is ApplicationInfo.Updates
35
+
36
+ private var _state = MutableStateFlow(
37
+ BranchesState(
38
+ branches = emptyList(),
39
+ isLoading = areUpdatesConfigured
40
+ )
41
+ )
42
+
43
+ val state = _state.onStart {
44
+ if (areUpdatesConfigured) {
45
+ hasMore.value = true
46
+ loadMoreBranches()
47
+ }
48
+ }.stateIn(
49
+ scope = viewModelScope,
50
+ started = WhileSubscribed(5_000),
51
+ initialValue = _state.value
52
+ )
53
+
54
+ private suspend fun loadMoreBranches() {
55
+ val updateConfiguration = appService.applicationInfo as? ApplicationInfo.Updates
56
+
57
+ // If the app is not configured for updates, we don't need to fetch branches.
58
+ if (updateConfiguration == null || !hasMore.value) {
59
+ return
60
+ }
61
+
62
+ val appId = updateConfiguration.appId
63
+ val runtimeVersion = updateConfiguration.runtimeVersion
64
+ val limit = 50
65
+
66
+ val branches = if (runtimeVersion != null) {
67
+ apolloClientService.fetchBranches(
68
+ appId = appId,
69
+ runtimeVersion = runtimeVersion,
70
+ offset = _state.value.branches.size,
71
+ limit = limit
72
+ ).data?.app?.byId?.updateBranches?.map { updateBranch ->
73
+ Branch(
74
+ name = updateBranch.name,
75
+ compatibleUpdate = updateBranch.compatibleUpdates.firstOrNull()?.let { update ->
76
+ Update(
77
+ id = update.id,
78
+ name = update.message ?: "No message",
79
+ createdAt = update.createdAt as? String,
80
+ isCompatible = true,
81
+ permalink = update.manifestPermalink
82
+ )
83
+ }
84
+ )
85
+ }
86
+ } else {
87
+ apolloClientService.fetchBranches(
88
+ appId = appId,
89
+ offset = _state.value.branches.size,
90
+ limit = limit
91
+ ).data?.app?.byId?.updateBranches?.map { updateBranch ->
92
+ Branch(
93
+ name = updateBranch.name,
94
+ compatibleUpdate = null
95
+ )
96
+ }
97
+ } ?: emptyList()
98
+
99
+ hasMore.value = branches.size == limit
100
+
101
+ _state.value = _state.value.copy(
102
+ isLoading = false,
103
+ branches = _state.value.branches + branches
104
+ )
105
+ }
106
+
107
+ fun onAction(action: BranchesAction) {
108
+ when (action) {
109
+ BranchesAction.LoadMoreBranches -> viewModelScope.launch { loadMoreBranches() }
110
+ is BranchesAction.OpenBranch -> throw IllegalStateException("Opening branches should be handled in the screen, not in the ViewModel.")
111
+ }
112
+ }
113
+ }
@@ -0,0 +1,22 @@
1
+ package expo.modules.devlauncher.compose.models
2
+
3
+ import androidx.compose.runtime.mutableStateOf
4
+ import androidx.lifecycle.SavedStateHandle
5
+ import androidx.lifecycle.ViewModel
6
+ import androidx.navigation.toRoute
7
+ import expo.modules.devlauncher.compose.routes.CrashReport
8
+
9
+ data class CrashReportState(
10
+ val report: CrashReport
11
+ )
12
+
13
+ class CrashReportModel(
14
+ savedStateHandle: SavedStateHandle
15
+ ) : ViewModel() {
16
+ private val _state = mutableStateOf(
17
+ savedStateHandle.toRoute<CrashReport>()
18
+ )
19
+
20
+ val state
21
+ get() = _state.value
22
+ }
@@ -0,0 +1,50 @@
1
+ package expo.modules.devlauncher.compose.models
2
+
3
+ import androidx.compose.runtime.mutableStateOf
4
+ import androidx.lifecycle.ViewModel
5
+ import com.facebook.react.ReactActivity
6
+ import expo.modules.devlauncher.DevLauncherController
7
+ import expo.modules.devlauncher.launcher.errors.DevLauncherAppError
8
+ import expo.modules.devlauncher.services.inject
9
+ import kotlinx.coroutines.launch
10
+
11
+ sealed interface ErrorAction {
12
+ object Reload : ErrorAction
13
+ object GoToHome : ErrorAction
14
+ }
15
+
16
+ class ErrorViewModel() : ViewModel() {
17
+ private val devLauncher = inject<DevLauncherController>()
18
+
19
+ private val _appError = mutableStateOf<DevLauncherAppError?>(null)
20
+
21
+ val appError
22
+ get() = _appError.value
23
+
24
+ fun setError(error: DevLauncherAppError) {
25
+ _appError.value = error
26
+ }
27
+
28
+ fun onAction(action: ErrorAction) {
29
+ when (action) {
30
+ is ErrorAction.Reload -> {
31
+ val appUrl = devLauncher.latestLoadedApp
32
+
33
+ if (appUrl == null) {
34
+ devLauncher.navigateToLauncher()
35
+ return
36
+ }
37
+
38
+ devLauncher.coroutineScope.launch {
39
+ devLauncher
40
+ .loadApp(
41
+ appUrl,
42
+ devLauncher.appHost.currentReactContext?.currentActivity as? ReactActivity?
43
+ )
44
+ }
45
+ }
46
+
47
+ is ErrorAction.GoToHome -> devLauncher.navigateToLauncher()
48
+ }
49
+ }
50
+ }