{"id":1127,"date":"2020-12-17T12:21:45","date_gmt":"2020-12-17T20:21:45","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/surface-duo\/?p=1127"},"modified":"2020-12-17T12:21:45","modified_gmt":"2020-12-17T20:21:45","slug":"dual-screen-cordova-plugin","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/surface-duo\/dual-screen-cordova-plugin\/","title":{"rendered":"Build dual-screen Cordova apps for Microsoft Surface Duo"},"content":{"rendered":"<p>\n  Hello hybrid app developers,\n<\/p>\n<p>\n  This week I\u2019m happy to share an example plugin for use with <a href=\"https:\/\/cordova.apache.org\/\">Cordova<\/a> that will help to enhance your hybrid apps for dual-screens. The plugin includes <strong>DisplayMask.jar<\/strong> to detect the Surface Duo and return information about the masked hinge area when the app is spanned, and exposes it in JavaScript for Cordova apps to consume. In addition to the <a href=\"https:\/\/github.com\/conceptdev\/cordova-dualscreeninfo-plugin\/\">open-source plugin code<\/a> there are three Cordova sample apps <a href=\"https:\/\/github.com\/conceptdev\/cordova-samples\">available on GitHub<\/a> to show how to use it.\n<\/p>\n<h2>Cordova plugin<\/h2>\n<p>\n  The plugin example can be easily included in your app with the following command:\n<\/p>\n<pre>cordova\u00a0plugin\u00a0add\u00a0https:\/\/github.com\/conceptdev\/cordova-dualscreeninfo-plugin\/<\/pre>\n<p>\n  Once added to your Cordova app, you can use the methods provided by the plugin to get information about the device and, if it is a Surface Duo, whether the app is spanned and masked by the hinge:\n<\/p>\n<ul>\n<li>\n  <code>window.ScreenHelper.isDualScreenDevice<\/code> \u2013 Use this method to determine if the device is a Surface Duo. Returns <code>true<\/code> or <code>false<\/code>.\n<\/li>\n<li>\n  <code>window.ScreenHelper.getHinge<\/code> \u2013 Returns the coordinates of the hinge (if the app is spanned) as a comma-separated string <code>\"x,y,width,height\"<\/code>. All zeros <code>\"0,0,0,0\"<\/code> will be returned if the app is not spanned and the hinge can\u2019t be detected (or the app is running on a single-screen device).\n<\/li>\n<\/ul>\n<p>\n  Using these methods you can detect when your app is running on a Surface Duo, whether it is spanned, and the orientation. The samples include a <strong>Hinge<\/strong> class to help work with the data returned.\n<\/p>\n<h2>Default app template demo<\/h2>\n<p>\n  By adding the plugin to a <strong>cordova new<\/strong> app template, I used the two plugin methods to enhance the HTML and JavaScript for dual-screens:\n<\/p>\n<ul>\n<li>\n    Show information about the device, screen size, and spanned state of the app.\n  <\/li>\n<li>\n    Display a \u2018debug\u2019 hinge that will draw slightly larger than the masked area (for testing, not required for your live apps).\n  <\/li>\n<li>\n    Position the content correctly when the app is spanned, and add a Surface Duo photo on the second screen.\n  <\/li>\n<\/ul>\n<p>\n  When spanned, the app shows the content side-by-side with the hinge debug visualization in the middle:\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"1069\" height=\"836\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/12\/word-image-3.png\" class=\"wp-image-1128\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/12\/word-image-3.png 1069w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/12\/word-image-3-300x235.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/12\/word-image-3-1024x801.png 1024w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/12\/word-image-3-768x601.png 768w\" sizes=\"(max-width: 1069px) 100vw, 1069px\" \/><br\/><em>Figure 1: Default Cordova app template enhanced with a dual-screen aware layout<\/em>\n<\/p>\n<p>\n  The basic structure of the <strong>index.html<\/strong> page consists of three <strong>div<\/strong> elements (the left and right panes, and the hinge between them):\n<\/p>\n<pre>&lt;div class=\"app\">\r\n    &lt;h1>Apache Cordova&lt;\/h1>&lt;!--text placeholders omitted for clarity-->\r\n&lt;\/div>\r\n&lt;div class=\"fold angled stripes single\" id=\"debughinge\">&lt;\/div>\r\n&lt;div class=\"secondscreen\">&lt;\/div><\/pre>\n<p>\n  The JavaScript for the page (in <strong>js\/index.js<\/strong>) requires an event listener for the <code>resize<\/code> event:\n<\/p>\n<pre>window.addEventListener('resize',\u00a0onResize,\u00a0true);<\/pre>\n<p>\n  The <code>onResize<\/code> function uses the <code>ScreenHelper.getHinge<\/code> method from the plugin to get the device metrics that indicate whether the app is spanned, and positions elements to adapt to the hinge and device orientation:\n<\/p>\n<pre>function onResize() {\r\n    window.ScreenHelper.getHinge(\r\n        function(result) { \r\n            var h = new Hinge (result);\r\n            if (h.isSpanned) {\r\n                \/\/ show the debug hinge visualization, and show the second screen content\r\n                document.getElementById('debughinge').classList.add('spanned');\r\n                document.getElementsByClassName(\"secondscreen\")[0].style.display = 'block';\r\n                if (h.isVertical) { \/\/ move the app content to the left\r\n                    document.getElementsByClassName(\"app\")[0].style.left = '25%';\r\n                    document.getElementsByClassName(\"app\")[0].style.top = '50%';\r\n                }\r\n\/\/ ... rest of the layout changes ...\r\n}, function(error) { }\r\n    );\r\n}<\/pre>\n<p>\n  The complete sample code is <a href=\"https:\/\/github.com\/conceptdev\/cordova-samples\/tree\/main\/plugin-demo\">available on GitHub<\/a> \u2013 this approach could be used with existing Cordova apps to adjust the layout when spanned on a Surface Duo.\n<\/p>\n<h2>Foldable CSS demo<\/h2>\n<p>\n  CSS and JavaScript browser extensions were discussed in an earlier blog post on <a href=\"https:\/\/devblogs.microsoft.com\/surface-duo\/build-and-test-dual-screen-web-apps\/\">building and testing dual-screen web apps<\/a>. While the web view that\u2019s embedded in Cordova does not yet natively support those extensions, it\u2019s possible to use the <a href=\"https:\/\/github.com\/foldable-devices\/\">polyfill implementations<\/a> (in conjunction with the plugin) to get them working in your apps. Here\u2019s how the <strong>boxes.html<\/strong> page looks when rendered inside Cordova (I added some app metrics text to each box):\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"1129\" height=\"881\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/12\/word-image-4.png\" class=\"wp-image-1129\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/12\/word-image-4.png 1129w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/12\/word-image-4-300x234.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/12\/word-image-4-1024x799.png 1024w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/12\/word-image-4-768x599.png 768w\" sizes=\"(max-width: 1129px) 100vw, 1129px\" \/><br\/><em>Figure 2: <\/em><a href=\"https:\/\/github.com\/MicrosoftEdge\/MSEdgeExplainers\/blob\/main\/Foldables\/explainer.md#colored-boxes-absolutely-positioned\"><em>Explainer<\/em><\/a><em> dual-screen CSS layout reproduced in a Cordova app<\/em>\n<\/p>\n<p>\n  You can download the <a href=\"https:\/\/github.com\/conceptdev\/cordova-samples\/tree\/main\/plugin-css-demo\">app source from GitHub<\/a> to see how it works \u2013 the key elements are summarized below.\n<\/p>\n<p>\n  The HTML consists of elements that are positioned using CSS, and includes the polyfill JavaScript:\n<\/p>\n<pre>&lt;div class=\"blue>&lt;\/div>&lt;!--text placeholders omitted for clarity-->\r\n&lt;div class=\"yellow\">&lt;\/div>\r\n&lt;div class=\"pink\">&lt;\/div>\r\n&lt;div class=\"green\">&lt;\/div>\r\n&lt;div class=\"fold angled stripes\">&lt;\/div>\r\n&lt;!-- Polyfills for dual screen capabilities -->\r\n&lt;script src=\"js\/spanningcsspolyfill.js\">&lt;\/script>\r\n&lt;script src=\"js\/windowsegmentspolyfill.js\">&lt;\/script><\/pre>\n<p>\n  The CSS uses the dual-screen <strong>@media<\/strong> query for <strong>screen-spanning<\/strong> to position the elements differently for each screen configuration (excerpt shown):\n<\/p>\n<pre>@media (screen-spanning: single-fold-vertical) {\r\n    .fold {\r\n      height: env(fold-height);\r\n      width: env(fold-width);\r\n      left: env(fold-left);\r\n      top: 0;\r\n    }\r\n    .blue {\r\n        left: calc(env(fold-left) - 100px);\r\n    }\r\n    .yellow {\r\n        width: calc(100vw - env(fold-left) - env(fold-width));  \/*fold-right*\/\r\n        left: calc(env(fold-left) + env(fold-width)); \/*fold-right*\/\r\n        top: 0;\r\n    }\r\n    \/* other styles *\/\r\n  }\r\n  @media (screen-spanning: single-fold-horizontal) {\r\n    \/* double-landscape styles *\/ \r\n  }\r\n  @media (screen-spanning: none) {\r\n    .fold {\r\n      height: 0;\r\n      width: 0;\r\n    }\r\n    \/* other single-screen styles *\/ \r\n}<\/pre>\n<p>\n  Finally, a resize event listener uses the plugin to detect when the app is spanned and supply relevant information to update the polyfills (which in turn processes the CSS and updates the layout):\n<\/p>\n<pre>window.ScreenHelper.getHinge(\r\n    function(result) { \r\n        var h = new Hinge (result);\r\n        if (h.isSpanned) {\r\n            if (h.isVertical) {\r\n                window[\"__foldables_env_vars__\"].update({spanning: 'single-fold-vertical', foldSize: parseInt(h.width)});\r\n            } else { \/\/ isHorizontal\r\n                window[\"__foldables_env_vars__\"].update({spanning: 'single-fold-horizontal', foldSize: parseInt(h.height)});\r\n            }\r\n        } else { \/\/ not spanned\r\n            window[\"__foldables_env_vars__\"].update({spanning: 'none', foldSize: 0});\r\n        }<\/pre>\n<h2>Photo Gallery sample<\/h2>\n<p>\n  This <a href=\"https:\/\/github.com\/foldable-devices\/demos\/tree\/master\/photo-gallery\">photo gallery sample<\/a> was created to demonstrate the foldable CSS <strong>@media screen-spanning<\/strong> feature <a href=\"https:\/\/foldable-devices.github.io\/demos\/photo-gallery\/\">in the browser<\/a>. It was embedded in a Cordova sample app using the approach described above to enable the CSS and JavaScript polyfills. The source is <a href=\"https:\/\/github.com\/conceptdev\/cordova-samples\/tree\/main\/photo-gallery-sample\">available on GitHub<\/a>, and a screenshot is shown below:\n<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/12\/cordova-photo-gallery.jpg\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/12\/cordova-photo-gallery.jpg\" alt=\"Cordova photo gallery example on Surface Duo\" width=\"1031\" height=\"806\" class=\"alignnone size-full wp-image-1134\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/12\/cordova-photo-gallery.jpg 1031w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/12\/cordova-photo-gallery-300x235.jpg 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/12\/cordova-photo-gallery-1024x801.jpg 1024w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2020\/12\/cordova-photo-gallery-768x600.jpg 768w\" sizes=\"(max-width: 1031px) 100vw, 1031px\" \/><\/a><br\/><em>Figure 3: Dual-screen web apps can be brought to Cordova with a few lines of code<\/em>\n<\/p>\n<h2>Feedback<\/h2>\n<p>\n  If you are using Cordova, Ionic, or any other web-based app deployment tools we\u2019d love to hear about your app and how it might be enhanced for dual-screens. \n<\/p>\n<p>\n  Please reach to out to the team using our\u00a0<a href=\"http:\/\/aka.ms\/SurfaceDuoSDK-Feedback\" target=\"_blank\" rel=\"noopener noreferrer\">feedback forum<\/a>\u00a0or message us on Twitter <a href=\"https:\/\/twitter.com\/surfaceduodev\">@surfaceduodev<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hello hybrid app developers, This week I\u2019m happy to share an example plugin for use with Cordova that will help to enhance your hybrid apps for dual-screens. The plugin includes DisplayMask.jar to detect the Surface Duo and return information about the masked hinge area when the app is spanned, and exposes it in JavaScript for [&hellip;]<\/p>\n","protected":false},"author":570,"featured_media":1128,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[707,31,708,412,530],"class_list":["post-1127","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-surface-duo-sdk","tag-cordova","tag-dual-screen-development","tag-hybrid","tag-javascript","tag-web"],"acf":[],"blog_post_summary":"<p>Hello hybrid app developers, This week I\u2019m happy to share an example plugin for use with Cordova that will help to enhance your hybrid apps for dual-screens. The plugin includes DisplayMask.jar to detect the Surface Duo and return information about the masked hinge area when the app is spanned, and exposes it in JavaScript for [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/1127","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/users\/570"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/comments?post=1127"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/1127\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media\/1128"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media?parent=1127"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/categories?post=1127"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/tags?post=1127"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}