@erkamyaman/capacitor-foldable 8.1.0 → 8.3.0

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 (36) hide show
  1. package/ErkamyamanCapacitorFoldable.podspec +1 -0
  2. package/Package.swift +2 -1
  3. package/README.md +131 -12
  4. package/android/src/main/kotlin/io/github/erkamyaman/foldable/FoldablePlugin.kt +31 -1
  5. package/dist/docs.json +176 -4
  6. package/dist/esm/css.d.ts +6 -1
  7. package/dist/esm/css.js +16 -2
  8. package/dist/esm/css.js.map +1 -1
  9. package/dist/esm/definitions.d.ts +95 -4
  10. package/dist/esm/definitions.js.map +1 -1
  11. package/dist/esm/polyfill.js +6 -1
  12. package/dist/esm/polyfill.js.map +1 -1
  13. package/dist/esm/web.d.ts +7 -0
  14. package/dist/esm/web.js +7 -1
  15. package/dist/esm/web.js.map +1 -1
  16. package/dist/plugin.cjs.js +29 -4
  17. package/dist/plugin.cjs.js.map +1 -1
  18. package/dist/plugin.js +29 -4
  19. package/dist/plugin.js.map +1 -1
  20. package/ionic-tabs.css +2 -1
  21. package/ios/Sources/FoldablePlugin/Foldable.swift +41 -1
  22. package/ios/Sources/FoldablePlugin/FoldableBridgeViewController.swift +41 -0
  23. package/ios/Sources/FoldablePlugin/FoldablePlugin.swift +41 -0
  24. package/ios/Sources/FoldablePlugin/ReservedRegionFoldProvider.swift +41 -11
  25. package/ios/Sources/FoldablePlugin/Resources/PrivacyInfo.xcprivacy +14 -0
  26. package/ios/Sources/FoldablePlugin/UIKitFoldSource.swift +3 -3
  27. package/ios/Tests/FoldablePluginTests/FoldableTests.swift +4 -0
  28. package/ios/Tests/FoldablePluginTests/ReservedRegionFoldProviderTests.swift +18 -0
  29. package/package.json +7 -6
  30. package/skills/capacitor-foldable/SKILL.md +87 -0
  31. package/skills/capacitor-foldable/references/android.md +52 -0
  32. package/skills/capacitor-foldable/references/api.md +112 -0
  33. package/skills/capacitor-foldable/references/css-layout.md +127 -0
  34. package/skills/capacitor-foldable/references/ionic.md +50 -0
  35. package/skills/capacitor-foldable/references/iphone-duo.md +54 -0
  36. package/skills/capacitor-foldable/references/testing.md +53 -0
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sources":["esm/css.js","esm/keyboard.js","esm/segments.js","esm/polyfill.js","esm/index.js","esm/size-class.js","esm/web.js"],"sourcesContent":["export const CSS_CLASSES = [\n 'horizontal-viewport-segments-2',\n 'vertical-viewport-segments-2',\n 'device-posture-continuous',\n 'device-posture-folded',\n];\nexport const BAR_CLASSES = ['vertical-bars-leading', 'vertical-bars-trailing'];\nexport const FOLD_CLASSES = ['fold-vertical', 'fold-horizontal'];\nexport const FOLD_VARIABLES = ['--fold-left', '--fold-top', '--fold-width', '--fold-height'];\nexport function foldCssFor(hingeBounds, hingeOrientation) {\n if (!hingeBounds || !hingeOrientation)\n return { variables: {}, className: null };\n return {\n variables: {\n '--fold-left': `${hingeBounds.x}px`,\n '--fold-top': `${hingeBounds.y}px`,\n '--fold-width': `${hingeBounds.width}px`,\n '--fold-height': `${hingeBounds.height}px`,\n },\n className: `fold-${hingeOrientation}`,\n };\n}\nexport function barClassFor(edge) {\n return edge ? `vertical-bars-${edge}` : null;\n}\nconst TAB_BAR_MARGIN = 24;\nconst CAMERA_GAP = 15;\nexport function verticalTabBarBottom(cameras, viewportHeight) {\n const below = cameras.filter((camera) => camera.y > viewportHeight / 2);\n const camera = below.sort((a, b) => a.width * a.height - b.width * b.height)[0];\n if (!camera)\n return TAB_BAR_MARGIN;\n return Math.max(TAB_BAR_MARGIN, Math.round(viewportHeight - camera.y + CAMERA_GAP));\n}\nexport function cssFor(segments, posture) {\n const stacked = segments.length > 1 && segments[1].y > segments[0].y;\n const variables = {};\n segments.forEach((segment, index) => {\n const suffix = stacked ? `0-${index}` : `${index}-0`;\n variables[`--viewport-segment-top-${suffix}`] = `${segment.y}px`;\n variables[`--viewport-segment-left-${suffix}`] = `${segment.x}px`;\n variables[`--viewport-segment-bottom-${suffix}`] = `${segment.y + segment.height}px`;\n variables[`--viewport-segment-right-${suffix}`] = `${segment.x + segment.width}px`;\n variables[`--viewport-segment-width-${suffix}`] = `${segment.width}px`;\n variables[`--viewport-segment-height-${suffix}`] = `${segment.height}px`;\n });\n const classes = [`device-posture-${posture}`];\n if (segments.length > 1) {\n classes.push(stacked ? 'vertical-viewport-segments-2' : 'horizontal-viewport-segments-2');\n }\n return { variables, classes };\n}\n//# sourceMappingURL=css.js.map","export const KEYBOARD_CLASS = 'foldable-keyboard-open';\nconst SHOW_EVENTS = ['keyboardWillShow', 'keyboardDidShow', 'ionKeyboardDidShow'];\nconst isEditing = (element) => !!element &&\n (element.isContentEditable || ['INPUT', 'TEXTAREA', 'SELECT'].includes(element.tagName));\nexport function takeOverKeyboard(target, doc) {\n const classes = doc.documentElement.classList;\n for (const name of SHOW_EVENTS) {\n target.addEventListener(name, (event) => {\n event.stopImmediatePropagation();\n if (isEditing(doc.activeElement))\n classes.add(KEYBOARD_CLASS);\n }, true);\n }\n target.addEventListener('keyboardWillHide', () => classes.remove(KEYBOARD_CLASS), true);\n}\n//# sourceMappingURL=keyboard.js.map","const EDGE_TOLERANCE = 1;\nexport function splitViewport(fold, width, height) {\n const whole = [{ x: 0, y: 0, width, height }];\n const hinge = fold === null || fold === void 0 ? void 0 : fold.hingeBounds;\n if (!(fold === null || fold === void 0 ? void 0 : fold.isSeparating) || !hinge)\n return whole;\n if (fold.hingeOrientation === 'vertical') {\n const right = hinge.x + hinge.width;\n const spansHeight = hinge.y <= EDGE_TOLERANCE && hinge.y + hinge.height >= height - EDGE_TOLERANCE;\n if (!spansHeight || hinge.x <= 0 || right >= width)\n return whole;\n return [\n { x: 0, y: 0, width: hinge.x, height },\n { x: right, y: 0, width: width - right, height },\n ];\n }\n const bottom = hinge.y + hinge.height;\n const spansWidth = hinge.x <= EDGE_TOLERANCE && hinge.x + hinge.width >= width - EDGE_TOLERANCE;\n if (!spansWidth || hinge.y <= 0 || bottom >= height)\n return whole;\n return [\n { x: 0, y: 0, width, height: hinge.y },\n { x: 0, y: bottom, width, height: height - bottom },\n ];\n}\n//# sourceMappingURL=segments.js.map","import { Capacitor } from '@capacitor/core';\nimport { BAR_CLASSES, barClassFor, CSS_CLASSES, cssFor, FOLD_CLASSES, FOLD_VARIABLES, foldCssFor, verticalTabBarBottom, } from './css';\nimport { takeOverKeyboard } from './keyboard';\nimport { splitViewport } from './segments';\nclass DevicePosturePolyfill extends EventTarget {\n constructor() {\n super(...arguments);\n this.onchange = null;\n this.current = 'continuous';\n }\n get type() {\n return this.current;\n }\n update(type) {\n var _a;\n if (type === this.current)\n return;\n this.current = type;\n const event = new Event('change');\n (_a = this.onchange) === null || _a === void 0 ? void 0 : _a.call(this, event);\n this.dispatchEvent(event);\n }\n}\nconst postureOf = (fold) => (fold === null || fold === void 0 ? void 0 : fold.state) === 'half-opened' ? 'folded' : 'continuous';\nlet installation = null;\nexport function install(plugin, options = {}) {\n if (!Capacitor.isNativePlatform())\n return Promise.resolve();\n installation !== null && installation !== void 0 ? installation : (installation = run(plugin, options));\n return installation;\n}\nasync function run(plugin, options) {\n if (options.ionicKeyboard)\n takeOverKeyboard(window, document);\n let fold = null;\n const currentSegments = () => splitViewport(fold, window.innerWidth, window.innerHeight);\n if (!('Viewport' in window)) {\n Object.defineProperty(window, 'viewport', {\n configurable: true,\n value: {\n get segments() {\n return Object.freeze(currentSegments().map((rect) => new DOMRect(rect.x, rect.y, rect.width, rect.height)));\n },\n },\n });\n }\n const posture = 'DevicePosture' in window ? null : new DevicePosturePolyfill();\n if (posture) {\n Object.defineProperty(navigator, 'devicePosture', { configurable: true, value: posture });\n }\n const root = document.documentElement;\n let variables = [];\n const applyCss = () => {\n var _a;\n const css = cssFor(currentSegments(), postureOf(fold));\n for (const name of variables)\n root.style.removeProperty(name);\n variables = Object.keys(css.variables);\n for (const name of variables)\n root.style.setProperty(name, css.variables[name]);\n for (const name of CSS_CLASSES)\n root.classList.toggle(name, css.classes.includes(name));\n const foldCss = foldCssFor(fold === null || fold === void 0 ? void 0 : fold.hingeBounds, fold === null || fold === void 0 ? void 0 : fold.hingeOrientation);\n for (const name of FOLD_VARIABLES) {\n if (foldCss.variables[name])\n root.style.setProperty(name, foldCss.variables[name]);\n else\n root.style.removeProperty(name);\n }\n for (const name of FOLD_CLASSES)\n root.classList.toggle(name, name === foldCss.className);\n root.style.setProperty('--vertical-tab-bar-bottom', `${verticalTabBarBottom((_a = fold === null || fold === void 0 ? void 0 : fold.cameraBounds) !== null && _a !== void 0 ? _a : [], window.innerHeight)}px`);\n };\n const apply = (next) => {\n fold = next;\n posture === null || posture === void 0 ? void 0 : posture.update(postureOf(next));\n applyCss();\n };\n const applyBars = ({ verticalBarEdge }) => {\n const active = barClassFor(verticalBarEdge);\n for (const name of BAR_CLASSES)\n root.classList.toggle(name, name === active);\n };\n window.addEventListener('resize', applyCss);\n await plugin.addListener('foldStateChange', apply);\n await plugin.addListener('barPlacementChange', applyBars);\n apply(await plugin.getFoldState());\n applyBars(await plugin.getBarPlacement());\n}\n//# sourceMappingURL=polyfill.js.map","import { registerPlugin } from '@capacitor/core';\nimport { install } from './polyfill';\nconst Foldable = registerPlugin('Foldable', {\n web: () => import('./web').then((m) => new m.FoldableWeb()),\n});\nconst installFoldablePolyfill = (options) => install(Foldable, options);\nexport * from './definitions';\nexport { Foldable, installFoldablePolyfill };\n//# sourceMappingURL=index.js.map","const MEDIUM_WIDTH = 600;\nconst EXPANDED_WIDTH = 840;\nconst LARGE_WIDTH = 1200;\nconst EXTRA_LARGE_WIDTH = 1600;\nconst MEDIUM_HEIGHT = 480;\nconst EXPANDED_HEIGHT = 900;\nfunction widthClassOf(width) {\n if (width >= EXTRA_LARGE_WIDTH)\n return 'extraLarge';\n if (width >= LARGE_WIDTH)\n return 'large';\n if (width >= EXPANDED_WIDTH)\n return 'expanded';\n if (width >= MEDIUM_WIDTH)\n return 'medium';\n return 'compact';\n}\nfunction heightClassOf(height) {\n if (height >= EXPANDED_HEIGHT)\n return 'expanded';\n if (height >= MEDIUM_HEIGHT)\n return 'medium';\n return 'compact';\n}\nexport function sizeClassOf(width, height) {\n return {\n horizontal: width >= MEDIUM_WIDTH ? 'regular' : 'compact',\n vertical: height >= MEDIUM_HEIGHT ? 'regular' : 'compact',\n widthClass: widthClassOf(width),\n heightClass: heightClassOf(height),\n };\n}\n//# sourceMappingURL=size-class.js.map","import { WebPlugin } from '@capacitor/core';\nimport { sizeClassOf } from './size-class';\nexport class FoldableWeb extends WebPlugin {\n constructor() {\n super();\n this.sizeClass = sizeClassOf(window.innerWidth, window.innerHeight);\n window.addEventListener('resize', () => {\n const next = sizeClassOf(window.innerWidth, window.innerHeight);\n if (JSON.stringify(next) === JSON.stringify(this.sizeClass))\n return;\n this.sizeClass = next;\n this.notifyListeners('sizeClassChange', next);\n });\n }\n async isDeviceFoldable() {\n return { foldable: false, supportsTabletop: false };\n }\n async getFoldState() {\n return { state: 'flat', isSeparating: false, posture: 'flat' };\n }\n async getHingeAngle() {\n return { angle: null };\n }\n async getSizeClass() {\n return sizeClassOf(window.innerWidth, window.innerHeight);\n }\n async getDisplayModes() {\n return { rearDisplay: 'unsupported', dualScreen: 'unsupported' };\n }\n async getBarPlacement() {\n return { verticalBarEdge: null };\n }\n async startRearDisplay() {\n throw this.unavailable('Rear display mode is only available on Android.');\n }\n stopRearDisplay() {\n return Promise.resolve();\n }\n async startDualScreen() {\n throw this.unavailable('Dual-screen mode is only available on Android.');\n }\n stopDualScreen() {\n return Promise.resolve();\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["Capacitor","registerPlugin","WebPlugin"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","sources":["esm/css.js","esm/keyboard.js","esm/segments.js","esm/polyfill.js","esm/index.js","esm/size-class.js","esm/web.js"],"sourcesContent":["export const CSS_CLASSES = [\n 'horizontal-viewport-segments-2',\n 'vertical-viewport-segments-2',\n 'device-posture-continuous',\n 'device-posture-folded',\n];\nexport const BAR_CLASSES = ['vertical-bars-leading', 'vertical-bars-trailing'];\nexport const FOLD_CLASSES = ['fold-vertical', 'fold-horizontal'];\nexport const FOLD_VARIABLES = [\n '--fold-left',\n '--fold-top',\n '--fold-width',\n '--fold-height',\n '--fold-margin-top',\n '--fold-margin-right',\n '--fold-margin-bottom',\n '--fold-margin-left',\n];\nexport function foldCssFor(hingeBounds, hingeOrientation, hingeMargins) {\n if (!hingeBounds || !hingeOrientation)\n return { variables: {}, className: null };\n const margins = hingeMargins !== null && hingeMargins !== void 0 ? hingeMargins : { top: 0, right: 0, bottom: 0, left: 0 };\n return {\n variables: {\n '--fold-left': `${hingeBounds.x}px`,\n '--fold-top': `${hingeBounds.y}px`,\n '--fold-width': `${hingeBounds.width}px`,\n '--fold-height': `${hingeBounds.height}px`,\n '--fold-margin-top': `${margins.top}px`,\n '--fold-margin-right': `${margins.right}px`,\n '--fold-margin-bottom': `${margins.bottom}px`,\n '--fold-margin-left': `${margins.left}px`,\n },\n className: `fold-${hingeOrientation}`,\n };\n}\nexport function barClassFor(edge) {\n return edge ? `vertical-bars-${edge}` : null;\n}\nconst TAB_BAR_MARGIN = 24;\nconst CAMERA_GAP = 15;\nexport function verticalTabBarBottom(cameras, viewportHeight) {\n const below = cameras.filter((camera) => camera.y > viewportHeight / 2);\n const camera = below.sort((a, b) => a.width * a.height - b.width * b.height)[0];\n if (!camera)\n return TAB_BAR_MARGIN;\n return Math.max(TAB_BAR_MARGIN, Math.round(viewportHeight - camera.y + CAMERA_GAP));\n}\nexport function cssFor(segments, posture) {\n const stacked = segments.length > 1 && segments[1].y > segments[0].y;\n const variables = {};\n segments.forEach((segment, index) => {\n const suffix = stacked ? `0-${index}` : `${index}-0`;\n variables[`--viewport-segment-top-${suffix}`] = `${segment.y}px`;\n variables[`--viewport-segment-left-${suffix}`] = `${segment.x}px`;\n variables[`--viewport-segment-bottom-${suffix}`] = `${segment.y + segment.height}px`;\n variables[`--viewport-segment-right-${suffix}`] = `${segment.x + segment.width}px`;\n variables[`--viewport-segment-width-${suffix}`] = `${segment.width}px`;\n variables[`--viewport-segment-height-${suffix}`] = `${segment.height}px`;\n });\n const classes = [`device-posture-${posture}`];\n if (segments.length > 1) {\n classes.push(stacked ? 'vertical-viewport-segments-2' : 'horizontal-viewport-segments-2');\n }\n return { variables, classes };\n}\n//# sourceMappingURL=css.js.map","export const KEYBOARD_CLASS = 'foldable-keyboard-open';\nconst SHOW_EVENTS = ['keyboardWillShow', 'keyboardDidShow', 'ionKeyboardDidShow'];\nconst isEditing = (element) => !!element &&\n (element.isContentEditable || ['INPUT', 'TEXTAREA', 'SELECT'].includes(element.tagName));\nexport function takeOverKeyboard(target, doc) {\n const classes = doc.documentElement.classList;\n for (const name of SHOW_EVENTS) {\n target.addEventListener(name, (event) => {\n event.stopImmediatePropagation();\n if (isEditing(doc.activeElement))\n classes.add(KEYBOARD_CLASS);\n }, true);\n }\n target.addEventListener('keyboardWillHide', () => classes.remove(KEYBOARD_CLASS), true);\n}\n//# sourceMappingURL=keyboard.js.map","const EDGE_TOLERANCE = 1;\nexport function splitViewport(fold, width, height) {\n const whole = [{ x: 0, y: 0, width, height }];\n const hinge = fold === null || fold === void 0 ? void 0 : fold.hingeBounds;\n if (!(fold === null || fold === void 0 ? void 0 : fold.isSeparating) || !hinge)\n return whole;\n if (fold.hingeOrientation === 'vertical') {\n const right = hinge.x + hinge.width;\n const spansHeight = hinge.y <= EDGE_TOLERANCE && hinge.y + hinge.height >= height - EDGE_TOLERANCE;\n if (!spansHeight || hinge.x <= 0 || right >= width)\n return whole;\n return [\n { x: 0, y: 0, width: hinge.x, height },\n { x: right, y: 0, width: width - right, height },\n ];\n }\n const bottom = hinge.y + hinge.height;\n const spansWidth = hinge.x <= EDGE_TOLERANCE && hinge.x + hinge.width >= width - EDGE_TOLERANCE;\n if (!spansWidth || hinge.y <= 0 || bottom >= height)\n return whole;\n return [\n { x: 0, y: 0, width, height: hinge.y },\n { x: 0, y: bottom, width, height: height - bottom },\n ];\n}\n//# sourceMappingURL=segments.js.map","import { Capacitor } from '@capacitor/core';\nimport { BAR_CLASSES, barClassFor, CSS_CLASSES, cssFor, FOLD_CLASSES, FOLD_VARIABLES, foldCssFor, verticalTabBarBottom, } from './css';\nimport { takeOverKeyboard } from './keyboard';\nimport { splitViewport } from './segments';\nclass DevicePosturePolyfill extends EventTarget {\n constructor() {\n super(...arguments);\n this.onchange = null;\n this.current = 'continuous';\n }\n get type() {\n return this.current;\n }\n update(type) {\n var _a;\n if (type === this.current)\n return;\n this.current = type;\n const event = new Event('change');\n (_a = this.onchange) === null || _a === void 0 ? void 0 : _a.call(this, event);\n this.dispatchEvent(event);\n }\n}\nconst postureOf = (fold) => (fold === null || fold === void 0 ? void 0 : fold.state) === 'half-opened' ? 'folded' : 'continuous';\nlet installation = null;\nexport function install(plugin, options = {}) {\n if (!Capacitor.isNativePlatform())\n return Promise.resolve();\n installation !== null && installation !== void 0 ? installation : (installation = run(plugin, options));\n return installation;\n}\nasync function run(plugin, options) {\n if (options.ionicKeyboard)\n takeOverKeyboard(window, document);\n let fold = null;\n const currentSegments = () => splitViewport(fold, window.innerWidth, window.innerHeight);\n if (!('Viewport' in window)) {\n Object.defineProperty(window, 'viewport', {\n configurable: true,\n value: {\n get segments() {\n return Object.freeze(currentSegments().map((rect) => new DOMRect(rect.x, rect.y, rect.width, rect.height)));\n },\n },\n });\n }\n const posture = 'DevicePosture' in window ? null : new DevicePosturePolyfill();\n if (posture) {\n Object.defineProperty(navigator, 'devicePosture', { configurable: true, value: posture });\n }\n const root = document.documentElement;\n let variables = [];\n const applyCss = () => {\n var _a;\n const css = cssFor(currentSegments(), postureOf(fold));\n for (const name of variables)\n root.style.removeProperty(name);\n variables = Object.keys(css.variables);\n for (const name of variables)\n root.style.setProperty(name, css.variables[name]);\n for (const name of CSS_CLASSES)\n root.classList.toggle(name, css.classes.includes(name));\n const foldCss = foldCssFor(fold === null || fold === void 0 ? void 0 : fold.hingeBounds, fold === null || fold === void 0 ? void 0 : fold.hingeOrientation, fold === null || fold === void 0 ? void 0 : fold.hingeMargins);\n for (const name of FOLD_VARIABLES) {\n if (foldCss.variables[name])\n root.style.setProperty(name, foldCss.variables[name]);\n else\n root.style.removeProperty(name);\n }\n for (const name of FOLD_CLASSES)\n root.classList.toggle(name, name === foldCss.className);\n root.style.setProperty('--vertical-tab-bar-bottom', `${verticalTabBarBottom((_a = fold === null || fold === void 0 ? void 0 : fold.cameraBounds) !== null && _a !== void 0 ? _a : [], window.innerHeight)}px`);\n };\n const apply = (next) => {\n fold = next;\n posture === null || posture === void 0 ? void 0 : posture.update(postureOf(next));\n applyCss();\n // The web view can report the previous window size for a moment after the\n // app moves between displays, so measure again once it has settled.\n requestAnimationFrame(applyCss);\n setTimeout(applyCss, 300);\n };\n const applyBars = ({ verticalBarEdge }) => {\n const active = barClassFor(verticalBarEdge);\n for (const name of BAR_CLASSES)\n root.classList.toggle(name, name === active);\n };\n await plugin.addListener('foldingChange', ({ folding }) => root.classList.toggle('folding', folding));\n window.addEventListener('resize', applyCss);\n await plugin.addListener('foldStateChange', apply);\n await plugin.addListener('barPlacementChange', applyBars);\n apply(await plugin.getFoldState());\n applyBars(await plugin.getBarPlacement());\n}\n//# sourceMappingURL=polyfill.js.map","import { registerPlugin } from '@capacitor/core';\nimport { install } from './polyfill';\nconst Foldable = registerPlugin('Foldable', {\n web: () => import('./web').then((m) => new m.FoldableWeb()),\n});\nconst installFoldablePolyfill = (options) => install(Foldable, options);\nexport * from './definitions';\nexport { Foldable, installFoldablePolyfill };\n//# sourceMappingURL=index.js.map","const MEDIUM_WIDTH = 600;\nconst EXPANDED_WIDTH = 840;\nconst LARGE_WIDTH = 1200;\nconst EXTRA_LARGE_WIDTH = 1600;\nconst MEDIUM_HEIGHT = 480;\nconst EXPANDED_HEIGHT = 900;\nfunction widthClassOf(width) {\n if (width >= EXTRA_LARGE_WIDTH)\n return 'extraLarge';\n if (width >= LARGE_WIDTH)\n return 'large';\n if (width >= EXPANDED_WIDTH)\n return 'expanded';\n if (width >= MEDIUM_WIDTH)\n return 'medium';\n return 'compact';\n}\nfunction heightClassOf(height) {\n if (height >= EXPANDED_HEIGHT)\n return 'expanded';\n if (height >= MEDIUM_HEIGHT)\n return 'medium';\n return 'compact';\n}\nexport function sizeClassOf(width, height) {\n return {\n horizontal: width >= MEDIUM_WIDTH ? 'regular' : 'compact',\n vertical: height >= MEDIUM_HEIGHT ? 'regular' : 'compact',\n widthClass: widthClassOf(width),\n heightClass: heightClassOf(height),\n };\n}\n//# sourceMappingURL=size-class.js.map","import { WebPlugin } from '@capacitor/core';\nimport { sizeClassOf } from './size-class';\nexport class FoldableWeb extends WebPlugin {\n constructor() {\n super();\n this.sizeClass = sizeClassOf(window.innerWidth, window.innerHeight);\n window.addEventListener('resize', () => {\n const next = sizeClassOf(window.innerWidth, window.innerHeight);\n if (JSON.stringify(next) === JSON.stringify(this.sizeClass))\n return;\n this.sizeClass = next;\n this.notifyListeners('sizeClassChange', next);\n });\n }\n async isDeviceFoldable() {\n return { foldable: false, supportsTabletop: false };\n }\n async getFoldState() {\n return { state: 'flat', isSeparating: false, posture: 'flat' };\n }\n async setVerticalBarBehavior() {\n return { applied: false };\n }\n async getReservedRegions() {\n return { regions: [] };\n }\n async getHingeAngle() {\n return { angle: null, status: null };\n }\n async getSizeClass() {\n return sizeClassOf(window.innerWidth, window.innerHeight);\n }\n async getDisplayModes() {\n return { rearDisplay: 'unsupported', dualScreen: 'unsupported' };\n }\n async getBarPlacement() {\n return { verticalBarEdge: null };\n }\n async startRearDisplay() {\n throw this.unavailable('Rear display mode is only available on Android.');\n }\n stopRearDisplay() {\n return Promise.resolve();\n }\n async startDualScreen() {\n throw this.unavailable('Dual-screen mode is only available on Android.');\n }\n stopDualScreen() {\n return Promise.resolve();\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["Capacitor","registerPlugin","WebPlugin"],"mappings":"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"}
package/ionic-tabs.css CHANGED
@@ -60,7 +60,8 @@
60
60
  --padding-end: env(safe-area-inset-right);
