expo-dev-launcher 5.2.0-canary-20250722-599a28f → 5.2.0-canary-20250811-5c940c0

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 (258) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/android/build.gradle +6 -2
  3. package/android/src/debug/AndroidManifest.xml +4 -2
  4. package/android/src/debug/java/expo/modules/devlauncher/DevLauncherController.kt +12 -5
  5. package/android/src/debug/java/expo/modules/devlauncher/compose/DevLauncherBottomTabsNavigator.kt +14 -6
  6. package/android/src/debug/java/expo/modules/devlauncher/compose/UpdatesModels.kt +14 -0
  7. package/android/src/debug/java/expo/modules/devlauncher/compose/models/BranchViewModel.kt +105 -0
  8. package/android/src/debug/java/expo/modules/devlauncher/compose/models/BranchesViewModel.kt +113 -0
  9. package/android/src/debug/java/expo/modules/devlauncher/compose/models/ErrorViewModel.kt +50 -0
  10. package/android/src/debug/java/expo/modules/devlauncher/compose/models/HomeViewModel.kt +36 -29
  11. package/android/src/debug/java/expo/modules/devlauncher/compose/models/SettingsViewModel.kt +12 -3
  12. package/android/src/debug/java/expo/modules/devlauncher/compose/primitives/Accordion.kt +5 -4
  13. package/android/src/debug/java/expo/modules/devlauncher/compose/primitives/Asyncimage.kt +1 -2
  14. package/android/src/debug/java/expo/modules/devlauncher/compose/primitives/CircularProgressBar.kt +89 -0
  15. package/android/src/debug/java/expo/modules/devlauncher/compose/routes/Home.kt +16 -4
  16. package/android/src/debug/java/expo/modules/devlauncher/compose/routes/Routes.kt +20 -0
  17. package/android/src/debug/java/expo/modules/devlauncher/compose/routes/Settings.kt +0 -4
  18. package/android/src/debug/java/expo/modules/devlauncher/compose/routes/Updates.kt +95 -0
  19. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/BranchScreen.kt +240 -0
  20. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/BranchesScreen.kt +262 -0
  21. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/CrashReportScreen.kt +8 -2
  22. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/ErrorScreen.kt +79 -0
  23. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/HomeScreen.kt +73 -28
  24. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/NoUpdatesScreen.kt +73 -0
  25. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/SettingsScreen.kt +38 -10
  26. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/AccountAvatar.kt +3 -4
  27. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/AccountSelector.kt +3 -4
  28. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/AppHeader.kt +54 -15
  29. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/AppLoadingErrorDialog.kt +81 -0
  30. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/BottomTabBar.kt +10 -6
  31. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/BottomTabButton.kt +6 -3
  32. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/DevelopmentServerHelp.kt +0 -4
  33. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/ProfileLayout.kt +18 -10
  34. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/RunningAppCard.kt +2 -1
  35. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/ScreenHeaderContainer.kt +2 -1
  36. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/SectionHeader.kt +0 -24
  37. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/ServerUrlInput.kt +1 -0
  38. package/android/src/debug/java/expo/modules/devlauncher/compose/utils/DateFormat.kt +40 -0
  39. package/android/src/debug/java/expo/modules/devlauncher/compose/utils/UpdatesUtils.kt +12 -0
  40. package/android/src/debug/java/expo/modules/devlauncher/helpers/DevLauncherReactUtils.kt +2 -2
  41. package/android/src/debug/java/expo/modules/devlauncher/launcher/errors/DevLauncherErrorActivity.kt +27 -96
  42. package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherExpoAppLoader.kt +2 -2
  43. package/android/src/debug/java/expo/modules/devlauncher/react/DevLauncherDevSupportManagerSwapper.kt +2 -2
  44. package/android/src/debug/java/expo/modules/devlauncher/react/DevLauncherReactNativeHostHandler.kt +0 -5
  45. package/android/src/debug/java/expo/modules/devlauncher/services/ApolloClientService.kt +53 -0
  46. package/android/src/debug/java/expo/modules/devlauncher/services/DependencyInjection.kt +2 -1
  47. package/android/src/debug/java/expo/modules/devlauncher/services/ImageLoaderService.kt +17 -5
  48. package/android/src/debug/java/expo/modules/devlauncher/services/PackagerService.kt +0 -1
  49. package/android/src/debug/java/expo/modules/devlauncher/services/SessionService.kt +1 -6
  50. package/android/src/debug/res/drawable-mdpi/qr_code.png +0 -0
  51. package/android/src/debug/res/drawable-night-mdpi/qr_code.png +0 -0
  52. package/android/src/debug/res/drawable-night-xhdpi/qr_code.png +0 -0
  53. package/android/src/debug/res/drawable-night-xxhdpi/qr_code.png +0 -0
  54. package/android/src/debug/res/drawable-xhdpi/qr_code.png +0 -0
  55. package/android/src/debug/res/drawable-xxhdpi/qr_code.png +0 -0
  56. package/android/src/main/AndroidManifest.xml +6 -1
  57. package/android/src/main/graphql/GetBranches.graphql +17 -5
  58. package/android/src/main/graphql/GetUpdates.graphql +0 -13
  59. package/android/src/main/java/expo/modules/devlauncher/react/activitydelegates/DevLauncherReactActivityNOPDelegate.kt +2 -2
  60. package/android/src/main/java/expo/modules/devlauncher/splashscreen/DevLauncherSplashScreen.kt +0 -2
  61. package/android/src/main/res/drawable-mdpi/dev_menu_fab_icon.png +0 -0
  62. package/android/src/main/res/drawable-xhdpi/dev_menu_fab_icon.png +0 -0
  63. package/android/src/main/res/drawable-xxhdpi/dev_menu_fab_icon.png +0 -0
  64. package/android/src/main/res/values/styles.xml +0 -14
  65. package/expo-dev-launcher.podspec +2 -1
  66. package/ios/Assets.xcassets/expo-go-logo.imageset/Contents.json +21 -0
  67. package/ios/Assets.xcassets/expo-go-logo.imageset/expo-go logo.png +0 -0
  68. package/ios/Assets.xcassets/log-in.imageset/Contents.json +21 -0
  69. package/ios/Assets.xcassets/log-in.imageset/log-in.png +0 -0
  70. package/ios/EXDevLauncherController.m +2 -0
  71. package/ios/EXDevLauncherInstallationIDHelper.swift +6 -0
  72. package/ios/EXDevLauncherPendingDeepLinkRegistry.swift +2 -0
  73. package/ios/EXDevLauncherRecentlyOpenedAppsRegistry.swift +1 -0
  74. package/ios/EXDevLauncherUtils.swift +2 -11
  75. package/ios/Errors/EXDevLauncherErrorManager.swift +41 -5
  76. package/ios/Manifest/EXDevLauncherManifestHelper.swift +14 -10
  77. package/ios/ReactNative/EXDevLauncherBundleURLProviderInterceptor.swift +2 -0
  78. package/ios/SwiftUI/AccountSheet.swift +49 -35
  79. package/ios/SwiftUI/CrashReportView.swift +175 -0
  80. package/ios/SwiftUI/DevLauncherTabBarManager.swift +4 -4
  81. package/ios/SwiftUI/DevLauncherViewModel.swift +42 -23
  82. package/ios/SwiftUI/DevLauncherViews.swift +19 -14
  83. package/ios/SwiftUI/DevServerInfoModal.swift +21 -9
  84. package/ios/SwiftUI/DevServersView.swift +21 -18
  85. package/ios/SwiftUI/ErrorView.swift +46 -67
  86. package/ios/SwiftUI/HomeTabView.swift +28 -9
  87. package/ios/SwiftUI/Navigation/Navigation.swift +18 -8
  88. package/ios/SwiftUI/SettingsTabView.swift +90 -32
  89. package/ios/SwiftUI/StackFrameView.swift +29 -0
  90. package/ios/SwiftUI/UpdatesTab/NotSignedInView.swift +31 -18
  91. package/ios/SwiftUI/UpdatesTab/UpdateRow.swift +2 -0
  92. package/ios/SwiftUI/UpdatesTab/UpdatesListView.swift +4 -0
  93. package/ios/SwiftUI/UpdatesTab/UpdatesTabView.swift +1 -12
  94. package/ios/SwiftUI/Utils/Utils.swift +49 -0
  95. package/ios/Tests/EXDevLauncherControllerTest.swift +0 -10
  96. package/ios/Unsafe/RCTPackagerConnection+EXDevLauncherPackagerConnectionInterceptor.m +19 -5
  97. package/package.json +4 -4
  98. package/android/src/debug/res/drawable-mdpi/__node_modules_reactnavigation_elements_lib_module_assets_backicon.png +0 -0
  99. package/android/src/debug/res/drawable-mdpi/__node_modules_reactnavigation_elements_lib_module_assets_backiconmask.png +0 -0
  100. package/android/src/debug/res/drawable-mdpi/__node_modules_reactnavigation_elements_lib_module_assets_clearicon.png +0 -0
  101. package/android/src/debug/res/drawable-mdpi/__node_modules_reactnavigation_elements_lib_module_assets_closeicon.png +0 -0
  102. package/android/src/debug/res/drawable-mdpi/__node_modules_reactnavigation_elements_lib_module_assets_searchicon.png +0 -0
  103. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_branchiconlight.png +0 -0
  104. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_checkiconlight.png +0 -0
  105. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_clipboardicon.png +0 -0
  106. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_debugicon.png +0 -0
  107. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_extensionsfilledactiveicon.png +0 -0
  108. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_extensionsfilledactiveiconlight.png +0 -0
  109. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_extensionsfilledinactiveicon.png +0 -0
  110. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_extensionsfilledinactiveiconlight.png +0 -0
  111. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_extensionsiconlight.png +0 -0
  112. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_homefilledactiveicon.png +0 -0
  113. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_homefilledactiveiconlight.png +0 -0
  114. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_homefilledinactiveiconlight.png +0 -0
  115. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_infoiconlight.png +0 -0
  116. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_inspectelementicon.png +0 -0
  117. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_loadingindicatoricon.png +0 -0
  118. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_logoicon.png +0 -0
  119. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_performanceicon.png +0 -0
  120. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_questionmarkicon.png +0 -0
  121. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_refreshicon.png +0 -0
  122. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_runicon.png +0 -0
  123. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_settingsfilledactiveicon.png +0 -0
  124. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_settingsfilledactiveiconlight.png +0 -0
  125. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_settingsfilledinactiveiconlight.png +0 -0
  126. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_showmenuatlaunchiconlight.png +0 -0
  127. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_threefingerlongpressiconlight.png +0 -0
  128. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_toolbaroverlayicon.png +0 -0
  129. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_updateiconlight.png +0 -0
  130. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_usericonlight.png +0 -0
  131. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_warningtriangleicon.png +0 -0
  132. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_warningtriangleiconlight.png +0 -0
  133. package/android/src/debug/res/drawable-mdpi/_expodevclientcomponents_assets_xiconlight.png +0 -0
  134. package/android/src/debug/res/drawable-xhdpi/__node_modules_reactnavigation_elements_lib_module_assets_backicon.png +0 -0
  135. package/android/src/debug/res/drawable-xhdpi/__node_modules_reactnavigation_elements_lib_module_assets_clearicon.png +0 -0
  136. package/android/src/debug/res/drawable-xhdpi/__node_modules_reactnavigation_elements_lib_module_assets_closeicon.png +0 -0
  137. package/android/src/debug/res/drawable-xhdpi/__node_modules_reactnavigation_elements_lib_module_assets_searchicon.png +0 -0
  138. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_branchiconlight.png +0 -0
  139. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_checkiconlight.png +0 -0
  140. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_clipboardicon.png +0 -0
  141. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_debugicon.png +0 -0
  142. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_extensionsfilledactiveicon.png +0 -0
  143. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_extensionsfilledactiveiconlight.png +0 -0
  144. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_extensionsfilledinactiveicon.png +0 -0
  145. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_extensionsfilledinactiveiconlight.png +0 -0
  146. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_extensionsiconlight.png +0 -0
  147. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_homefilledactiveicon.png +0 -0
  148. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_homefilledactiveiconlight.png +0 -0
  149. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_homefilledinactiveiconlight.png +0 -0
  150. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_infoiconlight.png +0 -0
  151. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_inspectelementicon.png +0 -0
  152. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_loadingindicatoricon.png +0 -0
  153. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_logoicon.png +0 -0
  154. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_performanceicon.png +0 -0
  155. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_questionmarkicon.png +0 -0
  156. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_refreshicon.png +0 -0
  157. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_runicon.png +0 -0
  158. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_settingsfilledactiveicon.png +0 -0
  159. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_settingsfilledactiveiconlight.png +0 -0
  160. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_settingsfilledinactiveiconlight.png +0 -0
  161. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_showmenuatlaunchiconlight.png +0 -0
  162. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_threefingerlongpressiconlight.png +0 -0
  163. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_toolbaroverlayicon.png +0 -0
  164. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_updateiconlight.png +0 -0
  165. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_usericonlight.png +0 -0
  166. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_warningtriangleicon.png +0 -0
  167. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_warningtriangleiconlight.png +0 -0
  168. package/android/src/debug/res/drawable-xhdpi/_expodevclientcomponents_assets_xiconlight.png +0 -0
  169. package/android/src/debug/res/drawable-xxhdpi/__node_modules_reactnavigation_elements_lib_module_assets_backicon.png +0 -0
  170. package/android/src/debug/res/drawable-xxhdpi/__node_modules_reactnavigation_elements_lib_module_assets_clearicon.png +0 -0
  171. package/android/src/debug/res/drawable-xxhdpi/__node_modules_reactnavigation_elements_lib_module_assets_closeicon.png +0 -0
  172. package/android/src/debug/res/drawable-xxhdpi/__node_modules_reactnavigation_elements_lib_module_assets_searchicon.png +0 -0
  173. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_branchiconlight.png +0 -0
  174. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_checkiconlight.png +0 -0
  175. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_clipboardicon.png +0 -0
  176. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_debugicon.png +0 -0
  177. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_extensionsfilledactiveicon.png +0 -0
  178. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_extensionsfilledactiveiconlight.png +0 -0
  179. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_extensionsfilledinactiveicon.png +0 -0
  180. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_extensionsfilledinactiveiconlight.png +0 -0
  181. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_extensionsiconlight.png +0 -0
  182. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_homefilledactiveicon.png +0 -0
  183. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_homefilledactiveiconlight.png +0 -0
  184. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_homefilledinactiveiconlight.png +0 -0
  185. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_infoiconlight.png +0 -0
  186. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_inspectelementicon.png +0 -0
  187. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_loadingindicatoricon.png +0 -0
  188. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_logoicon.png +0 -0
  189. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_performanceicon.png +0 -0
  190. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_questionmarkicon.png +0 -0
  191. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_refreshicon.png +0 -0
  192. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_runicon.png +0 -0
  193. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_settingsfilledactiveicon.png +0 -0
  194. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_settingsfilledactiveiconlight.png +0 -0
  195. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_settingsfilledinactiveiconlight.png +0 -0
  196. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_showmenuatlaunchiconlight.png +0 -0
  197. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_threefingerlongpressiconlight.png +0 -0
  198. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_toolbaroverlayicon.png +0 -0
  199. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_updateiconlight.png +0 -0
  200. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_usericonlight.png +0 -0
  201. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_warningtriangleicon.png +0 -0
  202. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_warningtriangleiconlight.png +0 -0
  203. package/android/src/debug/res/drawable-xxhdpi/_expodevclientcomponents_assets_xiconlight.png +0 -0
  204. package/android/src/debug/res/drawable-xxxhdpi/__node_modules_reactnavigation_elements_lib_module_assets_backicon.png +0 -0
  205. package/android/src/debug/res/drawable-xxxhdpi/__node_modules_reactnavigation_elements_lib_module_assets_clearicon.png +0 -0
  206. package/android/src/debug/res/drawable-xxxhdpi/__node_modules_reactnavigation_elements_lib_module_assets_closeicon.png +0 -0
  207. package/android/src/debug/res/drawable-xxxhdpi/__node_modules_reactnavigation_elements_lib_module_assets_searchicon.png +0 -0
  208. package/android/src/main/res/drawable-mdpi/_expodevclientcomponents_assets_logoicon.png +0 -0
  209. package/android/src/main/res/drawable-xhdpi/_expodevclientcomponents_assets_logoicon.png +0 -0
  210. package/android/src/main/res/drawable-xxhdpi/_expodevclientcomponents_assets_logoicon.png +0 -0
  211. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_branchicon.png → branch_icon.png} +0 -0
  212. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_buildingicon.png → building_icon.png} +0 -0
  213. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_checkicon.png → check_icon.png} +0 -0
  214. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_chevronrighticon.png → chevron_right_icon.png} +0 -0
  215. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_extensionsicon.png → extensions_icon.png} +0 -0
  216. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_homefilledinactiveicon.png → home_icon.png} +0 -0
  217. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_infoicon.png → info_icon.png} +0 -0
  218. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_settingsfilledinactiveicon.png → settings_icon.png} +0 -0
  219. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_shakedeviceicon.png → shake_device_icon.png} +0 -0
  220. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_showmenuatlaunchicon.png → show_menu_at_launch_icon.png} +0 -0
  221. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_terminalicon.png → terminal_icon.png} +0 -0
  222. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_threefingerlongpressicon.png → three_finger_long_press_icon.png} +0 -0
  223. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_updateicon.png → update_icon.png} +0 -0
  224. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_usericon.png → user_icon.png} +0 -0
  225. /package/android/src/debug/res/drawable-mdpi/{_expodevclientcomponents_assets_xicon.png → x_icon.png} +0 -0
  226. /package/android/src/debug/res/{drawable-mdpi/_expodevclientcomponents_assets_shakedeviceiconlight.png → drawable-night-mdpi/shake_device_icon.png} +0 -0
  227. /package/android/src/debug/res/{drawable-xhdpi/_expodevclientcomponents_assets_shakedeviceiconlight.png → drawable-night-xhdpi/shake_device_icon.png} +0 -0
  228. /package/android/src/debug/res/{drawable-xxhdpi/_expodevclientcomponents_assets_shakedeviceiconlight.png → drawable-night-xxhdpi/shake_device_icon.png} +0 -0
  229. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_branchicon.png → branch_icon.png} +0 -0
  230. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_buildingicon.png → building_icon.png} +0 -0
  231. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_checkicon.png → check_icon.png} +0 -0
  232. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_chevronrighticon.png → chevron_right_icon.png} +0 -0
  233. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_extensionsicon.png → extensions_icon.png} +0 -0
  234. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_homefilledinactiveicon.png → home_icon.png} +0 -0
  235. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_infoicon.png → info_icon.png} +0 -0
  236. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_settingsfilledinactiveicon.png → settings_icon.png} +0 -0
  237. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_shakedeviceicon.png → shake_device_icon.png} +0 -0
  238. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_showmenuatlaunchicon.png → show_menu_at_launch_icon.png} +0 -0
  239. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_terminalicon.png → terminal_icon.png} +0 -0
  240. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_threefingerlongpressicon.png → three_finger_long_press_icon.png} +0 -0
  241. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_updateicon.png → update_icon.png} +0 -0
  242. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_usericon.png → user_icon.png} +0 -0
  243. /package/android/src/debug/res/drawable-xhdpi/{_expodevclientcomponents_assets_xicon.png → x_icon.png} +0 -0
  244. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_branchicon.png → branch_icon.png} +0 -0
  245. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_buildingicon.png → building_icon.png} +0 -0
  246. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_checkicon.png → check_icon.png} +0 -0
  247. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_chevronrighticon.png → chevron_right_icon.png} +0 -0
  248. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_extensionsicon.png → extensions_icon.png} +0 -0
  249. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_homefilledinactiveicon.png → home_icon.png} +0 -0
  250. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_infoicon.png → info_icon.png} +0 -0
  251. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_settingsfilledinactiveicon.png → settings_icon.png} +0 -0
  252. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_shakedeviceicon.png → shake_device_icon.png} +0 -0
  253. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_showmenuatlaunchicon.png → show_menu_at_launch_icon.png} +0 -0
  254. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_terminalicon.png → terminal_icon.png} +0 -0
  255. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_threefingerlongpressicon.png → three_finger_long_press_icon.png} +0 -0
  256. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_updateicon.png → update_icon.png} +0 -0
  257. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_usericon.png → user_icon.png} +0 -0
  258. /package/android/src/debug/res/drawable-xxhdpi/{_expodevclientcomponents_assets_xicon.png → x_icon.png} +0 -0
