-
Notifications
You must be signed in to change notification settings - Fork 188
Migration from 2.x
This page is kept in sync with
docs/MIGRATION.mdin the repository; the two say the same thing.
3.0 is a full rewrite and keeps none of the 2.x API. The old
io.github.petterpx:floatingx / io.github.petterpx:floatingx-compose coordinates are no longer
published, and the com.petterp.floatingx.* package has been split into five module sub-packages.
This page maps every "2.x way → 3.0 way".
// 2.x
implementation 'io.github.petterpx:floatingx:2.3.7'
implementation 'io.github.petterpx:floatingx-compose:2.3.7'
// 3.0: take what you need, core comes transitively with the others
implementation "io.github.petterpx:floatingx-core:3.0.0"
implementation "io.github.petterpx:floatingx-app:3.0.0"
implementation "io.github.petterpx:floatingx-system:3.0.0"
implementation "io.github.petterpx:floatingx-scope:3.0.0"
implementation "io.github.petterpx:floatingx-compose:3.0.0"| 2.x package | 3.0 package |
|---|---|
com.petterp.floatingx.FloatingX |
com.petterp.floatingx.core.FloatingX |
com.petterp.floatingx.assist.* |
com.petterp.floatingx.core.config.* / core.layout.* / core.gesture.*
|
com.petterp.floatingx.assist.helper.FxAppHelper |
com.petterp.floatingx.app.AppHost + com.petterp.floatingx.system.SystemHost
|
com.petterp.floatingx.assist.helper.FxScopeHelper |
com.petterp.floatingx.scope.ViewGroupHost / FragmentHost
|
com.petterp.floatingx.listener.control.IFxControl |
com.petterp.floatingx.core.FxControl |
No more AndroidManifest edits. 2.x made you declare SYSTEM_ALERT_WINDOW yourself; in 3.0
floatingx-system already declares the permission and the transparent permission-request Activity
in its own manifest, and floatingx-app registers the Activity tracker from a ContentProvider
(which is why setContext is gone too).
// 2.x: one install for everything, scopeType picks the implementation
FloatingX.install {
setContext(context)
setLayout(R.layout.item_floating)
setScopeType(FxScopeType.SYSTEM_AUTO)
}.show()
// 3.0: install must name a host, and the host is "where it hangs"
FloatingX.install("tag") {
layout(R.layout.item_floating)
systemHost(app) { fallback(AppHost.builder(app).build()) } // equivalent to SYSTEM_AUTO
}.show()| 2.x | 3.0 |
|---|---|
FxScopeType.APP |
appHost(app) { … } |
FxScopeType.SYSTEM |
systemHost(app) { … } (no fallback) |
FxScopeType.SYSTEM_AUTO |
systemHost(app) { fallback(AppHost.builder(app).build()) } |
setContext(context) |
Removed. appHost(app) / systemHost(app) take the Application directly |
setTag("x") |
FloatingX.install("x") { … } (the tag is install's first argument) |
| 2.x | 3.0 |
|---|---|
setLayout(R.layout.x) |
layout(R.layout.x) |
setLayoutView(view) |
view(view), or view { ctx -> … } to build it from a context on the spot |
updateView(resource) / updateView(view) / updateView(provider)
|
control.setContent(FxContent.layout(id)) / FxContent.view(v) / FxContent.provider {}
|
updateViewContent { holder -> … } |
control.updateContent { holder -> … } (works before show in 3.0) |
| 2.x | 3.0 |
|---|---|
setGravity(FxGravity.RIGHT_OR_BOTTOM) |
anchor(FxGravity.BOTTOM_END) |
setX(x) / setY(y) / setXY(x, y)
|
anchor(gravity, dx, dy) (offset from the anchored edge, no longer an absolute coordinate) |
setOffsetXY(x, y) |
anchor(gravity, dx = x, dy = y) |
setBorderMargin(t, l, b, r) |
margin(left, top, right, bottom) |
setTopBorderMargin(t) and the other three |
margin(top = t) (Kotlin named arguments) |
setEnableScrollOutsideScreen(true) |
overflow(top = true, bottom = true, left = true, right = true), per edge |
setEnableSafeArea(false) |
safeArea = false |
setEnableEdgeAdsorption(true) |
adsorb(FxAdsorb.Edges(setOf(FxEdge.START, FxEdge.END))) |
setEdgeAdsorbDirection(FxAdsorbDirection.LEFT_OR_RIGHT) |
adsorb(FxAdsorb.horizontal()); vertical() for top/bottom, all() for all four |
setEdgeOffset(edge) |
margin(...) (the inset after adsorption is just the margin, no separate offset) |
setEnableHalfHide(true) + setHalfHidePercent(0.3f)
|
adsorb(FxAdsorb.Edges(edges, halfHide = FxHalfHide(0.3f))), and start/end may differ via FxHalfHide(start, end)
|
| (rebound always on) |
FxAdsorb.Edges(rebound = true/false), can be turned off |
setSaveDirectionImpl(IFxConfigStorage) |
persist(FxSpStorage(context)), or implement FxStorage yourself |
FloatingX.clearConfig() |
FxStorage.clear(key); the framework builds the key as "$tag:$orientation" (portrait and landscape remembered separately) |
| 2.x | 3.0 |
|---|---|
FxGravity.DEFAULT / LEFT_OR_TOP
|
FxGravity.TOP_START |
FxGravity.TOP_OR_CENTER |
FxGravity.TOP_CENTER |
FxGravity.RIGHT_OR_TOP |
FxGravity.TOP_END |
FxGravity.LEFT_OR_CENTER |
FxGravity.CENTER_START |
FxGravity.CENTER |
FxGravity.CENTER |
FxGravity.RIGHT_OR_CENTER |
FxGravity.CENTER_END |
FxGravity.LEFT_OR_BOTTOM |
FxGravity.BOTTOM_START |
FxGravity.BOTTOM_OR_CENTER |
FxGravity.BOTTOM_CENTER |
FxGravity.RIGHT_OR_BOTTOM |
FxGravity.BOTTOM_END |
START / END are logical directions and flip automatically under RTL (2.x's LEFT/RIGHT were
hard-coded physical directions).
2.x packed "can it be clicked, can it be dragged" into the single FxDisplayMode enum; 3.0 splits
that into independent switches.
| 2.x | 3.0 |
|---|---|
setDisplayMode(FxDisplayMode.Normal) |
The default, FxGesture.Normal
|
setDisplayMode(FxDisplayMode.ClickOnly) |
gesture { drag = FxDrag.DISABLED }, or gesture(FxGesture.ClickOnly)
|
setDisplayMode(FxDisplayMode.DisplayOnly) |
gesture(FxGesture.DisplayOnly) (touchable = false, full pass-through) |
setEnableTouch(false) |
gesture { touchable = false } |
| (not in 2.x) |
gesture { drag = FxDrag.AFTER_LONG_PRESS } drag only after a long press, or FxGesture.LongPressToDrag
|
| (not in 2.x) |
gesture { dragRegion = FxRegion.child(R.id.header) } drags start only on the named child view |
(2.x left it to IFxTouchListener) |
gesture { childPriority = FxChildPriority.AUTO / PARENT / CHILD }, the conflict policy with scrollable children |
setTouchListener(IFxTouchListener) |
Drag policy moved into gesture {}; for callbacks only use FxListener.onDragStart/onDrag/onDragEnd
|
setScrollListener(...) (deprecated) |
Same as above |
setOnClickListener(listener) / setOnLongClickListener(listener)
|
control.addListener(object : FxListener { override fun onClick(control, view) {} }) |
| 2.x | 3.0 |
|---|---|
setViewLifecycle(IFxViewLifecycle) (deprecated) / addViewLifecycle(...)
|
control.addListener(FxListener): onAttach / onDetach / onShow / onHide / onCancel
|
| (2.x had no position callback) |
FxListener.onPositionChanged(control, anchor), tells you which edge it adsorbed to |
| Custom container behaviour meant patching the library |
addFeature(FxFeature), a container behaviour plugin (Location / Gesture / Animation / ModalScrim built in) |
| 2.x | 3.0 |
|---|---|
addInstallBlackClass(vararg Class) |
appHost(app) { blacklist(X::class.java) }, matched with isInstance, so subclasses are hit too
|
addInstallBlackClass(vararg String) |
appHost(app) { blacklist("com.x.YActivity") } (exact fully-qualified name) |
addInstallWhiteClass(...) |
appHost(app) { whitelist(...) } |
setEnableAllInstall(false) + a whitelist |
Just write whitelist(...): once a whitelist exists, only whitelisted pages show the window |
| (2.x had no custom rule) |
appHost(app) { filter { activity -> !activity.isFinishing } }, callable repeatedly; all must pass |
| (2.x always used DecorView) | appHost(app) { attachTo(AppAttachTarget.DECOR / CONTENT) } |
| (2.x used the Application context, so Material components crashed) | appHost(app) { theme(R.style.Theme_App) } |
| 2.x | 3.0 |
|---|---|
setPermissionInterceptor(IFxPermissionInterceptor) |
systemHost(app) { permission(FxPermissionStrategy.manual { request -> … }) }; request.proceed() / useFallback() / deny()
|
| (2.x had no "skip the check") | permission(FxPermissionStrategy.skip()) |
setManagerParams(FrameLayout.LayoutParams) |
systemHost(app) { layoutParams { it.type = …; it.flags = … } }, editing WindowManager.LayoutParams directly |
setEnableKeyBoardAdapt(true, ...) |
systemHost(app) { keyboard(R.id.etInput) }, registered by EditText id |
setKeyBackListener(IKeyBackListener) |
systemHost(app) { onBackPressed { true } } |
| No retry after a permission denial | (control.host as? SystemHost)?.retryPermission() |
// 2.x
ScopeHelper.builder {
setLayout(R.layout.item_floating)
}.toControl(activity) // or toControl(fragment) / toControl(viewGroup)
private val scopeFx by createFx {
setLayout(R.layout.item_floating)
build().toControl(this)
}
// 3.0
val fx = activity.fxScope("tag") { layout(R.layout.item_floating) } // call after setContentView
val fx = viewGroup.fxScope("tag") { layout(R.layout.item_floating) }
val fx = fragment.fxScope("tag") { layout(R.layout.item_floating) } // fine inside onCreate
fx.show()| 2.x | 3.0 |
|---|---|
ScopeHelper.builder{}.toControl(activity) |
activity.fxScope {} |
ScopeHelper.builder{}.toControl(fragment) |
fragment.fxScope {} |
ScopeHelper.builder{}.toControl(viewGroup) |
viewGroup.fxScope {} |
The createFx { … } delegate |
Just fxScope {} (it returns FxControl; keep it in a field yourself) |
toControl(...) from Java |
FloatingX.create(config, ViewGroupHost.of(viewGroup), "tag") |
Local windows are still not registered: they never appear in FloatingX.controls() and their
lifetime belongs to the caller. New automatic cleanup: Activity.fxScope cancels itself when the
page is destroyed on API 29+, and Fragment.fxScope cancels itself when the fragment is destroyed.
// 2.x: flip a switch on AppHelper, then hand it a ComposeView yourself
FloatingX.install {
setContext(context)
enableComposeSupport()
setLayoutView(ComposeView(context).apply { setContent { … } })
}
// 3.0: the content is a composable
FloatingX.install("compose") {
compose { control ->
val state by control.stateFlow().collectAsState()
…
}
appHost(app)
}.show()| 2.x | 3.0 |
|---|---|
enableComposeSupport() + setLayoutView(ComposeView)
|
compose { control -> … } |
| The owner lived on the host Activity (lost on page change) | Every window owns an FxComposeOwner, so viewModel() / rememberSaveable survive page changes |
| (not in 2.x) |
control.stateFlow() / control.positionFlow()
|
| 2.x | 3.0 |
|---|---|
control.updateConfig { … } (IFxConfigControl) |
control.update { … } (the same FxConfigScope DSL; anything you don't set keeps its old value) |
control.isShow() |
control.isShowing |
control.getX() / getY()
|
control.position (FxPoint, identical semantics for all three hosts: screen coords of the content's top-left) |
control.getView() / getViewHolder()
|
control.contentView / control.holder
|
control.getManagerView() |
No equivalent: the container belongs to core and is no longer exposed |
control.move(x, y) / move(x, y, useAnimation)
|
control.moveTo(x, y) / moveTo(x, y, animate)
|
control.moveByVector(x, y) |
control.moveBy(dx, dy) / moveBy(dx, dy, animate)
|
setEnableAnimation(true) + setAnimationImpl(FxAnimation)
|
animation(FxAnimations.fade()) / animation(FxAnimations.scale()) / animation(your own FxAnimation) (pass null to disable) |
setEnableLog(true, "tag") |
enableLog("tag") (completely silent unless called) |
The FloatingX registry: install(tag) / create(tag) / control(tag) / controlOrNull(tag) /
controls() / isInstalled(tag) / uninstall(tag) / uninstallAll().
tag defaults to FloatingX.DEFAULT_TAG.
| 2.x | Why |
|---|---|
Position "force fix" / assisted location (enableAssistLocation) |
3.0 stores an anchor rather than absolute coordinates and recomputes on size / usable-area changes, so no per-device fix switch is needed |
setTagActivityLifecycle(IFxProxyTagActivityLifecycle) |
The hook proxying the host Activity's lifecycle is gone; to observe attachment use FxListener.onAttach/onDetach and control.attachedActivity
|
| Multi-process | Out of scope. The FloatingX registry is per-process, so a child process sees a separate, empty registry (#129) |
Container APIs beyond FxViewHolder (getManagerView and friends) |
The container belongs to core and is no longer public API |
- Swap the dependency coordinates and imports; delete the permission declaration you added to
AndroidManifestfor the window. - Add a host inside
install {}(appHost/systemHost/viewGroupHost), and deletesetContext/setScopeType/setTag. -
setGravity+setOffsetXY→ a singleanchor(gravity, dx, dy); turnsetX/setYinto offsets from the anchored edge. -
setDisplayMode/setEnableTouch/setTouchListener→gesture {}. -
setOnClickListener/addViewLifecycle→control.addListener(FxListener). -
ScopeHelper.builder{}.toControl(x)→x.fxScope {}. -
enableComposeSupport()+ComposeView→compose {}. -
updateConfig {}→update {};updateView→setContent;updateViewContent→updateContent.
Start at Getting Started, see every config option in Configuration, or go back to Home.