61
61
  }
62
62
 
63
- .foldable-keyboard-open ion-tab-bar {
63
+ .foldable-keyboard-open ion-tab-bar,
64
+ ion-tab-bar[hidden] {
64
65
  display: none;
65
66
  }
66
67
 
@@ -9,6 +9,8 @@ struct NativeFold {
9
9
  var occludedBounds: CGRect?
10
10
  var activeDisplay: String?
11
11
  var cameraBounds: [CGRect] = []
12
+ var hingeMargins: UIEdgeInsets?
13
+ var regions: [ReservedRegion] = []
12
14
  }
13
15
 
14
16
  protocol FoldProvider {
@@ -16,6 +18,7 @@ protocol FoldProvider {
16
18
  var supportsTabletop: Bool { get }
17
19
  func fold(in view: UIView?) -> NativeFold?
18
20
  func hingeAngle() -> Double?
21
+ func hingeStatus() -> String?
19
22
  func observe(_ view: UIView, onChange: @escaping () -> Void)
20
23
  }
21
24
 
@@ -27,6 +30,10 @@ struct UnsupportedFoldProvider: FoldProvider {
27
30
  return nil
28
31
  }
29
32
 
33
+ func hingeStatus() -> String? {
34
+ return nil
35
+ }
36
+
30
37
  func hingeAngle() -> Double? {
31
38
  return nil
32
39
  }
@@ -77,6 +84,14 @@ struct UnsupportedFoldProvider: FoldProvider {
77
84
  result["hingeBounds"] = fold.hingeBounds.map(bounds(of:))
78
85
  result["occludedBounds"] = fold.occludedBounds.map(bounds(of:))
79
86
  result["activeDisplay"] = fold.activeDisplay
87
+ if let margins = fold.hingeMargins {
88
+ result["hingeMargins"] = [
89
+ "top": Int(margins.top.rounded()),
90
+ "right": Int(margins.right.rounded()),
91
+ "bottom": Int(margins.bottom.rounded()),
92
+ "left": Int(margins.left.rounded())
93
+ ]
94
+ }
80
95
  if !fold.cameraBounds.isEmpty {
81
96
  result["cameraBounds"] = fold.cameraBounds.map(bounds(of:))
82
97
  }
@@ -84,7 +99,32 @@ struct UnsupportedFoldProvider: FoldProvider {
84
99
  }
85
100
 
86
101
  @objc public func getHingeAngle() -> [String: Any] {
87
- return ["angle": provider.hingeAngle().map { $0 as Any } ?? NSNull()]
102
+ return [
103
+ "angle": provider.hingeAngle().map { $0 as Any } ?? NSNull(),
104
+ "status": provider.hingeStatus().map { $0 as Any } ?? NSNull()
105
+ ]
106
+ }
107
+
108
+ public func getReservedRegions(in view: UIView? = nil) -> [String: Any] {
109
+ let regions = provider.fold(in: view)?.regions ?? []
110
+ return [
111
+ "regions": regions.map { region in
112
+ [
113
+ "kind": region.kind == .division ? "division" : "occlusion",
114
+ "isActive": region.isActive,
115
+ "x": Int(region.frame.origin.x.rounded()),
116
+ "y": Int(region.frame.origin.y.rounded()),
117
+ "width": Int(region.frame.width.rounded()),
118
+ "height": Int(region.frame.height.rounded()),
119
+ "margins": [
120
+ "top": Int(region.margins.top.rounded()),
121
+ "right": Int(region.margins.right.rounded()),
122
+ "bottom": Int(region.margins.bottom.rounded()),
123
+ "left": Int(region.margins.left.rounded())
124
+ ]
125
+ ]
126
+ }
127
+ ]
88
128
  }
89
129
 
90
130
  @objc public func barPlacement(in traits: UITraitCollection) -> [String: Any] {
@@ -0,0 +1,41 @@
1
+ import Capacitor
2
+ import UIKit
3
+
4
+ /// Drop-in replacement for `CAPBridgeViewController` that lets JavaScript decide
5
+ /// whether iPhone Duo may move bars to the side, through
6
+ /// `setVerticalBarBehavior()`. Set this class on the view controller in
7
+ /// `Main.storyboard` to use it.
8
+ @objc(FoldableBridgeViewController)
9
+ public class FoldableBridgeViewController: CAPBridgeViewController {
10
+ /// `nil` leaves the system default in place.
11
+ static var verticalBarsDisabled = false
12
+
13
+ static weak var current: FoldableBridgeViewController?
14
+
15
+ public override func viewDidLoad() {
16
+ super.viewDidLoad()
17
+ Self.current = self
18
+ }
19
+
20
+ #if canImport(UIKit, _underlyingVersion: 9127.0.85) && !targetEnvironment(macCatalyst)
21
+ @available(iOS 27.1, *)
22
+ public override var preferredVerticalBarBehavior: UIVerticalBarBehavior {
23
+ Self.verticalBarsDisabled ? .disabled : .automatic
24
+ }
25
+ #endif
26
+
27
+ /// Applies a new behaviour, and returns whether this controller is in use.
28
+ @discardableResult
29
+ static func apply(disabled: Bool) -> Bool {
30
+ verticalBarsDisabled = disabled
31
+
32
+ guard let controller = current else { return false }
33
+
34
+ #if canImport(UIKit, _underlyingVersion: 9127.0.85) && !targetEnvironment(macCatalyst)
35
+ if #available(iOS 27.1, *) {
36
+ controller.setNeedsUpdateOfVerticalBarConfiguration()
37
+ }
38
+ #endif
39
+ return true
40
+ }
41
+ }
@@ -13,6 +13,8 @@ public class FoldablePlugin: CAPPlugin, CAPBridgedPlugin {
13
13
  public let pluginMethods: [CAPPluginMethod] = [
14
14
  CAPPluginMethod(name: "isDeviceFoldable", returnType: CAPPluginReturnPromise),
15
15
  CAPPluginMethod(name: "getFoldState", returnType: CAPPluginReturnPromise),
16
+ CAPPluginMethod(name: "getReservedRegions", returnType: CAPPluginReturnPromise),
17
+ CAPPluginMethod(name: "setVerticalBarBehavior", returnType: CAPPluginReturnPromise),
16
18
  CAPPluginMethod(name: "getHingeAngle", returnType: CAPPluginReturnPromise),
17
19
  CAPPluginMethod(name: "getSizeClass", returnType: CAPPluginReturnPromise),
18
20
  CAPPluginMethod(name: "getDisplayModes", returnType: CAPPluginReturnPromise),
@@ -27,6 +29,8 @@ public class FoldablePlugin: CAPPlugin, CAPBridgedPlugin {
27
29
  private var lastFoldState: [String: Any]?
28
30
  private var lastBarPlacement: [String: Any]?
29
31
  private var lastHingeAngle: Double?
32
+ private var isFolding = false
33
+ private var foldingWorkItem: DispatchWorkItem?
30
34
  private var orientationObserver: NSObjectProtocol?
31
35
 
32
36
  @objc override public func load() {
@@ -59,6 +63,25 @@ public class FoldablePlugin: CAPPlugin, CAPBridgedPlugin {
59
63
  }
60
64
  }
61
65
 
66
+ @objc func setVerticalBarBehavior(_ call: CAPPluginCall) {
67
+ let behavior = call.getString("behavior") ?? "automatic"
68
+ guard behavior == "automatic" || behavior == "disabled" else {
69
+ call.reject("behavior must be 'automatic' or 'disabled'")
70
+ return
71
+ }
72
+
73
+ DispatchQueue.main.async {
74
+ let applied = FoldableBridgeViewController.apply(disabled: behavior == "disabled")
75
+ call.resolve(["applied": applied])
76
+ }
77
+ }
78
+
79
+ @objc func getReservedRegions(_ call: CAPPluginCall) {
80
+ DispatchQueue.main.async {
81
+ call.resolve(self.implementation.getReservedRegions(in: self.bridge?.viewController?.view))
82
+ }
83
+ }
84
+
62
85
  @objc func getHingeAngle(_ call: CAPPluginCall) {
63
86
  DispatchQueue.main.async { [weak self] in
64
87
  guard let self = self else { return }
@@ -163,6 +186,24 @@ public class FoldablePlugin: CAPPlugin, CAPBridgedPlugin {
163
186
 
164
187
  lastHingeAngle = angle
165
188
  notifyListeners("hingeAngleChange", data: ["angle": angle])
189
+ notifyFolding()
190
+ }
191
+
192
+ /// `true` while the hinge keeps moving, `false` once it has been still for a moment.
193
+ private func notifyFolding() {
194
+ if !isFolding {
195
+ isFolding = true
196
+ notifyListeners("foldingChange", data: ["folding": true])
197
+ }
198
+
199
+ foldingWorkItem?.cancel()
200
+ let settle = DispatchWorkItem { [weak self] in
201
+ guard let self = self, self.isFolding else { return }
202
+ self.isFolding = false
203
+ self.notifyListeners("foldingChange", data: ["folding": false])
204
+ }
205
+ foldingWorkItem = settle
206
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: settle)
166
207
  }
167
208
 
168
209
  private func notifyBarPlacementIfChanged() {
@@ -8,6 +8,7 @@ struct ReservedRegion {
8
8
 
9
9
  var kind: Kind
10
10
  var frame: CGRect
11
+ var margins: UIEdgeInsets = .zero
11
12
  var isActive: Bool
12
13
  }
13
14
 
@@ -33,38 +34,67 @@ struct ReservedRegionFoldProvider: FoldProvider {
33
34
 
34
35
  func fold(in view: UIView?) -> NativeFold? {
35
36
  guard source.hasFold, let view = view else { return nil }
36
- return nativeFoldOf(regions: source.reservedRegions(in: view), hingeStatus: source.hingeStatus)
37
+ return nativeFoldOf(
38
+ regions: source.reservedRegions(in: view),
39
+ hingeStatus: source.hingeStatus,
40
+ hingeAngle: source.hingeAngle
41
+ )
37
42
  }
38
43
 
39
44
  func hingeAngle() -> Double? {
40
45
  return source.hingeAngle
41
46
  }
42
47
 
48
+ func hingeStatus() -> String? {
49
+ switch source.hingeStatus {
50
+ case .closed?: return "closed"
51
+ case .partiallyOpen?: return "partiallyOpen"
52
+ case .fullyOpen?: return "fullyOpen"
53
+ case nil: return nil
54
+ }
55
+ }
56
+
43
57
  func observe(_ view: UIView, onChange: @escaping () -> Void) {
44
58
  source.observe(view, onChange: onChange)
45
59
  }
46
60
  }
47
61
 
48
- func nativeFoldOf(regions: [ReservedRegion], hingeStatus: HingeStatus?) -> NativeFold {
49
- let divisions = hingeStatus == .closed ? [] : regions.filter { $0.kind == .division }
62
+ /// UIKit updates the hinge status lazily, so the angle decides when it disagrees.
63
+ private let foldedAngles = 20.0...160.0
64
+
65
+ func nativeFoldOf(
66
+ regions: [ReservedRegion],
67
+ hingeStatus: HingeStatus?,
68
+ hingeAngle: Double? = nil
69
+ ) -> NativeFold {
70
+ // UIKit can keep reporting `closed` while the phone is already open, so the
71
+ // angle wins wherever it is known.
72
+ let isClosed = hingeAngle.map { $0 < foldedAngles.lowerBound } ?? (hingeStatus == .closed)
73
+ let divisions = isClosed ? [] : regions.filter { $0.kind == .division }
50
74
  let division = divisions.first { $0.isActive } ?? divisions.first
51
75
  let isFolded: Bool
52
- switch hingeStatus {
53
- case .partiallyOpen?:
54
- isFolded = division != nil
55
- case .fullyOpen?:
56
- isFolded = false
57
- default:
58
- isFolded = division?.isActive == true
76
+ if let angle = hingeAngle, division != nil {
77
+ isFolded = foldedAngles.contains(angle)
78
+ } else {
79
+ switch hingeStatus {
80
+ case .partiallyOpen?:
81
+ isFolded = division != nil
82
+ case .fullyOpen?:
83
+ isFolded = false
84
+ default:
85
+ isFolded = division?.isActive == true
86
+ }
59
87
  }
60
88
 
61
89
  var fold = NativeFold(state: isFolded ? "half-opened" : "flat", isSeparating: isFolded)
62
90
  if let division = division {
63
91
  fold.hingeBounds = division.frame
92
+ fold.hingeMargins = division.margins
64
93
  fold.hingeOrientation = division.frame.height >= division.frame.width ? "vertical" : "horizontal"
65
94
  }
66
95
  fold.cameraBounds = regions.filter { $0.kind == .occlusion && $0.isActive }.map { $0.frame }
96
+ fold.regions = regions
67
97
  let onInnerDisplay = regions.contains { $0.kind == .division }
68
- fold.activeDisplay = hingeStatus.map { $0 == .closed || !onInnerDisplay ? "outer" : "inner" }
98
+ fold.activeDisplay = hingeStatus.map { _ in isClosed || !onInnerDisplay ? "outer" : "inner" }
69
99
  return fold
70
100
  }
@@ -0,0 +1,14 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>NSPrivacyTracking</key>
6
+ <false/>
7
+ <key>NSPrivacyTrackingDomains</key>
8
+ <array/>
9
+ <key>NSPrivacyCollectedDataTypes</key>
10
+ <array/>
11
+ <key>NSPrivacyAccessedAPITypes</key>
12
+ <array/>
13
+ </dict>
14
+ </plist>
@@ -32,10 +32,10 @@ final class UIKitFoldSource: @preconcurrency FoldSource {
32
32
 
33
33
  func reservedRegions(in view: UIView) -> [ReservedRegion] {
34
34
  let divisions = view.reservedRegions(kind: .division, options: .includeInactive).map {
35
- ReservedRegion(kind: .division, frame: $0.frame, isActive: $0.isActive)
35
+ ReservedRegion(kind: .division, frame: $0.frame, margins: $0.margins, isActive: $0.isActive)
36
36
  }
37
- let occlusions = view.reservedRegions(kind: .occlusion).map {
38
- ReservedRegion(kind: .occlusion, frame: $0.frame, isActive: $0.isActive)
37
+ let occlusions = view.reservedRegions(kind: .occlusion, options: .includeInactive).map {
38
+ ReservedRegion(kind: .occlusion, frame: $0.frame, margins: $0.margins, isActive: $0.isActive)
39
39
  }
40
40
  return divisions + occlusions
41
41
  }
@@ -12,6 +12,10 @@ private final class FakeFoldProvider: FoldProvider {
12
12
  return reportedFold
13
13
  }
14
14
 
15
+ func hingeStatus() -> String? {
16
+ return nil
17
+ }
18
+
15
19
  func hingeAngle() -> Double? {
16
20
  return angle
17
21
  }
@@ -153,6 +153,24 @@ class ReservedRegionFoldProviderTests: XCTestCase {
153
153
  XCTAssertEqual(result["posture"] as? String, "book")
154
154
  }
155
155
 
156
+ func testAngleDecidesWhenTheStatusDisagrees() {
157
+ let source = FakeFoldSource()
158
+ source.hingeStatus = .fullyOpen
159
+ source.hingeAngle = 60
160
+ source.regions = [ReservedRegion(kind: .division, frame: CGRect(x: 456, y: 0, width: 40, height: 669), isActive: false)]
161
+
162
+ XCTAssertEqual(foldState(source)["state"] as? String, "half-opened")
163
+ }
164
+
165
+ func testAngleNearFlatIsFlatEvenIfTheStatusSaysPartiallyOpen() {
166
+ let source = FakeFoldSource()
167
+ source.hingeStatus = .partiallyOpen
168
+ source.hingeAngle = 178
169
+ source.regions = [ReservedRegion(kind: .division, frame: CGRect(x: 456, y: 0, width: 40, height: 669), isActive: true)]
170
+
171
+ XCTAssertEqual(foldState(source)["state"] as? String, "flat")
172
+ }
173
+
156
174
  func testHingeAngleComesFromSource() {
157
175
  let source = FakeFoldSource()
158
176
  source.hingeAngle = 95
package/package.json CHANGED
@@ -1,22 +1,23 @@
1
1
  {
2
2
  "name": "@erkamyaman/capacitor-foldable",
3
- "version": "8.1.0",
3
+ "version": "8.3.0",
4
4
  "description": "Fold-state and dual-display APIs for foldable devices in Capacitor apps",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",
7
7
  "types": "dist/esm/index.d.ts",
8
8
  "unpkg": "dist/plugin.js",
9
9
  "files": [
10
+ "android/build.gradle",
11
+ "android/src/main/",
12
+ "dist/",
10
13
  "ErkamyamanCapacitorFoldable.podspec",
11
14
  "ionic-tabs-ios26.css",
12
15
  "ionic-tabs.css",
16
+ "ios/Sources",
17
+ "ios/Tests",
13
18
  "LICENSE",
14
19
  "Package.swift",
15
- "android/build.gradle",
16
- "android/src/main/",
17
- "dist/",
18
- "ios/Sources",
19
- "ios/Tests"
20
+ "skills/"
20
21
  ],
21
22
  "author": "Erkam Yaman <erkamyaman35@gmail.com>",
22
23
  "license": "MIT",
@@ -0,0 +1,87 @@
1
+ ---
2
+ name: capacitor-foldable
3
+ description: Build Capacitor apps that adapt to foldable devices, iPhone Duo and Android foldables, with @erkamyaman/capacitor-foldable. Trigger when laying out around the crease or fold, reading the hinge angle or posture, using window size classes, polyfilling the Device Posture and Viewport Segments APIs, telling the outer display from the inner one, using rear display or dual screen on an Android foldable, moving an Ionic tab bar where iPhone Duo puts native bars, or testing on the iPhone Duo simulator or a foldable emulator.
4
+ license: MIT
5
+ metadata:
6
+ author: erkamyaman
7
+ version: '0.1'
8
+ status: beta
9
+ ---
10
+
11
+ # Capacitor Foldable
12
+
13
+ Guidance for [`@erkamyaman/capacitor-foldable`](https://github.com/erkamyaman/capacitor-foldable), which reports the fold to a Capacitor web view: where the crease is, the hinge angle, the posture, and which display the app is on.
14
+
15
+ This skill is new. Everything in it was checked against the plugin source and measured on the iPhone Duo simulator, but it has not been through much real use yet, so [report anything that misleads you](https://github.com/erkamyaman/capacitor-foldable/issues).
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ npm install @erkamyaman/capacitor-foldable
21
+ npx cap sync
22
+ ```
23
+
24
+ Capacitor 8 uses the current major. Capacitor 7 apps install `@erkamyaman/capacitor-foldable@7`, which reports the fold on Android and size classes everywhere, but has no iPhone Duo support: a Capacitor 7 app built with Xcode 27 crashes at launch, because iOS 27 needs the scene lifecycle that Capacitor 8.5 and later adopted. The 7.x line also predates the `--fold-*` variables (8.1.0) and the Ionic tab stylesheets, so the CSS below needs Capacitor 8.
25
+
26
+ ## Start here: CSS before JavaScript
27
+
28
+ Install the polyfill once at startup, then lay out with CSS. Most foldable work needs no JavaScript at all.
29
+
30
+ ```typescript
31
+ import { Foldable, installFoldablePolyfill } from '@erkamyaman/capacitor-foldable';
32
+
33
+ await installFoldablePolyfill();
34
+ ```
35
+
36
+ Whenever the device reports a fold, that sets `--fold-left`, `--fold-top`, `--fold-width`, `--fold-height` and `--fold-margin-*` on `<html>`, and adds `fold-vertical` or `fold-horizontal`. It also sets the `vertical-bars-*` classes the Ionic stylesheet needs, and fills in the standard Viewport Segments and Device Posture APIs, which Safari and Android's WebView do not provide.
37
+
38
+ Prefer the `--fold-*` variables over the polyfilled segment variables for layout: the segments only split when the fold actually separates the window, which on iPhone Duo means half open, while the fold variables are there when it is open flat too.
39
+
40
+ ```css
41
+ .fold-vertical .reader { position: fixed; inset: 0; }
42
+
43
+ .fold-vertical .reader .page {
44
+ position: absolute;
45
+ top: 0;
46
+ bottom: 0;
47
+ padding: 14px;
48
+ box-sizing: border-box;
49
+ }
50
+
51
+ .fold-vertical .reader .page:first-child { left: 0; width: var(--fold-left); }
52
+ .fold-vertical .reader .page:last-child { left: calc(var(--fold-left) + var(--fold-width)); right: 0; }
53
+ ```
54
+
55
+ On a phone with no fold there are no `--fold-*` variables and no `fold-*` class, so write the flat layout first and let these rules refine it. The install is also a no-op off native, so nothing appears in a desktop browser: iterate in the simulator or on a device.
56
+
57
+ Handle both orientations. On iPhone Duo the crease is vertical in landscape and horizontal in portrait, so a layout that only styles `.fold-vertical` flattens when the phone is turned.
58
+
59
+ Reach for the plugin's methods when CSS cannot answer the question: the hinge angle, the posture, which display you are on, or where the system put its bars.
60
+
61
+ ## When to consult these references
62
+
63
+ - **Laying out around the crease** (variables, classes, worked layouts): [css-layout.md](references/css-layout.md)
64
+ - **The API** (methods, events, and what is platform specific): [api.md](references/api.md)
65
+ - **Ionic apps** (`ion-tabs` on the side, the keyboard event a fold fires, Angular's CSS rule): [ionic.md](references/ionic.md)
66
+ - **iPhone Duo specifics** (reserved regions, vertical bars, safe areas, what the hinge status gets wrong): [iphone-duo.md](references/iphone-duo.md)
67
+ - **Android foldables** (postures, rear display, dual screen): [android.md](references/android.md)
68
+ - **Testing** (iPhone Duo simulator, foldable emulator, driving the hinge from the terminal): [testing.md](references/testing.md)
69
+
70
+ ## Hard rules
71
+
72
+ - ✅ **Split on `var(--fold-left)`, never on 50%.** The variables give the band's leading edge, not its centre, and the coordinates are measured against the web view, so any inset moves them off the viewport midpoint. On iPhone Duo the fold is a 40 point band, so a halfway split also lands inside the part the system wants kept clear.
73
+ - ✅ **Treat `--fold-width` as the keep-clear band.** The margins are *inside* it, so `--fold-margin-*` tells you how much of the band is margin rather than physical crease. Do not add them on top of it. They are iOS only and read `0px` on Android, where a seamless fold also reports zero width, so keep your own minimum clearance: `max(12px, var(--fold-width, 0px))`, with the fallback because the variable does not exist at all on a phone with no fold.
74
+ - ✅ **Expect the fold when the device is fully open, not just half.** The position is reported flat as well as folded, so a layout can line up with the crease before anyone bends anything. A *closed* device reports `state: 'flat'` and no `hingeBounds`, because the plugin drops the fold below 20 degrees.
75
+ - ✅ **Let the layout expand; do not design a screen per pose.** Apple: "Don't reinvent your app when it resizes; allow the existing layout to expand based on the available space instead", and "Avoid extreme layout changes as people fold the device." Google's equivalent is to adapt to window size classes rather than device type, and to consider whether controls and text should avoid the hinge space.
76
+ - ❌ **Do not use `setVerticalBarBehavior({ behavior: 'disabled' })` to avoid adapting.** Apple's HIG asks you not to override bar placement. UIKit's own reference gives the exception: disable it only for interfaces better served by horizontal bars, "such as a fullscreen video player with toolbar controls or a non-scrolling layout like a calculator". It returns the bars to horizontal; it does not hand you the full display.
77
+ - ❌ **Do not poll `getFoldState()` on a timer** when `foldStateChange` will tell you. The exception is the moment the app moves between displays, where iOS pauses the web view: see [iphone-duo.md](references/iphone-duo.md).
78
+ - ❌ **Do not confuse "this device folds" with "there is a fold on this screen".** `isDeviceFoldable()` answers the first, though it can answer `false` on an Android foldable that advertises no tabletop posture and no hinge sensor. The presence of `hingeBounds`, or `--fold-left` in CSS, answers the second, and is what layout should key on. For the *outer screen*, neither is the gate: use `getDisplayModes()` and `displayModeChange`, since rear display and dual screen are Android only and read `unsupported` on iPhone Duo even though it folds. Every *read* method answers safely on any device; `startRearDisplay()` and `startDualScreen()` reject when the mode is unavailable.
79
+
80
+ ## Before reporting done
81
+
82
+ ```bash
83
+ npm run build # or the app's build
84
+ npx cap sync
85
+ ```
86
+
87
+ Then run it open flat, half open and closed, and fold it shut and open again, which suspends and resumes the web view on iOS. On iPhone Duo the posture picks the display, so that covers both screens; on Android the outer display needs rear display mode. [testing.md](references/testing.md) has the commands.
@@ -0,0 +1,52 @@
1
+ # Android foldables
2
+
3
+ Backed by Jetpack WindowManager, so the fold comes from whatever the device reports: Pixel Fold, Galaxy Z Fold and Flip, and the foldable emulators.
4
+
5
+ ## Postures
6
+
7
+ `getFoldState()` reports `posture` as `flat`, `tabletop` or `book`, and `foldStateChange` fires as the device moves. Tabletop is half open with a horizontal hinge, which is the laptop pose: content above the crease, controls below. Book is half open with a vertical hinge.
8
+
9
+ `isSeparating` says whether the fold actually splits the window into two areas. A flat *foldable* still reports `hingeBounds`, which is what lets a layout line up with the crease before anyone folds it. A device with no fold reports `posture: 'flat'` and no `hingeBounds`, which is the difference worth testing for. It still reports `cameraBounds` from the display cutouts.
10
+
11
+ `hingeMargins`, `activeDisplay` and the hinge `status` are iOS only and never appear here. `cameraBounds` and `occludedBounds` do: the cutouts, and the fold's own bounds when it fully occludes content. To tell the cover screen from the inner one, lean on `getSizeClass()`, where the outer display shows up as a compact width, keeping in mind it describes the window rather than the panel, so rotation and multi-window move it too.
12
+
13
+ ## The hinge angle
14
+
15
+ Only devices with a hinge sensor report an angle, and only on Android 11 and later; everything else answers `null`, and the fold state still works, since it comes from WindowManager rather than the sensor. The plugin registers at the normal sensor rate, so it needs no permission and is nowhere near the 200 Hz threshold that would require `HIGH_SAMPLING_RATE_SENSORS`.
16
+
17
+ ## Rear display: move the app to the outer screen
18
+
19
+ ```typescript
20
+ const { rearDisplay } = await Foldable.getDisplayModes();
21
+ if (rearDisplay === 'available') await Foldable.startRearDisplay();
22
+ await Foldable.stopRearDisplay();
23
+ ```
24
+
25
+ Statuses are `active`, `available`, `unavailable` and `unsupported`. The classic use is a selfie with the main camera, previewing on the outer screen. The system asks the user to confirm before the app moves.
26
+
27
+ The status the `start` methods check comes from a background collector rather than from `getDisplayModes()`, so immediately after launch a positive check can still be rejected. Listen for `displayModeChange` before offering the button, or retry.
28
+
29
+ ## Dual screen: a second window on the other display
30
+
31
+ ```typescript
32
+ await Foldable.startDualScreen({ url: 'cover.html' });
33
+ await Foldable.stopDualScreen();
34
+ ```
35
+
36
+ The URL is loaded in a second web view on the other display, and a relative path resolves against the app's own URL, so the page has to be part of the web build. A `data:` URL is often easier for dynamic content, and is what the plugin's own example app uses.
37
+
38
+ Treat it as a presentation surface: a plain web view with no Capacitor bridge, so no plugin API inside it. Anything it needs goes in the URL, though the page is served through the app's own local server, so same-origin storage is readable there too. Calling `startDualScreen` again rebuilds the page rather than messaging it.
39
+
40
+ Both modes share one window area behind the scenes, and both stop when the activity goes away.
41
+
42
+ Both modes report through `displayModeChange`, and both are Android only. On iOS and web they read `unsupported`, and the `start` methods reject.
43
+
44
+ Emulators do not offer dual screen at all, and a Z Flip only in Flex mode, so `unsupported` there is the emulator rather than a bug in your app.
45
+
46
+ ## Permissions
47
+
48
+ None. The plugin's manifest is empty and adds nothing to your app.
49
+
50
+ ## Quality guidelines
51
+
52
+ Google's adaptive app quality guidelines make posture support a Tier 1 check and adaptive layouts driven by window size classes a Tier 2 one. Keeping controls and text off the fold is separate fold-aware design guidance rather than a quality check: "Don't place UI controls too close to a fold or hinge when `isSeparating` is true." `posture`, `getSizeClass()` and the fold variables are what all three ask for.