@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.
- package/ErkamyamanCapacitorFoldable.podspec +1 -0
- package/Package.swift +2 -1
- package/README.md +131 -12
- package/android/src/main/kotlin/io/github/erkamyaman/foldable/FoldablePlugin.kt +31 -1
- package/dist/docs.json +176 -4
- package/dist/esm/css.d.ts +6 -1
- package/dist/esm/css.js +16 -2
- package/dist/esm/css.js.map +1 -1
- package/dist/esm/definitions.d.ts +95 -4
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/polyfill.js +6 -1
- package/dist/esm/polyfill.js.map +1 -1
- package/dist/esm/web.d.ts +7 -0
- package/dist/esm/web.js +7 -1
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +29 -4
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +29 -4
- package/dist/plugin.js.map +1 -1
- package/ionic-tabs.css +2 -1
- package/ios/Sources/FoldablePlugin/Foldable.swift +41 -1
- package/ios/Sources/FoldablePlugin/FoldableBridgeViewController.swift +41 -0
- package/ios/Sources/FoldablePlugin/FoldablePlugin.swift +41 -0
- package/ios/Sources/FoldablePlugin/ReservedRegionFoldProvider.swift +41 -11
- package/ios/Sources/FoldablePlugin/Resources/PrivacyInfo.xcprivacy +14 -0
- package/ios/Sources/FoldablePlugin/UIKitFoldSource.swift +3 -3
- package/ios/Tests/FoldablePluginTests/FoldableTests.swift +4 -0
- package/ios/Tests/FoldablePluginTests/ReservedRegionFoldProviderTests.swift +18 -0
- package/package.json +7 -6
- package/skills/capacitor-foldable/SKILL.md +87 -0
- package/skills/capacitor-foldable/references/android.md +52 -0
- package/skills/capacitor-foldable/references/api.md +112 -0
- package/skills/capacitor-foldable/references/css-layout.md +127 -0
- package/skills/capacitor-foldable/references/ionic.md +50 -0
- package/skills/capacitor-foldable/references/iphone-duo.md +54 -0
- package/skills/capacitor-foldable/references/testing.md +53 -0
package/dist/plugin.js.map
CHANGED
|
@@ -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
|
@@ -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 [
|
|
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(
|
|
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
|
-
|
|
49
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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 {
|
|
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
|
}
|
|
@@ -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.
|
|
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
|
-
"
|
|
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.
|