@erkamyaman/capacitor-foldable 7.0.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 (43) hide show
  1. package/ErkamyamanCapacitorFoldable.podspec +17 -0
  2. package/LICENSE +21 -0
  3. package/Package.swift +28 -0
  4. package/README.md +457 -0
  5. package/android/build.gradle +71 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/kotlin/io/github/erkamyaman/foldable/DisplayModes.kt +115 -0
  8. package/android/src/main/kotlin/io/github/erkamyaman/foldable/Foldable.kt +189 -0
  9. package/android/src/main/kotlin/io/github/erkamyaman/foldable/FoldablePlugin.kt +333 -0
  10. package/android/src/main/res/.gitkeep +0 -0
  11. package/dist/docs.json +584 -0
  12. package/dist/esm/css.d.ts +8 -0
  13. package/dist/esm/css.js +29 -0
  14. package/dist/esm/css.js.map +1 -0
  15. package/dist/esm/definitions.d.ts +267 -0
  16. package/dist/esm/definitions.js +2 -0
  17. package/dist/esm/definitions.js.map +1 -0
  18. package/dist/esm/index.d.ts +5 -0
  19. package/dist/esm/index.js +9 -0
  20. package/dist/esm/index.js.map +1 -0
  21. package/dist/esm/polyfill.d.ts +19 -0
  22. package/dist/esm/polyfill.js +76 -0
  23. package/dist/esm/polyfill.js.map +1 -0
  24. package/dist/esm/segments.d.ts +8 -0
  25. package/dist/esm/segments.js +26 -0
  26. package/dist/esm/segments.js.map +1 -0
  27. package/dist/esm/size-class.d.ts +2 -0
  28. package/dist/esm/size-class.js +33 -0
  29. package/dist/esm/size-class.js.map +1 -0
  30. package/dist/esm/web.d.ts +21 -0
  31. package/dist/esm/web.js +46 -0
  32. package/dist/esm/web.js.map +1 -0
  33. package/dist/plugin.cjs.js +222 -0
  34. package/dist/plugin.cjs.js.map +1 -0
  35. package/dist/plugin.js +225 -0
  36. package/dist/plugin.js.map +1 -0
  37. package/ios/Sources/FoldablePlugin/Foldable.swift +156 -0
  38. package/ios/Sources/FoldablePlugin/FoldablePlugin.swift +194 -0
  39. package/ios/Sources/FoldablePlugin/ReservedRegionFoldProvider.swift +69 -0
  40. package/ios/Sources/FoldablePlugin/UIKitFoldSource.swift +53 -0
  41. package/ios/Tests/FoldablePluginTests/FoldableTests.swift +104 -0
  42. package/ios/Tests/FoldablePluginTests/ReservedRegionFoldProviderTests.swift +143 -0
  43. package/package.json +96 -0
