{"id":2955,"date":"2022-12-15T11:53:55","date_gmt":"2022-12-15T19:53:55","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/surface-duo\/?p=2955"},"modified":"2022-12-15T11:53:55","modified_gmt":"2022-12-15T19:53:55","slug":"drag-and-drop-androidx","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/surface-duo\/drag-and-drop-androidx\/","title":{"rendered":"Drag and drop with AndroidX"},"content":{"rendered":"<p>\n  Hello Android developers,\n<\/p>\n<p>\n  Foldable and large-screen devices are great for multi-tasking \u2013 you can position two apps side-by-side to compare data or just do two things at once! The other benefit of side-by-side apps is the ability to drag and drop content between them, whether the apps are across screens on a dual-screen device, or next to each other in multi-window mode on a tablet device.\n<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/12\/two-devices.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/12\/two-devices-1024x604.png\" alt=\"a tablet and a surface duo both showing drag and drop samples\" width=\"640\" height=\"378\" class=\"alignnone size-large wp-image-2959\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/12\/two-devices-1024x604.png 1024w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/12\/two-devices-300x177.png 300w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/12\/two-devices-768x453.png 768w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/12\/two-devices-1536x907.png 1536w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/12\/two-devices-2048x1209.png 2048w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/a><\/p>\n<p>The Surface Duo drag and drop sample has been updated to use the <a href=\"https:\/\/developer.android.com\/reference\/kotlin\/androidx\/draganddrop\/package-summary.html\">AndroidX drag and drop package<\/a> to show how to add this feature to your apps. This package provides helper methods to simplify drag and drop implementation.\n<\/p>\n<p>\n  The <a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk-samples-kotlin\">Surface Duo SDK samples GitHub repo<\/a> includes a drag-and-drop demo that works both within the sample app as well as dragging to other apps. Here\u2019s a screenshot of the app spanned across both screens \u2013 the image and text can be dragged and dropped into the container targets.\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"683\" height=\"542\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/12\/graphical-user-interface-application-description-1.png\" class=\"wp-image-2956\" alt=\"Surface Duo with drag and drop sample\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/12\/graphical-user-interface-application-description-1.png 683w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/12\/graphical-user-interface-application-description-1-300x238.png 300w\" sizes=\"(max-width: 683px) 100vw, 683px\" \/>\n<\/p>\n<p>\n  To use the AndroidX drag and drop helpers, add the following to the module\u2019s <strong>build.gradle<\/strong> file dependencies:\n<\/p>\n<pre>implementation 'androidx.draganddrop:draganddrop:1.0.0'<\/pre>\n<p>\n  More information on AndroidX drag and drop is in the <a href=\"https:\/\/developer.android.com\/jetpack\/androidx\/releases\/draganddrop\">release notes<\/a> and <a href=\"https:\/\/developer.android.com\/guide\/topics\/ui\/drag-drop\">docs<\/a>. Google also provides <a href=\"https:\/\/developer.android.com\/develop\/ui\/views\/touch-and-input\/drag-drop\">detailed guidance for implementing drag and drop<\/a> that explains implementations with built-in Android APIs as well as using the AndroidX helper package.\n<\/p>\n<h2>Drag and drop text<\/h2>\n<p>\n  The simplest element to drag and drop is plain text. The <a href=\"https:\/\/developer.android.com\/reference\/androidx\/core\/view\/DragStartHelper\">DragStartHelper<\/a> class is attached to a view, and will detect common drag gestures (eg. long click), which helps track the drag on screen (including the <a href=\"https:\/\/developer.android.com\/develop\/ui\/views\/touch-and-input\/drag-drop#AboutDragShadowBuilder\">shadow)<\/a>.\n<\/p>\n<p>\n  The sample implements <code>DragStartHelper<\/code> for the text view in <code>DragSourceFragment.onViewCreated<\/code>:\n<\/p>\n<pre>DragStartHelper(binding.dragTextView) { sourceView, _ -&gt;\r\n    val text = (sourceView as TextView).text\r\n    val dragClipData = ClipData.newPlainText(ClipDescription.MIMETYPE_TEXT_PLAIN, text)\r\n    val dragShadowBuilder = View.DragShadowBuilder(sourceView)\r\n    sourceView.startDragAndDrop(dragClipData, dragShadowBuilder, null, DRAG_FLAG_GLOBAL)\r\n}.attach()<\/pre>\n<p>\n  Text content can be passed directly in the drag message so the clip data object that\u2019s created contains the text and the method to accept the dropped data is relatively simple. To receive dragged text, the sample <code><a href=\"https:\/\/developer.android.com\/reference\/kotlin\/androidx\/draganddrop\/DropHelper\">DropHelper<\/a><\/code> is implemented in <code>DropTargetFragment.onViewCreated<\/code>:\n<\/p>\n<pre>DropHelper.configureView(\r\n    requireActivity(),\r\n    targetText,\r\n    arrayOf(\r\n        ClipDescription.MIMETYPE_TEXT_PLAIN,\r\n        MIME_TYPE_EXTERNAL\r\n    ),\r\n    DropHelper.Options.Builder()\r\n        .setHighlightColor(requireContext().getColor(R.color.gray))\r\n        .setHighlightCornerRadiusPx(0)\r\n        .build()\r\n) { _, payload -&gt;\r\n    val item = payload.clip.getItemAt(0)\r\n    val remaining = payload.partition { it == item }.second\r\n    if (payload.clip.description.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {\r\n        targetTextView.text = item.text  \/\/ could also be a URI, see the full code in the sample\r\n    }\r\n    remaining\r\n}<\/pre>\n<p>\n  The parameters of <code>DropHelper.configureView<\/code> perform the following:\n<\/p>\n<ul>\n<li>\n    Attaches to the Activity\n  <\/li>\n<li>\n    Attaches to the TextView where items can be dropped\n  <\/li>\n<li>\n    Sets the filter of allowable content types \n  <\/li>\n<li>\n    Defines the drop visual indicator properties\n  <\/li>\n<\/ul>\n<p>\n  The method body is executed when the drop is completed, and shows how to extract text and assign it to the target text view (you could also take other steps here if required). \n<\/p>\n<p>\n  Supporting text drag and drop requires just a few lines of code and is a great first step towards a better multi-tasking user experience. \n<\/p>\n<h2>Drag and drop an image<\/h2>\n<p>\n  Here\u2019s the sample app side-by-side with our <a href=\"https:\/\/github.com\/microsoft\/surface-duo-window-manager-samples\/tree\/main\/TwoNote\">TwoNote sample<\/a>, which demonstrates dragging an image across applications while multi-tasking.\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"678\" height=\"537\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/12\/graphical-user-interface-application-description-2.png\" class=\"wp-image-2957\" alt=\"Surface Duo with drag and drop sample and TwoNote sample\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/12\/graphical-user-interface-application-description-2.png 678w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2022\/12\/graphical-user-interface-application-description-2-300x238.png 300w\" sizes=\"(max-width: 678px) 100vw, 678px\" \/>\n<\/p>\n<p>\n  The <code>DragStartHelper<\/code> implementation in <code>DragSourceFragment.onViewCreated<\/code> looks similar to the draggable text version above, but unlike text data, binary data like images is not included in the drag and drop message, instead a URI is created which is used by the application where the drop occurs to retrieve the image data. Here\u2019s a summarized version of the <a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk-samples-kotlin\/blob\/main\/DragAndDrop\/src\/main\/java\/com\/microsoft\/device\/display\/samples\/draganddrop\/fragment\/DragSourceFragment.kt#L67-L101\">code<\/a>:\n<\/p>\n<pre>\r\nDragStartHelper(binding.dragImageView) { view, _ -&gt;\r\n    \/* code removed from blog for clarity:\r\n    *  - Ensure file exists provider location\r\n    *  - Get URI from FileProvider *\/\r\n    val dragClipData = ClipData.newUri(requireContext().contentResolver, DRAG_FOLDER_NAME, imageUri)\r\n    val dragShadow = View.DragShadowBuilder(view)\r\n    view.startDragAndDrop(\r\n        dragClipData,\r\n        dragShadow,\r\n        null,\r\n        \/\/ Need to Use the DRAG_FLAG_GLOBAL_URI_READ to allow other apps to read from our\r\n        \/\/ content provider. Without it, other apps won't receive the drag events.\r\n            DRAG_FLAG_GLOBAL.or(View.DRAG_FLAG_GLOBAL_URI_READ)\r\n        )\r\n    }.attach()\r\n}<\/pre>\n<p>\n  The <code>FileProvider<\/code> has been set up using the <code>&lt;provider&gt;<\/code> element in the <strong>AndroidManifest.xml<\/strong>, with the file provider path defined in XML matching the path that\u2019s used in the fragment to ensure the image file exists in filesystem. When the image is dropped, a <a href=\"https:\/\/github.com\/microsoft\/surface-duo-sdk-samples-kotlin\/blob\/main\/DragAndDrop\/src\/main\/java\/com\/microsoft\/device\/display\/samples\/draganddrop\/fragment\/DropTargetFragment.kt#LL179\">content resolver is used<\/a> to retrieve the image data stream from the URI and process it (save to disk or display on screen).\n<\/p>\n<p>\n  Going beyond simple text to images, formatted text, or other complex data types adds polish to your multi-tasking user experience for apps on dual-screen, foldable, and large screens like tablets or desktops.\n<\/p>\n<h2>Resources and feedback<\/h2>\n<p>\n  Please share your experiences with creating foldable and large screen user experiences for multi-tasking &#8211; reach out via the\u00a0<a href=\"http:\/\/aka.ms\/SurfaceDuoSDK-Feedback\">feedback forum<\/a>\u00a0or on Twitter\u00a0<a href=\"https:\/\/twitter.com\/surfaceduodev\">@surfaceduodev<\/a>.\u00a0  \n<\/p>\n<p>\n  Our last <a href=\"https:\/\/twitch.tv\/surfaceduodev\">Twitch livestream<\/a> for the year will be on Friday, 16<sup>th<\/sup> December at 11am PST to chat about drag and drop, and anything else you\u2019re curious to learn. We\u2019ll be taking a break from streaming over the holidays &#8211; see you online again in 2023!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hello Android developers, Foldable and large-screen devices are great for multi-tasking \u2013 you can position two apps side-by-side to compare data or just do two things at once! The other benefit of side-by-side apps is the ability to drag and drop content between them, whether the apps are across screens on a dual-screen device, or [&hellip;]<\/p>\n","protected":false},"author":570,"featured_media":2959,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[717,473],"class_list":["post-2955","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-surface-duo-sdk","tag-foldable","tag-kotlin"],"acf":[],"blog_post_summary":"<p>Hello Android developers, Foldable and large-screen devices are great for multi-tasking \u2013 you can position two apps side-by-side to compare data or just do two things at once! The other benefit of side-by-side apps is the ability to drag and drop content between them, whether the apps are across screens on a dual-screen device, or [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/2955","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=2955"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/2955\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media\/2959"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media?parent=2955"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/categories?post=2955"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/tags?post=2955"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}