package/CHANGELOG.md CHANGED
@@ -7,15 +7,20 @@
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))
11
+ - [iOS] Partial support for Apple TV. ([#38388](https://github.com/expo/expo/pull/38388) by [@douglowder](https://github.com/douglowder))
10
12
 
11
13
  ### 🐛 Bug fixes
12
14
 
13
15
  - [iOS] Fix missing CDP headers when using static frameworks. ([#37448](https://github.com/expo/expo/pull/37448) by [@alanjhughes](https://github.com/alanjhughes))
14
16
  - [Android] Use same strings in UI as iOS. ([#37786](https://github.com/expo/expo/pull/37786) by [@douglowder](https://github.com/douglowder))
17
+ - [iOS] Fix an issue where the packager commands stop working after a reload. ([#38579](https://github.com/expo/expo/pull/38579) by [@alanjhughes](https://github.com/alanjhughes))
15
18
 
16
19
  ### 💡 Others
17
20
 
21
+ - [Android] RN 0.81.0: patched some reflection issues after kotlin upgrade ([#38451](https://github.com/expo/expo/pull/38451) by [@chrfalch](https://github.com/chrfalch))
18
22
  - Fixed release build error on Android. ([#37579](https://github.com/expo/expo/pull/37579) by [@kudo](https://github.com/kudo))
23
+ - Informational message for tvOS on signin page. ([#38518](https://github.com/expo/expo/pull/38518) by [@douglowder](https://github.com/douglowder))
19
24
 
20
25
  ### ⚠️ Notices
21
26
 
@@ -20,13 +20,13 @@ expoModule {
20
20
  }
21
21
 
22
22
  group = "host.exp.exponent"
23
- version = "5.2.0-canary-20250722-599a28f"
23
+ version = "5.2.0-canary-20250811-5c940c0"
24
24
 
25
25
  android {
26
26
  namespace "expo.modules.devlauncher"
27
27
  defaultConfig {
28
28
  versionCode 9
29
- versionName "5.2.0-canary-20250722-599a28f"
29
+ versionName "5.2.0-canary-20250811-5c940c0"
30
30
  }
31
31
 
32
32
  buildTypes {
@@ -97,6 +97,10 @@ 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
 
@@ -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,6 +5,7 @@ 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
9
10
  import androidx.core.net.toUri
10
11
  import com.facebook.react.ReactActivity
@@ -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
@@ -90,7 +87,6 @@ class DevLauncherController private constructor() :
90
87
 
91
88
  override val coroutineScope = CoroutineScope(Dispatchers.Default)
92
89
 
93
-
94
90
  private val recentlyOpedAppsRegistry = DevLauncherRecentlyOpenedAppsRegistry(context)
95
91
  override var manifest: Manifest? = null
96
92
  private set
@@ -419,6 +415,17 @@ class DevLauncherController private constructor() :
419
415
 
420
416
  @JvmStatic
421
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
+
422
429
  val testInterceptor = DevLauncherKoinContext.app.koin.get<DevLauncherTestInterceptor>()
423
430
  if (!testInterceptor.allowReinitialization()) {
424
431
  check(!wasInitialized()) { "DevelopmentClientController was initialized." }
@@ -8,6 +8,7 @@ import androidx.compose.foundation.background
8
8
  import androidx.compose.foundation.layout.Box
9
9
  import androidx.compose.foundation.layout.fillMaxSize
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
@@ -16,11 +17,11 @@ import androidx.navigation.compose.rememberNavController
16
17
  import expo.modules.devlauncher.compose.primitives.DefaultScaffold
17
18
  import expo.modules.devlauncher.compose.routes.CrashReport
18
19
  import expo.modules.devlauncher.compose.routes.CrashReportRoute
19
- import expo.modules.devlauncher.compose.routes.Home
20
20
  import expo.modules.devlauncher.compose.routes.HomeRoute
21
21
  import expo.modules.devlauncher.compose.routes.ProfileRoute
22
- import expo.modules.devlauncher.compose.routes.Settings
22
+ import expo.modules.devlauncher.compose.routes.Routes
23
23
  import expo.modules.devlauncher.compose.routes.SettingsRoute
24
+ import expo.modules.devlauncher.compose.routes.UpdatesRoute
24
25
  import expo.modules.devlauncher.compose.ui.BottomTabBar
25
26
  import expo.modules.devlauncher.compose.ui.Full
26
27
  import expo.modules.devlauncher.compose.ui.rememberBottomSheetState
@@ -55,6 +56,10 @@ fun DevLauncherBottomTabsNavigator() {
55
56
  val bottomTabsNavController = rememberNavController()
56
57
  val bottomSheetState = rememberBottomSheetState()
57
58
 
59
+ val navigateToProfile = remember {
60
+ { bottomSheetState.targetDetent = Full }
61
+ }
62
+
58
63
  NavHost(
59
64
  navController = mainNavController,
60
65
  startDestination = Main
@@ -78,7 +83,7 @@ fun DevLauncherBottomTabsNavigator() {
78
83
  }) {
79
84
  NavHost(
80
85
  navController = bottomTabsNavController,
81
- startDestination = Home,
86
+ startDestination = Routes.Home,
82
87
  enterTransition = {
83
88
  EnterTransition.None
84
89
  },
@@ -86,10 +91,13 @@ fun DevLauncherBottomTabsNavigator() {
86
91
  ExitTransition.None
87
92
  }
88
93
  ) {
89
- composable<Home> {
90
- HomeRoute(navController = mainNavController, onProfileClick = { bottomSheetState.jumpTo(Full) })
94
+ composable<Routes.Home> {
95
+ HomeRoute(navController = mainNavController, onProfileClick = navigateToProfile)
96
+ }
97
+ composable<Routes.Updates> {
98
+ UpdatesRoute(onProfileClick = navigateToProfile)
91
99
  }
92
- composable<Settings> {
100
+ composable<Routes.Settings> {
93
101
  SettingsRoute()
94
102
  }
95
103
  }
@@ -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,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
+ }
@@ -1,24 +1,22 @@
1
1
  package expo.modules.devlauncher.compose.models
2
2
 
3
- import android.util.Log
3
+ import android.content.Context
4
4
  import androidx.compose.runtime.mutableStateOf
5
5
  import androidx.core.net.toUri
6
6
  import androidx.lifecycle.ViewModel
7
7
  import androidx.lifecycle.viewModelScope
8
+ import com.google.mlkit.vision.barcode.common.Barcode
9
+ import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions
10
+ import com.google.mlkit.vision.codescanner.GmsBarcodeScanning
8
11
  import expo.modules.devlauncher.DevLauncherController
9
- import expo.modules.devlauncher.MeQuery
10
12
  import expo.modules.devlauncher.launcher.DevLauncherAppEntry
11
13
  import expo.modules.devlauncher.launcher.errors.DevLauncherErrorInstance
12
- import expo.modules.devlauncher.services.AppService
13
14
  import expo.modules.devlauncher.services.ErrorRegistryService
14
15
  import expo.modules.devlauncher.services.PackagerInfo
15
16
  import expo.modules.devlauncher.services.PackagerService
16
- import expo.modules.devlauncher.services.SessionService
17
- import expo.modules.devlauncher.services.UserState
18
17
  import expo.modules.devlauncher.services.inject
19
18
  import kotlinx.coroutines.flow.launchIn
20
19
  import kotlinx.coroutines.flow.onEach
21
- import kotlinx.coroutines.flow.onStart
22
20
  import kotlinx.coroutines.launch
23
21
 
24
22
  sealed interface HomeAction {
@@ -26,32 +24,26 @@ sealed interface HomeAction {
26
24
  object RefetchRunningApps : HomeAction
27
25
  object ResetRecentlyOpenedApps : HomeAction
28
26
  class NavigateToCrashReport(val crashReport: DevLauncherErrorInstance) : HomeAction
27
+ object ScanQRCode : HomeAction
28
+ object ClearLoadingError : HomeAction
29
29
  }
30
30
 
31
31
  data class HomeState(
32
- val appName: String = "Unknown App",
33
32
  val runningPackagers: Set<PackagerInfo> = emptySet(),
34
33
  val isFetchingPackagers: Boolean = false,
35
- val currentAccount: MeQuery.Account? = null,
36
34
  val recentlyOpenedApps: List<DevLauncherAppEntry> = emptyList(),
37
- val crashReport: DevLauncherErrorInstance? = null
35
+ val crashReport: DevLauncherErrorInstance? = null,
36
+ val loadingError: String? = null
38
37
  )
39
38
 
40
39
  class HomeViewModel() : ViewModel() {
41
40
  val devLauncherController = inject<DevLauncherController>()
42
- val sessionService = inject<SessionService>()
43
41
  val packagerService = inject<PackagerService>()
44
- val appService = inject<AppService>()
45
42
  val errorRegistryService = inject<ErrorRegistryService>()
46
43
 
47
44
  private var _state = mutableStateOf(
48
45
  HomeState(
49
- appName = appService.applicationInfo.appName,
50
46
  runningPackagers = packagerService.runningPackagers.value,
51
- currentAccount = when (val userState = sessionService.user.value) {
52
- UserState.Fetching, UserState.LoggedOut -> null
53
- is UserState.LoggedIn -> userState.selectedAccount
54
- },
55
47
  recentlyOpenedApps = devLauncherController.getRecentlyOpenedApps(),
56
48
  crashReport = errorRegistryService.consumeException()
57
49
  )
@@ -70,18 +62,6 @@ class HomeViewModel() : ViewModel() {
70
62
  }
71
63
  .launchIn(viewModelScope)
72
64
 
73
- sessionService.user.onEach { newUser ->
74
- when (newUser) {
75
- UserState.Fetching, UserState.LoggedOut -> _state.value = _state.value.copy(
76
- currentAccount = null
77
- )
78
-
79
- is UserState.LoggedIn -> _state.value = _state.value.copy(
80
- currentAccount = newUser.selectedAccount
81
- )
82
- }
83
- }.launchIn(viewModelScope)
84
-
85
65
  packagerService.isLoading.onEach { isLoading ->
86
66
  _state.value = _state.value.copy(
87
67
  isFetchingPackagers = isLoading
@@ -96,7 +76,9 @@ class HomeViewModel() : ViewModel() {
96
76
  try {
97
77
  devLauncherController.loadApp(action.url.toUri(), mainActivity = null)
98
78
  } catch (e: Exception) {
99
- Log.e("DevLauncher", "Failed to open app: ${action.url}", e)
79
+ _state.value = _state.value.copy(
80
+ loadingError = e.message ?: "Unknown error"
81
+ )
100
82
  }
101
83
  }
102
84
 
@@ -107,7 +89,32 @@ class HomeViewModel() : ViewModel() {
107
89
  _state.value = _state.value.copy(recentlyOpenedApps = emptyList())
108
90
  }
109
91
 
92
+ is HomeAction.ClearLoadingError -> _state.value = _state.value.copy(loadingError = null)
93
+
110
94
  is HomeAction.NavigateToCrashReport -> IllegalStateException("Navigation action should be handled by the UI layer, not the ViewModel.")
95
+
96
+ is HomeAction.ScanQRCode -> IllegalStateException("QR code scanning should be handled by the UI layer, not the ViewModel.")
111
97
  }
112
98
  }
99
+
100
+ fun scanQRCode(context: Context, onResult: (String) -> Unit, onError: (String) -> Unit) {
101
+ val options = GmsBarcodeScannerOptions.Builder()
102
+ .setBarcodeFormats(Barcode.FORMAT_QR_CODE)
103
+ .build()
104
+
105
+ val scanner = GmsBarcodeScanning.getClient(context, options)
106
+
107
+ scanner.startScan()
108
+ .addOnSuccessListener { barcode ->
109
+ barcode.rawValue?.let {
110
+ onResult(it)
111
+ } ?: onError("No QR code data found")
112
+ }
113
+ .addOnCanceledListener {
114
+ onError("Scanning cancelled")
115
+ }
116
+ .addOnFailureListener { exception ->
117
+ onError("Scanning failed: ${exception.message ?: "Unknown error"}")
118
+ }
119
+ }
113
120
  }
@@ -13,7 +13,9 @@ data class SettingsState(
13
13
  val isShakeEnable: Boolean = true,
14
14
  val isThreeFingerLongPressEnable: Boolean = true,
15
15
  val isKeyCommandEnabled: Boolean = true,
16
- val applicationInfo: ApplicationInfo? = null
16
+ val applicationInfo: ApplicationInfo? = null,
17
+ // TODO @behenate - make true the default for VR
18
+ val showFabAtLaunch: Boolean = false
17
19
  )
18
20
 
19
21
  sealed interface SettingsAction {
@@ -21,6 +23,7 @@ sealed interface SettingsAction {
21
23
  data class ToggleShakeEnable(val newValue: Boolean) : SettingsAction
22
24
  data class ToggleThreeFingerLongPressEnable(val newValue: Boolean) : SettingsAction
23
25
  data class ToggleKeyCommandEnable(val newValue: Boolean) : SettingsAction
26
+ data class ToggleShowFabAtLaunch(val newValue: Boolean) : SettingsAction
24
27
  }
25
28
 
26
29
  class SettingsViewModel(application: Application) : AndroidViewModel(application) {
@@ -33,7 +36,8 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
33
36
  isShakeEnable = menuPreferences.motionGestureEnabled,
34
37
  isThreeFingerLongPressEnable = menuPreferences.touchGestureEnabled,
35
38
  isKeyCommandEnabled = menuPreferences.keyCommandsEnabled,
36
- applicationInfo = appService.applicationInfo
39
+ applicationInfo = appService.applicationInfo,
40
+ showFabAtLaunch = menuPreferences.showFab
37
41
  )
38
42
  )
39
43
 
@@ -45,7 +49,8 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
45
49
  showMenuAtLaunch = menuPreferences.showsAtLaunch,
46
50
  isShakeEnable = menuPreferences.motionGestureEnabled,
47
51
  isThreeFingerLongPressEnable = menuPreferences.touchGestureEnabled,
48
- isKeyCommandEnabled = menuPreferences.keyCommandsEnabled
52
+ isKeyCommandEnabled = menuPreferences.keyCommandsEnabled,
53
+ showFabAtLaunch = menuPreferences.showFab
49
54
  )
50
55
  }
51
56
 
@@ -75,6 +80,10 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
75
80
  is SettingsAction.ToggleKeyCommandEnable -> {
76
81
  menuPreferences.keyCommandsEnabled = action.newValue
77
82
  }
83
+
84
+ is SettingsAction.ToggleShowFabAtLaunch -> {
85
+ menuPreferences.showFab = action.newValue
86
+ }
78
87
  }
79
88
  }
80
89
  }
@@ -20,9 +20,9 @@ import androidx.compose.runtime.setValue
20
20
  import androidx.compose.ui.Alignment
21
21
  import androidx.compose.ui.Modifier
22
22
  import androidx.compose.ui.draw.rotate
23
- import androidx.compose.ui.res.painterResource
24
23
  import androidx.compose.ui.tooling.preview.Preview
25
- import com.composables.core.Icon
24
+ import expo.modules.devlauncher.R
25
+ import expo.modules.devmenu.compose.primitives.DayNighIcon
26
26
  import expo.modules.devmenu.compose.primitives.Text
27
27
  import expo.modules.devmenu.compose.theme.Theme
28
28
 
@@ -52,11 +52,12 @@ fun Accordion(
52
52
  modifier = Modifier
53
53
  .padding(Theme.spacing.medium)
54
54
  ) {
55
- Icon(
56
- painter = painterResource(expo.modules.devmenu.R.drawable._expodevclientcomponents_assets_chevronrighticon),
55
+ DayNighIcon(
56
+ id = R.drawable.chevron_right_icon,
57
57
  contentDescription = "Accordion Arrow",
58
58
  modifier = Modifier
59
59
  .rotate(arrowRotation)
60
+ .size(Theme.sizing.icon.extraSmall)
60
61
  )
61
62
  Spacer(Modifier.size(Theme.spacing.small))
62
63
  Text(
@@ -8,7 +8,6 @@ import androidx.compose.runtime.mutableStateOf
8
8
  import androidx.compose.runtime.remember
9
9
  import androidx.compose.runtime.rememberCoroutineScope
10
10
  import androidx.compose.runtime.setValue
11
- import androidx.compose.ui.graphics.ImageBitmap
12
11
  import androidx.compose.ui.graphics.asImageBitmap
13
12
  import expo.modules.devlauncher.services.ImageLoaderService
14
13
  import expo.modules.devlauncher.services.inject
@@ -20,7 +19,7 @@ fun AsyncImage(
20
19
  ) {
21
20
  val imageLoaderService = inject<ImageLoaderService>()
22
21
  val scope = rememberCoroutineScope()
23
- var imageBitmap by remember { mutableStateOf<ImageBitmap?>(null) }
22
+ var imageBitmap by remember { mutableStateOf(imageLoaderService.loadFromMemory(url)?.asImageBitmap()) }
24
23
 
25
24
  LaunchedEffect(url) {
26
25
  scope.launch {