@@ -0,0 +1,17 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'ErkamyamanCapacitorFoldable'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.license = package['license']
10
+ s.homepage = package['repository']['url']
11
+ s.author = package['author']
12
+ s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
13
+ s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
+ s.ios.deployment_target = '14.0'
15
+ s.dependency 'Capacitor'
16
+ s.swift_version = '5.1'
17
+ end
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Erkam Yaman (erkamyaman)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/Package.swift ADDED
@@ -0,0 +1,28 @@
1
+ // swift-tools-version: 5.9
2
+ import PackageDescription
3
+
4
+ let package = Package(
5
+ name: "ErkamyamanCapacitorFoldable",
6
+ platforms: [.iOS(.v14)],
7
+ products: [
8
+ .library(
9
+ name: "ErkamyamanCapacitorFoldable",
10
+ targets: ["FoldablePlugin"])
11
+ ],
12
+ dependencies: [
13
+ .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", "7.0.0"..<"8.0.0")
14
+ ],
15
+ targets: [
16
+ .target(
17
+ name: "FoldablePlugin",
18
+ dependencies: [
19
+ .product(name: "Capacitor", package: "capacitor-swift-pm"),
20
+ .product(name: "Cordova", package: "capacitor-swift-pm")
21
+ ],
22
+ path: "ios/Sources/FoldablePlugin"),
23
+ .testTarget(
24
+ name: "FoldablePluginTests",
25
+ dependencies: ["FoldablePlugin"],
26
+ path: "ios/Tests/FoldablePluginTests")
27
+ ]
28
+ )
package/README.md ADDED
@@ -0,0 +1,457 @@
1
+ <h1 align="center">Capacitor Foldable</h1>
2
+ <p align="center"><strong><code>@erkamyaman/capacitor-foldable</code></strong></p>
3
+ <p align="center">
4
+ <img src="https://img.shields.io/maintenance/yes/2026?style=for-the-badge" />
5
+ <a href="https://www.npmjs.com/package/@erkamyaman/capacitor-foldable"><img src="https://img.shields.io/npm/v/@erkamyaman/capacitor-foldable?style=for-the-badge" /></a>
6
+ <a href="https://www.npmjs.com/package/@erkamyaman/capacitor-foldable"><img src="https://img.shields.io/npm/dw/@erkamyaman/capacitor-foldable?style=for-the-badge" /></a>
7
+ <a href="LICENSE"><img src="https://img.shields.io/npm/l/@erkamyaman/capacitor-foldable?style=for-the-badge" /></a>
8
+ </p>
9
+ <p align="center">
10
+ Fold state, posture, hinge angle and size classes for foldable phones such as Galaxy Z Fold and Flip and Pixel Fold, in Capacitor 7 apps.
11
+ </p>
12
+
13
+ ```typescript
14
+ const { state, hingeOrientation } = await Foldable.getFoldState();
15
+ // { state: 'half-opened', hingeOrientation: 'horizontal' }
16
+ ```
17
+
18
+ > [!NOTE]
19
+ > **This is v7, for Capacitor 7.** On Android every function and event works. On iOS only size classes are real: `getSizeClass()` and `sizeClassChange`. The other functions run but report no fold (`getFoldState()` returns `flat`, `getHingeAngle()` returns `{ angle: null }` and `getBarPlacement()` `{ verticalBarEdge: null }`), and `hingeAngleChange` and `barPlacementChange` never fire. Capacitor 7 apps must be built with Xcode 26, which doesn't have Apple's iPhone Duo APIs. For iPhone Duo, use v8 with Capacitor 8.5 or later.
20
+
21
+ ### Versions
22
+
23
+ | Capacitor | Plugin | iOS |
24
+ | --------- | ------ | --- |
25
+ | v7.x | v7.x | Size classes only, build with Xcode 26 |
26
+ | v8.x | v8.x | Size classes, plus iPhone Duo on Capacitor 8.5+ |
27
+
28
+ ### Supported Platforms
29
+
30
+ | Platform | Status | Minimum |
31
+ | -------- | ------ | ------- |
32
+ | Android | Supported | API 23, `compileSdk` 34 |
33
+ | iOS | Size classes. No iPhone Duo features: use plugin v8 with Capacitor 8.5+ for those | iOS 14, built with Xcode 26 |
34
+ | Web | Stub, returns `flat` | |
35
+
36
+ ## Do you need this plugin?
37
+
38
+ Web views already resize with the window, so a responsive layout that pads with every safe-area inset adapts to foldables without a plugin. Use this one when your app needs to know what CSS can't tell it:
39
+
40
+ - **Whether the device folds at all**, and whether it can stand half-open like a laptop.
41
+ - **Where the fold is**, and whether it splits the screen.
42
+ - **How the device is held**: flat, tabletop or book.
43
+ - **The hinge angle**, for effects and interactions.
44
+ - **Outer or inner display**, and Material window size classes.
45
+ - **The outer display on Android foldables**: rear display and dual screen.
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ npm install @erkamyaman/capacitor-foldable@7
51
+ npx cap sync
52
+ ```
53
+
54
+ This is the Capacitor 7 version. On Capacitor 8, install the latest `@erkamyaman/capacitor-foldable` instead, which also supports iPhone Duo.
55
+
56
+ Build Capacitor 7 apps with Xcode 26. iOS 27 requires the scene lifecycle for apps built with Xcode 27, and Capacitor 7 doesn't adopt it, so a Capacitor 7 app built with Xcode 27 crashes at launch, with or without this plugin.
57
+
58
+ ## Usage
59
+
60
+ ```typescript
61
+ import { Foldable } from '@erkamyaman/capacitor-foldable';
62
+
63
+ const { posture } = await Foldable.getFoldState(); // 'flat' | 'tabletop' | 'book'
64
+
65
+ Foldable.addListener('foldStateChange', ({ posture }) => {
66
+ document.documentElement.dataset.posture = posture;
67
+ });
68
+ ```
69
+
70
+ More in [Examples](docs/examples.md).
71
+
72
+ ## Web standard APIs
73
+
74
+ Chrome ships the [Device Posture API](https://developer.mozilla.org/docs/Web/API/Device_Posture_API) and the [Viewport Segments API](https://developer.mozilla.org/docs/Web/API/Viewport_Segments_API), but Android's WebView turns both off and Safari doesn't support them, so neither works inside a Capacitor app. `installFoldablePolyfill()` fills them in from the native fold state, and mirrors the CSS features as classes and variables on `<html>`:
75
+
76
+ ```typescript
77
+ import { installFoldablePolyfill } from '@erkamyaman/capacitor-foldable';
78
+
79
+ await installFoldablePolyfill();
80
+
81
+ navigator.devicePosture.type; // 'continuous' | 'folded'
82
+ window.viewport.segments; // two DOMRects when the fold splits the web view
83
+ ```
84
+
85
+ | Standard CSS | With the polyfill |
86
+ | --- | --- |
87
+ | `@media (device-posture: folded)` | `.device-posture-folded` |
88
+ | `@media (horizontal-viewport-segments: 2)` | `.horizontal-viewport-segments-2` |
89
+ | `@media (vertical-viewport-segments: 2)` | `.vertical-viewport-segments-2` |
90
+ | `env(viewport-segment-width 0 0)` | `var(--viewport-segment-width-0-0)` |
91
+ | Native bars moved to the side (iPhone Duo) | `.vertical-bars-leading`, `.vertical-bars-trailing` |
92
+
93
+ ## Guides
94
+
95
+ - [Examples](docs/examples.md)
96
+ - [Layout tips](docs/layout-tips.md)
97
+ - [Testing on Android](docs/testing-android.md)
98
+ - [iPhone Duo](docs/iphone-duo.md)
99
+
100
+ ## API
101
+
102
+ <docgen-index>
103
+
104
+ * [`isDeviceFoldable()`](#isdevicefoldable)
105
+ * [`getFoldState()`](#getfoldstate)
106
+ * [`getHingeAngle()`](#gethingeangle)
107
+ * [`getSizeClass()`](#getsizeclass)
108
+ * [`getDisplayModes()`](#getdisplaymodes)
109
+ * [`getBarPlacement()`](#getbarplacement)
110
+ * [`startRearDisplay()`](#startreardisplay)
111
+ * [`stopRearDisplay()`](#stopreardisplay)
112
+ * [`startDualScreen(...)`](#startdualscreen)
113
+ * [`stopDualScreen()`](#stopdualscreen)
114
+ * [`addListener('foldStateChange', ...)`](#addlistenerfoldstatechange-)
115
+ * [`addListener('hingeAngleChange', ...)`](#addlistenerhingeanglechange-)
116
+ * [`addListener('sizeClassChange', ...)`](#addlistenersizeclasschange-)
117
+ * [`addListener('displayModeChange', ...)`](#addlistenerdisplaymodechange-)
118
+ * [`addListener('barPlacementChange', ...)`](#addlistenerbarplacementchange-)
119
+ * [Interfaces](#interfaces)
120
+ * [Type Aliases](#type-aliases)
121
+
122
+ </docgen-index>
123
+
124
+ <docgen-api>
125
+ <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
126
+
127
+ ### isDeviceFoldable()
128
+
129
+ ```typescript
130
+ isDeviceFoldable() => Promise<{ foldable: boolean; supportsTabletop: boolean; }>
131
+ ```
132
+
133
+ Whether the device has a fold at all, and whether it can be propped half
134
+ open like a laptop. Both `false` on web, and on iOS except on iPhone Duo
135
+ (iOS 27.1 or later).
136
+
137
+ **Returns:** <code>Promise&lt;{ foldable: boolean; supportsTabletop: boolean; }&gt;</code>
138
+
139
+ **Since:** 7.0.0
140
+
141
+ --------------------
142
+
143
+
144
+ ### getFoldState()
145
+
146
+ ```typescript
147
+ getFoldState() => Promise<FoldState>
148
+ ```
149
+
150
+ Read the current fold state. Resolves to
151
+ `{ state: 'flat', isSeparating: false, posture: 'flat' }` when there is no
152
+ fold information.
153
+
154
+ **Returns:** <code>Promise&lt;<a href="#foldstate">FoldState</a>&gt;</code>
155
+
156
+ **Since:** 7.0.0
157
+
158
+ --------------------
159
+
160
+
161
+ ### getHingeAngle()
162
+
163
+ ```typescript
164
+ getHingeAngle() => Promise<{ angle: number | null; }>
165
+ ```
166
+
167
+ Read the angle between the two halves of the device, in degrees: `0` when
168
+ closed, `180` when flat. Resolves to `{ angle: null }` on devices without a
169
+ hinge angle sensor, on web, and on iOS before 27.1.
170
+
171
+ **Returns:** <code>Promise&lt;{ angle: number | null; }&gt;</code>
172
+
173
+ **Since:** 7.0.0
174
+
175
+ --------------------
176
+
177
+
178
+ ### getSizeClass()
179
+
180
+ ```typescript
181
+ getSizeClass() => Promise<SizeClass>
182
+ ```
183
+
184
+ Read the window's size classes: Apple's compact and regular, the signal its
185
+ iPhone Duo guidelines recommend for telling the outer display from the inner
186
+ one, and Material's width and height classes. On iOS `horizontal` and
187
+ `vertical` are UIKit's size classes; everything else comes from the window
188
+ size.
189
+
190
+ **Returns:** <code>Promise&lt;<a href="#sizeclass">SizeClass</a>&gt;</code>
191
+
192
+ **Since:** 7.0.0
193
+
194
+ --------------------
195
+
196
+
197
+ ### getDisplayModes()
198
+
199
+ ```typescript
200
+ getDisplayModes() => Promise<DisplayModes>
201
+ ```
202
+
203
+ Read which of the foldable display modes the device offers right now. Both
204
+ are `'unsupported'` on iOS and web.
205
+
206
+ **Returns:** <code>Promise&lt;<a href="#displaymodes">DisplayModes</a>&gt;</code>
207
+
208
+ **Since:** 7.0.0
209
+
210
+ --------------------
211
+
212
+
213
+ ### getBarPlacement()
214
+
215
+ ```typescript
216
+ getBarPlacement() => Promise<BarPlacement>
217
+ ```
218
+
219
+ Read where native bars go. Native tab bars and toolbars move to the side of
220
+ the display on iPhone Duo, but HTML ones stay put, so use this to move your
221
+ own tab bar too.
222
+
223
+ **Returns:** <code>Promise&lt;<a href="#barplacement">BarPlacement</a>&gt;</code>
224
+
225
+ **Since:** 7.0.0
226
+
227
+ --------------------
228
+
229
+
230
+ ### startRearDisplay()
231
+
232
+ ```typescript
233
+ startRearDisplay() => Promise<void>
234
+ ```
235
+
236
+ Move the app to the outer display. Android asks the user to confirm first,
237
+ and the promise resolves once the app has moved. Rejects when rear display
238
+ mode is not `'available'`. Only on Android.
239
+
240
+ **Since:** 7.0.0
241
+
242
+ --------------------
243
+
244
+
245
+ ### stopRearDisplay()
246
+
247
+ ```typescript
248
+ stopRearDisplay() => Promise<void>
249
+ ```
250
+
251
+ Move the app back to the inner display.
252
+
253
+ **Since:** 7.0.0
254
+
255
+ --------------------
256
+
257
+
258
+ ### startDualScreen(...)
259
+
260
+ ```typescript
261
+ startDualScreen(options: { url: string; }) => Promise<void>
262
+ ```
263
+
264
+ Show a page on the outer display while the app stays on the inner one. A
265
+ relative `url` resolves against the app's own URL, so `'cover.html'` loads a
266
+ page bundled with the app. The page runs in its own web view without access
267
+ to Capacitor plugins. Calling this again while dual-screen mode is active
268
+ replaces the page. Only on Android.
269
+
270
+ | Param | Type |
271
+ | ------------- | ----------------------------- |
272
+ | **`options`** | <code>{ url: string; }</code> |
273
+
274
+ **Since:** 7.0.0
275
+
276
+ --------------------
277
+
278
+
279
+ ### stopDualScreen()
280
+
281
+ ```typescript
282
+ stopDualScreen() => Promise<void>
283
+ ```
284
+
285
+ Close the page on the outer display.
286
+
287
+ **Since:** 7.0.0
288
+
289
+ --------------------
290
+
291
+
292
+ ### addListener('foldStateChange', ...)
293
+
294
+ ```typescript
295
+ addListener(eventName: 'foldStateChange', listenerFunc: (state: FoldState) => void) => Promise<PluginListenerHandle>
296
+ ```
297
+
298
+ Listen for fold state changes. On a foldable this also fires when the
299
+ device rotates, because `hingeOrientation` and `hingeBounds` rotate with
300
+ the window.
301
+
302
+ | Param | Type |
303
+ | ------------------ | ------------------------------------------------------------------- |
304
+ | **`eventName`** | <code>'foldStateChange'</code> |
305
+ | **`listenerFunc`** | <code>(state: <a href="#foldstate">FoldState</a>) =&gt; void</code> |
306
+
307
+ **Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</code>
308
+
309
+ **Since:** 7.0.0
310
+
311
+ --------------------
312
+
313
+
314
+ ### addListener('hingeAngleChange', ...)
315
+
316
+ ```typescript
317
+ addListener(eventName: 'hingeAngleChange', listenerFunc: (event: { angle: number; }) => void) => Promise<PluginListenerHandle>
318
+ ```
319
+
320
+ Listen for hinge angle changes. On Android the hinge sensor only runs while
321
+ at least one of these listeners is registered. On iOS it fires on iPhone
322
+ Duo (iOS 27.1 or later). Never fires on web.
323
+
324
+ | Param | Type |
325
+ | ------------------ | --------------------------------------------------- |
326
+ | **`eventName`** | <code>'hingeAngleChange'</code> |
327
+ | **`listenerFunc`** | <code>(event: { angle: number; }) =&gt; void</code> |
328
+
329
+ **Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</code>
330
+
331
+ **Since:** 7.0.0
332
+
333
+ --------------------
334
+
335
+
336
+ ### addListener('sizeClassChange', ...)
337
+
338
+ ```typescript
339
+ addListener(eventName: 'sizeClassChange', listenerFunc: (sizeClass: SizeClass) => void) => Promise<PluginListenerHandle>
340
+ ```
341
+
342
+ Listen for size class changes, such as unfolding the device, rotating it or
343
+ resizing the window. On iOS this fires when UIKit's size classes change
344
+ (iOS 17 or later) or the device rotates, so a resize that keeps the same
345
+ size classes may not update `widthClass` and `heightClass` until then.
346
+
347
+ | Param | Type |
348
+ | ------------------ | ----------------------------------------------------------------------- |
349
+ | **`eventName`** | <code>'sizeClassChange'</code> |
350
+ | **`listenerFunc`** | <code>(sizeClass: <a href="#sizeclass">SizeClass</a>) =&gt; void</code> |
351
+
352
+ **Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</code>
353
+
354
+ **Since:** 7.0.0
355
+
356
+ --------------------
357
+
358
+
359
+ ### addListener('displayModeChange', ...)
360
+
361
+ ```typescript
362
+ addListener(eventName: 'displayModeChange', listenerFunc: (modes: DisplayModes) => void) => Promise<PluginListenerHandle>
363
+ ```
364
+
365
+ Listen for changes to the display modes, including a mode ending because
366
+ the user folded or unfolded the device. Never fires on iOS and web.
367
+
368
+ | Param | Type |
369
+ | ------------------ | ------------------------------------------------------------------------- |
370
+ | **`eventName`** | <code>'displayModeChange'</code> |
371
+ | **`listenerFunc`** | <code>(modes: <a href="#displaymodes">DisplayModes</a>) =&gt; void</code> |
372
+
373
+ **Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</code>
374
+
375
+ **Since:** 7.0.0
376
+
377
+ --------------------
378
+
379
+
380
+ ### addListener('barPlacementChange', ...)
381
+
382
+ ```typescript
383
+ addListener(eventName: 'barPlacementChange', listenerFunc: (placement: BarPlacement) => void) => Promise<PluginListenerHandle>
384
+ ```
385
+
386
+ Listen for bar placement changes, such as opening or rotating iPhone Duo.
387
+ Only fires on iOS 27.1 or later.
388
+
389
+ | Param | Type |
390
+ | ------------------ | ----------------------------------------------------------------------------- |
391
+ | **`eventName`** | <code>'barPlacementChange'</code> |
392
+ | **`listenerFunc`** | <code>(placement: <a href="#barplacement">BarPlacement</a>) =&gt; void</code> |
393
+
394
+ **Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</code>
395
+
396
+ **Since:** 7.0.0
397
+
398
+ --------------------
399
+
400
+
401
+ ### Interfaces
402
+
403
+
404
+ #### FoldState
405
+
406
+ | Prop | Type | Description | Since |
407
+ | ---------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
408
+ | **`state`** | <code>'flat' \| 'half-opened' \| 'closed'</code> | Posture of the fold. `'closed'` is never reported today: a device shut on its cover display reports `'flat'`. | 7.0.0 |
409
+ | **`isSeparating`** | <code>boolean</code> | Whether the fold splits the web view into two areas: `true` when half-opened, or when the hinge has a physical gap. | 7.0.0 |
410
+ | **`posture`** | <code>'flat' \| 'tabletop' \| 'book'</code> | How the device is held: `'tabletop'` when half-opened with a horizontal hinge, like a laptop, `'book'` when half-opened with a vertical hinge, and `'flat'` otherwise. | 7.0.0 |
411
+ | **`hingeOrientation`** | <code>'horizontal' \| 'vertical'</code> | Direction of the hinge relative to the window, so it flips when the device rotates. Omitted when there is no fold. | 7.0.0 |
412
+ | **`hingeBounds`** | <code>{ x: number; y: number; width: number; height: number; }</code> | Position of the fold in CSS pixels, relative to the web view. Zero wide (or zero tall) on a seamless fold. Omitted when there is no fold. | 7.0.0 |
413
+ | **`occludedBounds`** | <code>{ x: number; y: number; width: number; height: number; }</code> | Area of the web view the hinge covers, in CSS pixels. Only present on devices with a physical gap. | 7.0.0 |
414
+ | **`activeDisplay`** | <code>'inner' \| 'outer'</code> | Which display is showing the app, on a device with an inner and an outer display such as iPhone Duo. Reported on iPhone Duo (iOS 27.1 or later) and omitted elsewhere. | 7.0.0 |
415
+ | **`cameraBounds`** | <code>{ x: number; y: number; width: number; height: number; }[]</code> | Areas of the web view covered by a front-facing camera, in CSS pixels. On Android these are the display cutouts. On iPhone Duo they are the outer camera and the under-display inner camera while it is in use. Omitted when there are none. | 7.0.0 |
416
+
417
+
418
+ #### SizeClass
419
+
420
+ | Prop | Type | Description | Since |
421
+ | ----------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
422
+ | **`horizontal`** | <code>'compact' \| 'regular'</code> | Width size class of the window: `'compact'` on a phone and on the outer display of a foldable, `'regular'` on the inner display, tablets and wide windows. On Android and web `'regular'` starts at 600 CSS pixels. | 7.0.0 |
423
+ | **`vertical`** | <code>'compact' \| 'regular'</code> | Height size class of the window: `'compact'` on a phone in landscape. On Android and web `'regular'` starts at 480 CSS pixels. | 7.0.0 |
424
+ | **`widthClass`** | <code>'compact' \| 'medium' \| 'expanded' \| 'large' \| 'extraLarge'</code> | Material window width class, from the window width in CSS pixels: `'compact'` below 600, `'medium'` below 840, `'expanded'` below 1200, `'large'` below 1600 and `'extraLarge'` from 1600. | 7.0.0 |
425
+ | **`heightClass`** | <code>'compact' \| 'medium' \| 'expanded'</code> | Material window height class, from the window height in CSS pixels: `'compact'` below 480, `'medium'` below 900 and `'expanded'` from 900. | 7.0.0 |
426
+
427
+
428
+ #### DisplayModes
429
+
430
+ | Prop | Type | Description | Since |
431
+ | ----------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
432
+ | **`rearDisplay`** | <code><a href="#displaymodestatus">DisplayModeStatus</a></code> | Rear display mode moves the app to the outer display, so people can frame a photo with the rear cameras. Only on Android foldables that offer it. | 7.0.0 |
433
+ | **`dualScreen`** | <code><a href="#displaymodestatus">DisplayModeStatus</a></code> | Dual-screen mode shows a second page on the outer display while the app stays on the inner one. Only on Android foldables that offer it. | 7.0.0 |
434
+
435
+
436
+ #### BarPlacement
437
+
438
+ | Prop | Type | Description | Since |
439
+ | --------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
440
+ | **`verticalBarEdge`** | <code>'leading' \| 'trailing' \| null</code> | The edge iPhone Duo moves tab bars and toolbars to when it lays them out vertically: `'leading'` or `'trailing'` in the reading direction, so `'leading'` is the left edge in left-to-right languages. `null` when bars stay horizontal, and always on Android, web and iOS before 27.1. | 7.0.0 |
441
+
442
+
443
+ #### PluginListenerHandle
444
+
445
+ | Prop | Type |
446
+ | ------------ | ----------------------------------------- |
447
+ | **`remove`** | <code>() =&gt; Promise&lt;void&gt;</code> |
448
+
449
+
450
+ ### Type Aliases
451
+
452
+
453
+ #### DisplayModeStatus
454
+
455
+ <code>'unsupported' | 'unavailable' | 'available' | 'active'</code>
456
+
457
+ </docgen-api>
@@ -0,0 +1,71 @@
1
+ ext {
2
+ junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
3
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
4
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
5
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
6
+ androidxCoreKTXVersion = project.hasProperty('androidxCoreKTXVersion') ? rootProject.ext.androidxCoreKTXVersion : '1.15.0'
7
+ androidxWindowVersion = project.hasProperty('androidxWindowVersion') ? rootProject.ext.androidxWindowVersion : '1.5.1'
8
+ androidxLifecycleVersion = project.hasProperty('androidxLifecycleVersion') ? rootProject.ext.androidxLifecycleVersion : '2.8.7'
9
+ }
10
+
11
+ buildscript {
12
+ ext.kotlin_version = project.hasProperty("kotlin_version") ? rootProject.ext.kotlin_version : '2.1.21'
13
+ repositories {
14
+ google()
15
+ mavenCentral()
16
+ }
17
+ dependencies {
18
+ classpath 'com.android.tools.build:gradle:8.13.0'
19
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
20
+ }
21
+ }
22
+
23
+ apply plugin: 'com.android.library'
24
+ apply plugin: 'kotlin-android'
25
+
26
+ android {
27
+ namespace = "io.github.erkamyaman.foldable"
28
+ compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
29
+ defaultConfig {
30
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
31
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
32
+ versionCode 1
33
+ versionName "1.0"
34
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
35
+ }
36
+ buildTypes {
37
+ release {
38
+ minifyEnabled false
39
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
40
+ }
41
+ }
42
+ lintOptions {
43
+ abortOnError = false
44
+ }
45
+ compileOptions {
46
+ sourceCompatibility JavaVersion.VERSION_21
47
+ targetCompatibility JavaVersion.VERSION_21
48
+ }
49
+ }
50
+
51
+ kotlin {
52
+ jvmToolchain(21)
53
+ }
54
+
55
+ repositories {
56
+ google()
57
+ mavenCentral()
58
+ }
59
+
60
+
61
+ dependencies {
62
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
63
+ implementation project(':capacitor-android')
64
+ implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
65
+ implementation "androidx.core:core-ktx:$androidxCoreKTXVersion"
66
+ implementation "androidx.window:window:$androidxWindowVersion"
67
+ implementation "androidx.lifecycle:lifecycle-runtime-ktx:$androidxLifecycleVersion"
68
+ testImplementation "junit:junit:$junitVersion"
69
+ androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
70
+ androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
71
+ }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>