peachsim 0.1.669 → 0.1.670
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.
- package/cli/commands/control.ts +8 -5
- package/cli/commands/inspect/core.ts +7 -5
- package/cli/commands/inspect/wait-selector.ts +2 -11
- package/cli/commands/inspect.ts +6 -59
- package/cli/commands/kotlin.ts +6 -4
- package/cli/commands/skills.ts +28 -7
- package/cli/commands/upgrade.ts +18 -3
- package/cli/main.ts +21 -0
- package/cli/output-redaction.ts +182 -0
- package/cli/parse-args.ts +1 -0
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +1 -1
- package/dist-lib/cloud.mjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +29 -19
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +260 -17
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/kotlin-host.cjs +2 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +259 -16
- package/dist-lib/native-app-identity.cjs +1 -1
- package/dist-lib/native-app-identity.mjs +1 -1
- package/dist-lib/native-firebase-metadata.cjs +115 -0
- package/dist-lib/native-firebase-metadata.mjs +86 -0
- package/dist-lib/plist.cjs +1 -1
- package/dist-lib/plist.mjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
- package/dist-lib/sdk.cjs +2 -2
- package/dist-lib/sdk.mjs +2 -2
- package/dist-lib/skills.cjs +479 -203
- package/dist-lib/swift-compile.cjs +1 -1
- package/dist-lib/swift-compile.mjs +1 -1
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +21 -13
- package/dist-lib/vite.cjs +1 -1
- package/kotlin/AppSources/androidx/activity/ActivityFlags.kt +9 -0
- package/kotlin/AppSources/androidx/activity/BackEventCompat.kt +25 -4
- package/kotlin/AppSources/androidx/activity/ComponentActivity.kt +15 -4
- package/kotlin/AppSources/androidx/activity/EdgeToEdge.kt +12 -8
- package/kotlin/AppSources/androidx/activity/ExperimentalActivityApi.kt +6 -0
- package/kotlin/AppSources/androidx/activity/OnBackPressedCallback.kt +50 -9
- package/kotlin/AppSources/androidx/activity/OnBackPressedDispatcher.kt +67 -89
- package/kotlin/AppSources/androidx/activity/compose/BackHandler.kt +55 -24
- package/kotlin/AppSources/androidx/activity/compose/internal/BackHandlerCompat.kt +38 -0
- package/kotlin/AppSources/androidx/activity/compose/internal/BackHandlerDispatcherCompat.kt +27 -0
- package/kotlin/AppSources/dev/peach/app/PeachApp.kt +5 -5
- package/kotlin/ComposeSources/PeachComposeHost.kt +31 -7
- package/kotlin/fixtures/kotlin-app/app/build.gradle.kts +1 -1
- package/kotlin/fixtures/kotlin-app-variants/Browse.kt +73 -0
- package/kotlin/fixtures/kotlin-app-variants/Focus.kt +62 -0
- package/kotlin/fixtures/kotlin-app-variants/Lists.kt +57 -0
- package/kotlin/fixtures/kotlin-app-variants/Nav.kt +47 -0
- package/kotlin/fixtures/kotlin-app-variants/Nested.kt +64 -0
- package/kotlin/fixtures/kotlin-app-variants/Pan.kt +38 -0
- package/kotlin/fixtures/kotlin-app-variants/Pickers.kt +44 -0
- package/kotlin/fixtures/kotlin-app-variants/Press.kt +43 -0
- package/kotlin/fixtures/kotlin-app-variants/Transition.kt +69 -0
- package/kotlin/fixtures/kotlin-app-variants/ViewConfig.kt +22 -0
- package/kotlin/toolchain.json +30 -2
- package/package.json +8 -1
- package/skills/contrast/SKILL.md +2 -1
- package/skills/peach-debug/SKILL.md +2 -2
- package/skills/peach-test/SKILL.md +3 -3
- package/src/expo-swift-host.ts +13 -4
- package/src/kotlin-app.ts +35 -16
- package/src/kotlin-host.ts +1 -0
- package/src/metro-plugin.ts +24 -0
- package/src/native-firebase-config.ts +202 -21
- package/src/native-firebase-metadata.ts +114 -0
- package/src/swift-host.ts +30 -15
- package/src/vite-plugin.ts +46 -2
- package/kotlin/AppSources/androidx/activity/Cancellable.kt +0 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.670 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.670 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.670 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
package/dist-lib/swift.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.670 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
package/dist-lib/vite-swift.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.670 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -1718,13 +1718,11 @@ async function compile(packagePath, entry, stamp, moduleRootOverride, hosted, sn
|
|
|
1718
1718
|
stamp,
|
|
1719
1719
|
package: content
|
|
1720
1720
|
};
|
|
1721
|
-
const toolchain =
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
throw new Error(`swift toolchain manifest at ${moduleRoot} has no release`);
|
|
1727
|
-
}
|
|
1721
|
+
const toolchain = localSwiftToolchain(moduleRoot);
|
|
1722
|
+
if (!toolchain)
|
|
1723
|
+
throw new Error(
|
|
1724
|
+
`the Swift toolchain ${moduleRoot}/toolchain.json names is not installed under ~/Library/Developer/Toolchains`
|
|
1725
|
+
);
|
|
1728
1726
|
const artifact = await compileSwiftProjectUnit(snapshotCompileInput(snapshot, entry), {
|
|
1729
1727
|
// the compile rewrites Sources/App in its root before swiftpm builds, and
|
|
1730
1728
|
// its lock orders one process only: two vites on one root (a lane's dev
|
|
@@ -1737,14 +1735,24 @@ async function compile(packagePath, entry, stamp, moduleRootOverride, hosted, sn
|
|
|
1737
1735
|
(0, import_node_crypto3.createHash)("sha256").update(`${moduleRoot}\0${import_node_path5.default.resolve(packagePath)}`).digest("hex").slice(0, 16)
|
|
1738
1736
|
),
|
|
1739
1737
|
moduleRoot,
|
|
1740
|
-
|
|
1741
|
-
toolchainBin: import_node_path5.default.join(
|
|
1742
|
-
import_node_os3.default.homedir(),
|
|
1743
|
-
`Library/Developer/Toolchains/${release}.xctoolchain/usr/bin`
|
|
1744
|
-
)
|
|
1738
|
+
...toolchain
|
|
1745
1739
|
});
|
|
1746
1740
|
return { ...artifact, stamp, package: content };
|
|
1747
1741
|
}
|
|
1742
|
+
function localSwiftToolchain(moduleRoot) {
|
|
1743
|
+
const manifest = import_node_path5.default.join(moduleRoot, "toolchain.json");
|
|
1744
|
+
if (!import_node_fs4.default.existsSync(manifest)) return null;
|
|
1745
|
+
const toolchain = JSON.parse(import_node_fs4.default.readFileSync(manifest, "utf8"));
|
|
1746
|
+
const release = toolchain && typeof toolchain === "object" ? Reflect.get(toolchain, "release") : void 0;
|
|
1747
|
+
if (typeof release !== "string") {
|
|
1748
|
+
throw new Error(`swift toolchain manifest at ${moduleRoot} has no release`);
|
|
1749
|
+
}
|
|
1750
|
+
const toolchainBin = import_node_path5.default.join(
|
|
1751
|
+
import_node_os3.default.homedir(),
|
|
1752
|
+
`Library/Developer/Toolchains/${release}.xctoolchain/usr/bin`
|
|
1753
|
+
);
|
|
1754
|
+
return import_node_fs4.default.existsSync(import_node_path5.default.join(toolchainBin, "swift")) ? { release, toolchainBin } : null;
|
|
1755
|
+
}
|
|
1748
1756
|
async function compileRemote(packagePath, entry, snapshot, hosted) {
|
|
1749
1757
|
const auth = hosted ? null : resolveCliAuth();
|
|
1750
1758
|
if (!hosted && (!auth || auth.kind === "github")) {
|
package/dist-lib/vite.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.670 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
package androidx.activity
|
|
2
|
+
|
|
3
|
+
// androidx.activity 1.13.0's ActivityFlags
|
|
4
|
+
@ExperimentalActivityApi
|
|
5
|
+
object ActivityFlags {
|
|
6
|
+
// a lifecycle-bound back callback keeps its place in the dispatch queue
|
|
7
|
+
// across ON_STOP and ON_START, rather than moving to the top on each start
|
|
8
|
+
var isOnBackPressedLifecycleOrderMaintained: Boolean = true
|
|
9
|
+
}
|
|
@@ -1,17 +1,38 @@
|
|
|
1
1
|
package androidx.activity
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import androidx.navigationevent.NavigationEvent
|
|
4
|
+
|
|
5
|
+
// androidx.activity 1.13.0's BackEventCompat: one predictive back gesture sample
|
|
4
6
|
class BackEventCompat(
|
|
5
7
|
val touchX: Float,
|
|
6
8
|
val touchY: Float,
|
|
7
9
|
val progress: Float,
|
|
8
10
|
val swipeEdge: Int,
|
|
11
|
+
val frameTimeMillis: Long = 0,
|
|
9
12
|
) {
|
|
13
|
+
constructor(navigationEvent: NavigationEvent) : this(
|
|
14
|
+
touchX = navigationEvent.touchX,
|
|
15
|
+
touchY = navigationEvent.touchY,
|
|
16
|
+
progress = navigationEvent.progress,
|
|
17
|
+
swipeEdge = navigationEvent.swipeEdge,
|
|
18
|
+
frameTimeMillis = navigationEvent.frameTimeMillis,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
fun toNavigationEvent(): NavigationEvent = NavigationEvent(
|
|
22
|
+
touchX = touchX,
|
|
23
|
+
touchY = touchY,
|
|
24
|
+
progress = progress,
|
|
25
|
+
swipeEdge = swipeEdge,
|
|
26
|
+
frameTimeMillis = frameTimeMillis,
|
|
27
|
+
)
|
|
28
|
+
|
|
10
29
|
override fun toString(): String =
|
|
11
|
-
"BackEventCompat
|
|
30
|
+
"BackEventCompat(touchX=$touchX, touchY=$touchY, progress=$progress, " +
|
|
31
|
+
"swipeEdge=$swipeEdge, frameTimeMillis=$frameTimeMillis)"
|
|
12
32
|
|
|
13
33
|
companion object {
|
|
14
|
-
const val EDGE_LEFT = 0
|
|
15
|
-
const val EDGE_RIGHT = 1
|
|
34
|
+
const val EDGE_LEFT: Int = 0
|
|
35
|
+
const val EDGE_RIGHT: Int = 1
|
|
36
|
+
const val EDGE_NONE: Int = 2
|
|
16
37
|
}
|
|
17
38
|
}
|
|
@@ -14,6 +14,9 @@ import androidx.lifecycle.ViewModelStoreOwner
|
|
|
14
14
|
import androidx.lifecycle.enableSavedStateHandles
|
|
15
15
|
import androidx.lifecycle.viewmodel.CreationExtras
|
|
16
16
|
import androidx.lifecycle.viewmodel.MutableCreationExtras
|
|
17
|
+
import androidx.navigationevent.DirectNavigationEventInput
|
|
18
|
+
import androidx.navigationevent.NavigationEventDispatcher
|
|
19
|
+
import androidx.navigationevent.NavigationEventDispatcherOwner
|
|
17
20
|
import androidx.savedstate.SavedStateRegistry
|
|
18
21
|
import androidx.savedstate.SavedStateRegistryController
|
|
19
22
|
import androidx.savedstate.SavedStateRegistryOwner
|
|
@@ -25,7 +28,8 @@ import androidx.savedstate.SavedStateRegistryOwner
|
|
|
25
28
|
// API 29, and destroy clears the view model store unless the activity is
|
|
26
29
|
// changing configurations.
|
|
27
30
|
open class ComponentActivity : Activity(), LifecycleOwner, ViewModelStoreOwner,
|
|
28
|
-
HasDefaultViewModelProviderFactory, SavedStateRegistryOwner, OnBackPressedDispatcherOwner
|
|
31
|
+
HasDefaultViewModelProviderFactory, SavedStateRegistryOwner, OnBackPressedDispatcherOwner,
|
|
32
|
+
NavigationEventDispatcherOwner {
|
|
29
33
|
private val lifecycleRegistry = LifecycleRegistry(this)
|
|
30
34
|
private val savedStateRegistryController = SavedStateRegistryController.create(this)
|
|
31
35
|
|
|
@@ -92,16 +96,23 @@ open class ComponentActivity : Activity(), LifecycleOwner, ViewModelStoreOwner,
|
|
|
92
96
|
savedStateRegistryController.performSave(outState.state)
|
|
93
97
|
}
|
|
94
98
|
|
|
95
|
-
//
|
|
96
|
-
// back to Activity's own
|
|
99
|
+
// onBackPressed's back, an input of the navigation event dispatcher: an
|
|
100
|
+
// enabled handler gets it first; without one it falls back to Activity's own
|
|
101
|
+
private val onBackPressedInput: DirectNavigationEventInput by lazy {
|
|
102
|
+
DirectNavigationEventInput().also(navigationEventDispatcher::addInput)
|
|
103
|
+
}
|
|
104
|
+
|
|
97
105
|
override fun onBackPressed() {
|
|
98
|
-
|
|
106
|
+
onBackPressedInput.backCompleted()
|
|
99
107
|
}
|
|
100
108
|
|
|
101
109
|
final override val onBackPressedDispatcher: OnBackPressedDispatcher by lazy {
|
|
102
110
|
OnBackPressedDispatcher { super@ComponentActivity.onBackPressed() }
|
|
103
111
|
}
|
|
104
112
|
|
|
113
|
+
override val navigationEventDispatcher: NavigationEventDispatcher
|
|
114
|
+
get() = onBackPressedDispatcher.eventDispatcher
|
|
115
|
+
|
|
105
116
|
override fun dispatchActivityPostCreated() = lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
|
|
106
117
|
override fun dispatchActivityPostStarted() = lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
|
|
107
118
|
override fun dispatchActivityPostResumed() = lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
|
@@ -8,11 +8,12 @@ import android.view.WindowManager
|
|
|
8
8
|
import androidx.core.view.WindowCompat
|
|
9
9
|
import androidx.core.view.WindowInsetsControllerCompat
|
|
10
10
|
|
|
11
|
-
// androidx.activity 1.
|
|
12
|
-
// where it takes
|
|
13
|
-
// bars
|
|
14
|
-
//
|
|
15
|
-
//
|
|
11
|
+
// androidx.activity 1.13.0's EdgeToEdge as it runs on Peach's API 37 device,
|
|
12
|
+
// where it takes EdgeToEdgeApi35: the decor stops fitting system windows, the
|
|
13
|
+
// bars turn transparent, navigation bar contrast follows an auto style, the
|
|
14
|
+
// bars' appearance follows dark mode and the window lays out into every
|
|
15
|
+
// cutout. a style's non-transparent scrim is an androidx.core ProtectionLayout
|
|
16
|
+
// view added to the decor, which Peach's ComposeView-only window cannot host.
|
|
16
17
|
|
|
17
18
|
// DecorView's light scrim and the platform's dark scrim
|
|
18
19
|
internal val DefaultLightScrim = Color.argb(0xe6, 0xFF, 0xFF, 0xFF)
|
|
@@ -26,9 +27,12 @@ fun ComponentActivity.enableEdgeToEdge(
|
|
|
26
27
|
val statusBarIsDark = statusBarStyle.detectDarkMode(view.resources)
|
|
27
28
|
val navigationBarIsDark = navigationBarStyle.detectDarkMode(view.resources)
|
|
28
29
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
|
29
|
-
window.statusBarColor =
|
|
30
|
-
window.navigationBarColor =
|
|
31
|
-
|
|
30
|
+
window.statusBarColor = Color.TRANSPARENT
|
|
31
|
+
window.navigationBarColor = Color.TRANSPARENT
|
|
32
|
+
check(
|
|
33
|
+
statusBarStyle.getScrimWithEnforcedContrast(statusBarIsDark) == Color.TRANSPARENT &&
|
|
34
|
+
navigationBarStyle.getScrimWithEnforcedContrast(navigationBarIsDark) == Color.TRANSPARENT
|
|
35
|
+
) { "enableEdgeToEdge with a non-transparent scrim adds a ProtectionLayout view; Peach composes only ComposeView content" }
|
|
32
36
|
window.isNavigationBarContrastEnforced = navigationBarStyle.nightMode == UiModeManager.MODE_NIGHT_AUTO
|
|
33
37
|
WindowInsetsControllerCompat(window, view).run {
|
|
34
38
|
isAppearanceLightStatusBars = !statusBarIsDark
|
|
@@ -1,28 +1,69 @@
|
|
|
1
1
|
package androidx.activity
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import androidx.navigationevent.NavigationEvent
|
|
4
|
+
import androidx.navigationevent.NavigationEventHandler
|
|
5
|
+
import androidx.navigationevent.NavigationEventInfo
|
|
6
|
+
|
|
7
|
+
// androidx.activity 1.13.0's OnBackPressedCallback: each registration is a
|
|
8
|
+
// NavigationEventHandler on the dispatcher's NavigationEventDispatcher
|
|
4
9
|
abstract class OnBackPressedCallback(enabled: Boolean) {
|
|
10
|
+
private val eventHandlers: MutableList<OnBackPressedEventHandler> = mutableListOf()
|
|
11
|
+
|
|
5
12
|
var isEnabled: Boolean = enabled
|
|
6
13
|
set(value) {
|
|
7
14
|
field = value
|
|
8
|
-
|
|
15
|
+
// a handler registered without a lifecycle is always lifecycle active
|
|
16
|
+
for (callback in eventHandlers) callback.isBackEnabled = callback.isLifecycleActive && value
|
|
9
17
|
}
|
|
10
18
|
|
|
11
|
-
private val
|
|
12
|
-
internal var enabledChangedCallback: (() -> Unit)? = null
|
|
19
|
+
private val closeables = mutableListOf<AutoCloseable>()
|
|
13
20
|
|
|
14
|
-
fun remove()
|
|
21
|
+
fun remove() {
|
|
22
|
+
for (closeable in closeables.toList()) closeable.close()
|
|
23
|
+
closeables.clear()
|
|
24
|
+
for (callback in eventHandlers) callback.remove()
|
|
25
|
+
eventHandlers.clear()
|
|
26
|
+
}
|
|
15
27
|
|
|
16
28
|
open fun handleOnBackStarted(backEvent: BackEventCompat) {}
|
|
17
29
|
open fun handleOnBackProgressed(backEvent: BackEventCompat) {}
|
|
18
30
|
abstract fun handleOnBackPressed()
|
|
19
31
|
open fun handleOnBackCancelled() {}
|
|
20
32
|
|
|
21
|
-
internal fun
|
|
22
|
-
|
|
33
|
+
internal fun addCloseable(closeable: AutoCloseable) {
|
|
34
|
+
closeables += closeable
|
|
23
35
|
}
|
|
24
36
|
|
|
25
|
-
internal fun
|
|
26
|
-
|
|
37
|
+
internal fun createNavigationEventHandler(info: NavigationEventInfo): OnBackPressedEventHandler {
|
|
38
|
+
val newHandler = OnBackPressedEventHandler(onBackPressedCallback = this, info)
|
|
39
|
+
eventHandlers += newHandler
|
|
40
|
+
return newHandler
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
internal class OnBackPressedEventHandler(
|
|
44
|
+
private val onBackPressedCallback: OnBackPressedCallback,
|
|
45
|
+
info: NavigationEventInfo,
|
|
46
|
+
) : NavigationEventHandler<NavigationEventInfo>(initialInfo = info, isBackEnabled = onBackPressedCallback.isEnabled) {
|
|
47
|
+
var isLifecycleActive: Boolean = true
|
|
48
|
+
set(value) {
|
|
49
|
+
field = value
|
|
50
|
+
isBackEnabled = value && onBackPressedCallback.isEnabled
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
override fun onBackStarted(event: NavigationEvent) {
|
|
54
|
+
onBackPressedCallback.handleOnBackStarted(BackEventCompat(event))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
override fun onBackProgressed(event: NavigationEvent) {
|
|
58
|
+
onBackPressedCallback.handleOnBackProgressed(BackEventCompat(event))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
override fun onBackCompleted() {
|
|
62
|
+
onBackPressedCallback.handleOnBackPressed()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
override fun onBackCancelled() {
|
|
66
|
+
onBackPressedCallback.handleOnBackCancelled()
|
|
67
|
+
}
|
|
27
68
|
}
|
|
28
69
|
}
|
|
@@ -1,125 +1,101 @@
|
|
|
1
1
|
package androidx.activity
|
|
2
2
|
|
|
3
|
-
import java.lang.Runnable
|
|
4
3
|
import androidx.lifecycle.Lifecycle
|
|
5
4
|
import androidx.lifecycle.LifecycleEventObserver
|
|
6
5
|
import androidx.lifecycle.LifecycleOwner
|
|
6
|
+
import androidx.navigationevent.NavigationEvent
|
|
7
|
+
import androidx.navigationevent.NavigationEventDispatcher
|
|
8
|
+
import androidx.navigationevent.NavigationEventInfo
|
|
9
|
+
import androidx.navigationevent.NavigationEventInput
|
|
10
|
+
import java.lang.Runnable
|
|
7
11
|
|
|
8
|
-
// androidx.activity 1.
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
+
// androidx.activity 1.13.0's OnBackPressedDispatcher, a NavigationEventInput
|
|
13
|
+
// over its own NavigationEventDispatcher. the device's back dispatch stands in
|
|
14
|
+
// for its OnBackInvokedDispatcher inputs: a window with predictive back gives
|
|
15
|
+
// this dispatcher the back only while it has an enabled handler
|
|
16
|
+
// (hasEnabledCallbacks), as the registered OnBackInvokedCallback does.
|
|
12
17
|
class OnBackPressedDispatcher(
|
|
13
18
|
private val fallbackOnBackPressed: Runnable?,
|
|
14
19
|
private val onHasEnabledCallbacksChanged: ((Boolean) -> Unit)?,
|
|
15
20
|
) {
|
|
16
|
-
private val onBackPressedCallbacks = ArrayDeque<OnBackPressedCallback>()
|
|
17
|
-
private var inProgressCallback: OnBackPressedCallback? = null
|
|
18
21
|
private var hasEnabledCallbacks = false
|
|
22
|
+
private val eventInput by lazy { OnBackPressedEventInput() }
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
internal val eventDispatcher
|
|
25
|
+
get() = eventInput.dispatcher
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
val hadEnabledCallbacks = hasEnabledCallbacks
|
|
24
|
-
val hasEnabledCallbacks = onBackPressedCallbacks.any { it.isEnabled }
|
|
25
|
-
this.hasEnabledCallbacks = hasEnabledCallbacks
|
|
26
|
-
if (hasEnabledCallbacks != hadEnabledCallbacks)
|
|
27
|
-
onHasEnabledCallbacksChanged?.invoke(hasEnabledCallbacks)
|
|
28
|
-
}
|
|
27
|
+
constructor(fallbackOnBackPressed: Runnable? = null) : this(fallbackOnBackPressed, null)
|
|
29
28
|
|
|
30
29
|
fun addCallback(onBackPressedCallback: OnBackPressedCallback) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
internal fun addCancellableCallback(onBackPressedCallback: OnBackPressedCallback): Cancellable {
|
|
35
|
-
onBackPressedCallbacks.add(onBackPressedCallback)
|
|
36
|
-
val cancellable = OnBackPressedCancellable(onBackPressedCallback)
|
|
37
|
-
onBackPressedCallback.addCancellable(cancellable)
|
|
38
|
-
updateEnabledCallbacks()
|
|
39
|
-
onBackPressedCallback.enabledChangedCallback = ::updateEnabledCallbacks
|
|
40
|
-
return cancellable
|
|
30
|
+
val info = OnBackPressedCallbackInfo(onBackPressedCallback)
|
|
31
|
+
val handler = onBackPressedCallback.createNavigationEventHandler(info)
|
|
32
|
+
eventDispatcher.addHandler(handler)
|
|
41
33
|
}
|
|
42
34
|
|
|
35
|
+
@OptIn(ExperimentalActivityApi::class)
|
|
43
36
|
fun addCallback(owner: LifecycleOwner, onBackPressedCallback: OnBackPressedCallback) {
|
|
44
37
|
val lifecycle = owner.lifecycle
|
|
45
38
|
if (lifecycle.currentState === Lifecycle.State.DESTROYED) return
|
|
46
|
-
onBackPressedCallback
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
val info = OnBackPressedCallbackInfo(onBackPressedCallback, owner)
|
|
40
|
+
val eventHandler = onBackPressedCallback.createNavigationEventHandler(info)
|
|
41
|
+
// the order flag adds the handler once, so its place in the queue holds,
|
|
42
|
+
// and toggles it with the lifecycle; the legacy order re-adds it on each
|
|
43
|
+
// start, which moves it to the top
|
|
44
|
+
if (ActivityFlags.isOnBackPressedLifecycleOrderMaintained) {
|
|
45
|
+
eventHandler.isLifecycleActive = false
|
|
46
|
+
eventDispatcher.addHandler(eventHandler)
|
|
47
|
+
}
|
|
48
|
+
val observer = object : LifecycleEventObserver {
|
|
49
|
+
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
|
|
50
|
+
when (event) {
|
|
51
|
+
Lifecycle.Event.ON_START ->
|
|
52
|
+
if (ActivityFlags.isOnBackPressedLifecycleOrderMaintained) eventHandler.isLifecycleActive = true
|
|
53
|
+
else eventDispatcher.addHandler(eventHandler)
|
|
54
|
+
Lifecycle.Event.ON_STOP ->
|
|
55
|
+
if (ActivityFlags.isOnBackPressedLifecycleOrderMaintained) eventHandler.isLifecycleActive = false
|
|
56
|
+
else eventHandler.remove()
|
|
57
|
+
Lifecycle.Event.ON_DESTROY -> {
|
|
58
|
+
eventHandler.remove()
|
|
59
|
+
lifecycle.removeObserver(this)
|
|
60
|
+
}
|
|
61
|
+
else -> Unit
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
lifecycle.addObserver(observer)
|
|
66
|
+
onBackPressedCallback.addCloseable(AutoCloseable { lifecycle.removeObserver(observer) })
|
|
49
67
|
}
|
|
50
68
|
|
|
51
69
|
fun hasEnabledCallbacks(): Boolean = hasEnabledCallbacks
|
|
52
70
|
|
|
53
71
|
fun dispatchOnBackStarted(backEvent: BackEventCompat) {
|
|
54
|
-
|
|
55
|
-
if (inProgressCallback != null) dispatchOnBackCancelled()
|
|
56
|
-
inProgressCallback = callback
|
|
57
|
-
callback?.handleOnBackStarted(backEvent)
|
|
72
|
+
eventInput.backStarted(backEvent.toNavigationEvent())
|
|
58
73
|
}
|
|
59
74
|
|
|
60
75
|
fun dispatchOnBackProgressed(backEvent: BackEventCompat) {
|
|
61
|
-
|
|
62
|
-
callback?.handleOnBackProgressed(backEvent)
|
|
76
|
+
eventInput.backProgressed(backEvent.toNavigationEvent())
|
|
63
77
|
}
|
|
64
78
|
|
|
65
79
|
fun onBackPressed() {
|
|
66
|
-
|
|
67
|
-
inProgressCallback = null
|
|
68
|
-
if (callback != null) {
|
|
69
|
-
callback.handleOnBackPressed()
|
|
70
|
-
return
|
|
71
|
-
}
|
|
72
|
-
fallbackOnBackPressed?.run()
|
|
80
|
+
eventInput.backCompleted()
|
|
73
81
|
}
|
|
74
82
|
|
|
75
83
|
fun dispatchOnBackCancelled() {
|
|
76
|
-
|
|
77
|
-
inProgressCallback = null
|
|
78
|
-
callback?.handleOnBackCancelled()
|
|
84
|
+
eventInput.backCancelled()
|
|
79
85
|
}
|
|
80
86
|
|
|
81
|
-
private inner class
|
|
82
|
-
|
|
83
|
-
) : Cancellable {
|
|
84
|
-
override fun cancel() {
|
|
85
|
-
onBackPressedCallbacks.remove(onBackPressedCallback)
|
|
86
|
-
if (inProgressCallback == onBackPressedCallback) {
|
|
87
|
-
onBackPressedCallback.handleOnBackCancelled()
|
|
88
|
-
inProgressCallback = null
|
|
89
|
-
}
|
|
90
|
-
onBackPressedCallback.removeCancellable(this)
|
|
91
|
-
onBackPressedCallback.enabledChangedCallback?.invoke()
|
|
92
|
-
onBackPressedCallback.enabledChangedCallback = null
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// a lifecycle-bound callback receives back only between ON_START and ON_STOP
|
|
97
|
-
private inner class LifecycleOnBackPressedCancellable(
|
|
98
|
-
private val lifecycle: Lifecycle,
|
|
99
|
-
private val onBackPressedCallback: OnBackPressedCallback,
|
|
100
|
-
) : LifecycleEventObserver, Cancellable {
|
|
101
|
-
private var currentCancellable: Cancellable? = null
|
|
102
|
-
|
|
103
|
-
init {
|
|
104
|
-
lifecycle.addObserver(this)
|
|
105
|
-
}
|
|
87
|
+
private inner class OnBackPressedEventInput : NavigationEventInput() {
|
|
88
|
+
val dispatcher = NavigationEventDispatcher { fallbackOnBackPressed?.run() }.also { it.addInput(this) }
|
|
106
89
|
|
|
107
|
-
override fun
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
} else if (event === Lifecycle.Event.ON_STOP) {
|
|
111
|
-
currentCancellable?.cancel()
|
|
112
|
-
} else if (event === Lifecycle.Event.ON_DESTROY) {
|
|
113
|
-
cancel()
|
|
114
|
-
}
|
|
90
|
+
override fun onHasEnabledHandlersChanged(hasEnabledHandlers: Boolean) {
|
|
91
|
+
hasEnabledCallbacks = hasEnabledHandlers
|
|
92
|
+
onHasEnabledCallbacksChanged?.invoke(hasEnabledHandlers)
|
|
115
93
|
}
|
|
116
94
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
currentCancellable = null
|
|
122
|
-
}
|
|
95
|
+
fun backStarted(event: NavigationEvent) = dispatchOnBackStarted(event)
|
|
96
|
+
fun backProgressed(event: NavigationEvent) = dispatchOnBackProgressed(event)
|
|
97
|
+
fun backCancelled() = dispatchOnBackCancelled()
|
|
98
|
+
fun backCompleted() = dispatchOnBackCompleted()
|
|
123
99
|
}
|
|
124
100
|
}
|
|
125
101
|
|
|
@@ -128,17 +104,19 @@ class OnBackPressedDispatcher(
|
|
|
128
104
|
fun OnBackPressedDispatcher.addCallback(
|
|
129
105
|
owner: LifecycleOwner? = null,
|
|
130
106
|
enabled: Boolean = true,
|
|
131
|
-
onBackPressed: OnBackPressedCallback.() -> Unit
|
|
107
|
+
onBackPressed: OnBackPressedCallback.() -> Unit,
|
|
132
108
|
): OnBackPressedCallback {
|
|
133
109
|
val callback = object : OnBackPressedCallback(enabled) {
|
|
134
110
|
override fun handleOnBackPressed() {
|
|
135
111
|
onBackPressed()
|
|
136
112
|
}
|
|
137
113
|
}
|
|
138
|
-
if (owner != null)
|
|
139
|
-
|
|
140
|
-
} else {
|
|
141
|
-
addCallback(callback)
|
|
142
|
-
}
|
|
114
|
+
if (owner != null) addCallback(owner, callback)
|
|
115
|
+
else addCallback(callback)
|
|
143
116
|
return callback
|
|
144
117
|
}
|
|
118
|
+
|
|
119
|
+
private data class OnBackPressedCallbackInfo(
|
|
120
|
+
val callback: OnBackPressedCallback,
|
|
121
|
+
val owner: LifecycleOwner? = null,
|
|
122
|
+
) : NavigationEventInfo()
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
package androidx.activity.compose
|
|
2
2
|
|
|
3
|
-
import androidx.activity.
|
|
3
|
+
import androidx.activity.ActivityFlags
|
|
4
|
+
import androidx.activity.ExperimentalActivityApi
|
|
4
5
|
import androidx.activity.OnBackPressedDispatcherOwner
|
|
6
|
+
import androidx.activity.compose.internal.BackHandlerCompat
|
|
7
|
+
import androidx.activity.compose.internal.BackHandlerDispatcherCompat
|
|
5
8
|
import androidx.compose.runtime.Composable
|
|
6
9
|
import androidx.compose.runtime.DisposableEffect
|
|
7
10
|
import androidx.compose.runtime.ProvidedValue
|
|
8
11
|
import androidx.compose.runtime.SideEffect
|
|
9
12
|
import androidx.compose.runtime.compositionLocalOf
|
|
10
|
-
import androidx.compose.runtime.
|
|
13
|
+
import androidx.compose.runtime.currentCompositeKeyHashCode
|
|
11
14
|
import androidx.compose.runtime.remember
|
|
12
|
-
import androidx.compose.
|
|
13
|
-
import androidx.
|
|
15
|
+
import androidx.lifecycle.compose.LifecycleStartEffect
|
|
16
|
+
import androidx.navigationevent.NavigationEventDispatcherOwner
|
|
17
|
+
import androidx.navigationevent.NavigationEventInfo
|
|
18
|
+
import androidx.navigationevent.compose.LocalNavigationEventDispatcherOwner
|
|
14
19
|
|
|
15
|
-
// activity-compose 1.
|
|
20
|
+
// activity-compose 1.13.0's LocalOnBackPressedDispatcherOwner. on the device
|
|
16
21
|
// an unprovided value resolves from the view tree or the context, both of
|
|
17
22
|
// which reach the activity that set the content; the window host provides that
|
|
18
23
|
// activity here.
|
|
@@ -28,28 +33,54 @@ object LocalOnBackPressedDispatcherOwner {
|
|
|
28
33
|
LocalOnBackPressedDispatcherOwner.provides(dispatcherOwner)
|
|
29
34
|
}
|
|
30
35
|
|
|
31
|
-
// activity-compose 1.
|
|
36
|
+
// activity-compose 1.13.0's BackHandler: a handler on the navigation event
|
|
37
|
+
// dispatcher when one is provided, else a callback on the back dispatcher
|
|
38
|
+
@OptIn(ExperimentalActivityApi::class)
|
|
32
39
|
@Composable
|
|
33
40
|
fun BackHandler(enabled: Boolean = true, onBack: () -> Unit) {
|
|
34
|
-
val
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
val owner = LocalNavigationEventDispatcherOwner.current
|
|
42
|
+
?: LocalOnBackPressedDispatcherOwner.current
|
|
43
|
+
?: error(
|
|
44
|
+
"No NavigationEventDispatcherOwner was provided via " +
|
|
45
|
+
"LocalNavigationEventDispatcherOwner and no OnBackPressedDispatcherOwner was " +
|
|
46
|
+
"provided via LocalOnBackPressedDispatcherOwner. Please provide one of the two."
|
|
47
|
+
)
|
|
48
|
+
val dispatcher = remember(owner) {
|
|
49
|
+
BackHandlerDispatcherCompat(
|
|
50
|
+
(owner as? NavigationEventDispatcherOwner)?.navigationEventDispatcher,
|
|
51
|
+
(owner as? OnBackPressedDispatcherOwner)?.onBackPressedDispatcher,
|
|
52
|
+
)
|
|
44
53
|
}
|
|
45
|
-
val
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
val compositeKey = currentCompositeKeyHashCode
|
|
55
|
+
val handler = remember(dispatcher, compositeKey) { ComposeBackHandler(BackHandlerInfo(owner, compositeKey)) }
|
|
56
|
+
if (ActivityFlags.isOnBackPressedLifecycleOrderMaintained) {
|
|
57
|
+
SideEffect { handler.currentOnBackCompleted = onBack }
|
|
58
|
+
LifecycleStartEffect(enabled, handler) {
|
|
59
|
+
handler.isBackEnabled = enabled
|
|
60
|
+
onStopOrDispose { handler.isBackEnabled = false }
|
|
61
|
+
}
|
|
62
|
+
DisposableEffect(dispatcher, handler) {
|
|
63
|
+
dispatcher.addHandler(handler)
|
|
64
|
+
onDispose { dispatcher.removeHandler(handler) }
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
SideEffect {
|
|
68
|
+
handler.isBackEnabled = enabled
|
|
69
|
+
handler.currentOnBackCompleted = onBack
|
|
70
|
+
}
|
|
71
|
+
LifecycleStartEffect(dispatcher, handler) {
|
|
72
|
+
dispatcher.addHandler(handler)
|
|
73
|
+
onStopOrDispose { dispatcher.removeHandler(handler) }
|
|
53
74
|
}
|
|
54
75
|
}
|
|
55
76
|
}
|
|
77
|
+
|
|
78
|
+
private class ComposeBackHandler(info: BackHandlerInfo) : BackHandlerCompat(info) {
|
|
79
|
+
var currentOnBackCompleted: () -> Unit = {}
|
|
80
|
+
|
|
81
|
+
override fun onBackCompleted() {
|
|
82
|
+
currentOnBackCompleted()
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private data class BackHandlerInfo(val owner: Any, val compositeKey: Long) : NavigationEventInfo()
|