<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Android – IMG.LY Blog</title><description>Posts tagged Android on the IMG.LY blog.</description><link>https://img.ly/blog/tag/android/</link><language>en-us</language><image><url>https://img.ly/apple-touch-icon.png</url><title>Android – IMG.LY Blog</title><link>https://img.ly/blog/tag/android/</link></image><atom:link href="https://img.ly/blog/tag/android/rss.xml" rel="self" type="application/rss+xml"/><generator>Astro</generator><lastBuildDate>Fri, 19 Jun 2026 11:26:04 GMT</lastBuildDate><ttl>60</ttl><item><title>CE.SDK Video Editor Now Available on Android</title><link>https://img.ly/blog/ce-sdk-video-editor-now-available-on-android/</link><guid isPermaLink="true">https://img.ly/blog/ce-sdk-video-editor-now-available-on-android/</guid><description>Integrate a fully-customizable Video Editor into your Android App with CE.SDK.</description><pubDate>Tue, 22 Oct 2024 11:44:02 GMT</pubDate><content:encoded>&lt;p&gt;We are excited to announce the release of CE.SDK’s video editing and video content creation tools for Android!&lt;/p&gt;
&lt;p&gt;After the successful iOS launch, CE.SDK’s professional video editing capabilities are now available for Android. Whether your app focuses on social media, e-commerce, or marketing technology, CE.SDK enables users to create high-quality videos directly within your app.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-36/uncompressed/Android-crop-split-trim.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h2 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;Getting up and running with an instance of the CE.SDK video editor for Android is easy.&lt;/p&gt;
&lt;p&gt;After downloading your license (get your trial license here) from your dashboard, simply &lt;a href=&quot;https://img.ly/docs/cesdk/android/starterkits/video-editor-e1nlor/&quot;&gt;invoke &lt;code&gt;VideoEditor&lt;/code&gt; as composable function, Activity or Fragment.&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;our-video-editing-backstory&quot;&gt;Our Video Editing Backstory&lt;/h2&gt;
&lt;p&gt;Since we launched VideoEditor SDK almost ten years ago, we have seen an astounding range of creative and unique use cases of our SDK in customer projects. From Cameo connecting celebrities and their fans in new ways, over Multibrain supercharging their users marketing campaigns with editable video content, to &lt;a href=&quot;https://zigazoo.com/&quot;&gt;Zigazoo&lt;/a&gt; powering video content within their social network for kids while putting safety and parental control first.&lt;/p&gt;
&lt;p&gt;Since then, millions of users have experienced IMG.LY’s VideoEditor SDK through our customers’ products, shaping how they expect to interact with and modify videos. Continuous feedback from both our customers and their users has also influenced the way we approach video interface design, helping us refine and innovate along the way.&lt;/p&gt;
&lt;p&gt;Our goal was to combine the reliability and robustness of the VideoEditor SDK with true cross-platform compatibility and the flexibility to meet diverse needs. By applying the lessons we’ve learned, we aimed to deliver an exceptional video editing user experience straight out of the box. We described how those learnings shaped the &lt;a href=&quot;https://img.ly/blog/designing-a-timeline-for-mobile-video-editing/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=androidvideo&quot;&gt;design of the mobile video editing interface.&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;key-capabilities&quot;&gt;Key Capabilities&lt;/h2&gt;
&lt;p&gt;With CE.SDK’s sleek timeline interface, your Android users can now take their video editing from simple transforms and adjustments to complex composition and multi-track editing:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Timeline-Based Editing&lt;/strong&gt;&lt;br&gt;
Split, trim, arrange video and audio tracks. Position elements such as images and stickers on the timeline and control their duration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multi-Track Editing&lt;/strong&gt;&lt;br&gt;
Work with multiple video and audio tracks, allowing for complex composition.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Transform &amp;#x26; Enhance&lt;/strong&gt;&lt;br&gt;
Crop, flip, rotate, and use presets for common video formats. Add filters, effects, blur and essential adjustments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Customizable Templates&lt;/strong&gt;&lt;br&gt;
Create and import templates using our web video editor, to jumpstart your users’ video composition and designs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Asset Integration&lt;/strong&gt;&lt;br&gt;
Integrate your own asset libraries for audio tracks, sound effects, images, and more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cross-platform Interoperability&lt;/strong&gt;&lt;br&gt;
CE.SDK’s video capabilities extends across iOS, Android and the Web, enabling cross-platform applications to offer seamless video editing capabilities. Users can edit and save work in progress and pick up on any device.&lt;/p&gt;
&lt;p&gt;Check out the &lt;a href=&quot;https://img.ly/demos/video-ui/android/&quot;&gt;showcase app for an interactive demo&lt;/a&gt; of these features.&lt;/p&gt;
&lt;h2 id=&quot;customization&quot;&gt;Customization&lt;/h2&gt;
&lt;p&gt;The ability to tailor the video editor to your app’s particular use case has been a front and center concern in its development.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/docs/cesdk/android/configuration-2c1c3d/&quot;&gt;&lt;strong&gt;UI Customization&lt;/strong&gt;&lt;/a&gt;: While theming is currently limited to &lt;code&gt;dark&lt;/code&gt; and &lt;code&gt;light&lt;/code&gt; model, future releases will introduce more flexible theming, allowing you to modify the look and feel of the editor to match your app’s branding. Furthermore, we expose &lt;strong&gt;callbacks&lt;/strong&gt; that make it possible to hook into editor events and &lt;strong&gt;overlays&lt;/strong&gt; for custom dialogues.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/docs/cesdk/android/create-video-c41a08/&quot;&gt;&lt;strong&gt;Configure Video Presets&lt;/strong&gt;&lt;/a&gt;: You can configure the editor to create video presets based on your users’ needs. For instance, you may want to fix the video’s aspect ratio or optimize for certain resolutions, depending on the target platform.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/docs/cesdk/android/use-templates/apply-template-35c73e/&quot;&gt;&lt;strong&gt;Video Templates&lt;/strong&gt;&lt;/a&gt;: With the CE.SDK video editor web UI, you can create an unlimited number of video templates, such as collages, text designs, and animations, providing your users with professional-quality starting points for their video designs.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/docs/cesdk/android/import-media/asset-panel/customize-c9a4de/&quot;&gt;&lt;strong&gt;Assets&lt;/strong&gt;&lt;/a&gt;: Provide custom filters, fonts, and stickers to enhance the user experience. For niche apps, this can be an excellent way to engage users by offering content that feels unique to your community.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;ideal-for-social-media-ecommerce-and-marketing-tech-apps&quot;&gt;Ideal for Social Media, Ecommerce, and Marketing Tech Apps&lt;/h2&gt;
&lt;p&gt;CE.SDK’s new Android video editing capabilities are perfect for a variety of use cases, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/use-cases/story-reels-short-video-creation/&quot;&gt;&lt;strong&gt;Social Media Applications&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;:&lt;/strong&gt; The CE.SDK video editor for Android excels at short-form video, ideal for social media applications, whether your users are publishing to other networks or you run a network yourself.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/industries/e-commerce/&quot;&gt;&lt;strong&gt;Ecommerce&lt;/strong&gt;&lt;/a&gt;: Allow your users to create engaging product showcases, ads, and promotional videos, testimonials or reviews.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/industries/marketing-tech/&quot;&gt;&lt;strong&gt;Marketing Tech Platforms&lt;/strong&gt;&lt;/a&gt;: Users can leverage our versatile templating to easily create polished social media and marketing graphics from within your app.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What’s Next?&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;Camera UI: Record videos on Android.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;988&quot; src=&quot;https://img.ly/_astro/camera-UI-Android-1_1FyMRS.webp&quot; srcset=&quot;/_astro/camera-UI-Android-1_ZjEi38.webp 640w, /_astro/camera-UI-Android-1_Z743FA.webp 750w, /_astro/camera-UI-Android-1_ZYwhh4.webp 828w, /_astro/camera-UI-Android-1_8a3zG.webp 1080w, /_astro/camera-UI-Android-1_19mjIs.webp 1280w, /_astro/camera-UI-Android-1_1FyMRS.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;This Android release is just the beginning! We are about to launch a &lt;strong&gt;Camera UI&lt;/strong&gt; for Android and are working on more advanced template creation features. So stay tuned.&lt;br&gt;
To see some of these features in action, check out our iOS demo, where the next phase of innovation is unfolding.&lt;/p&gt;
&lt;h2 id=&quot;get-started&quot;&gt;Get Started&lt;/h2&gt;
&lt;p&gt;Explore our &lt;a href=&quot;https://img.ly/demos/video-ui/android/&quot;&gt;Video UI Showcase&lt;/a&gt; and &lt;a href=&quot;https://img.ly/docs/cesdk/android/starterkits/video-editor-e1nlor/&quot;&gt;Documentation for Android&lt;/a&gt; to discover how CE.SDK can streamline your content creation projects and make Android video editing easier than ever.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Over 3,000 creative professionals gain early access to new features and updates—don’t miss out, and&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;&lt;strong&gt;subscribe&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;to our newsletter.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Jan</dc:creator><media:content url="https://blog.img.ly/2024/10/android-video-editor-sdk-imgly-creative-editor_s.jpg" medium="image"/><category>Android</category><category>Android App Development</category><category>Creative Editor</category><category>Video Editing</category><category>Video Editor</category></item><item><title>CE.SDK v1.20 Release Notes</title><link>https://img.ly/blog/creative-editor-sdk-v_1_20_0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v_1_20_0-release-notes/</guid><description>Merge photos, videos or text into fantastic shapes. Or bring image adjustments to your iOS app with CE.SDK v1.20!</description><pubDate>Mon, 29 Jan 2024 10:37:42 GMT</pubDate><content:encoded>&lt;p&gt;Since our &lt;a href=&quot;https://img.ly/blog/creative-editor-sdk-v1-19-release-notes/&quot;&gt;last release&lt;/a&gt;, we’ve been crafting new features to empower your users’ creative journey. Today, we are happy to introduce CE.SDK v1.20!&lt;/p&gt;
&lt;p&gt;With this release, you can:&lt;/p&gt;
&lt;h3 id=&quot;create-unique-element-combinations&quot;&gt;Create Unique Element Combinations&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Platforms:&lt;/strong&gt; iOS, Android, Web, API&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-20/boolean-1-20.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Forge &lt;strong&gt;limitless combinations&lt;/strong&gt; of photos, videos, texts, and shapes with our new boolean operations feature. This dynamic suite includes operations such as Combine, Subtract, Intersect, and Exclude.&lt;/p&gt;
&lt;p&gt;Subtract text from a shape, or seamlessly merge a distinctive shape with a lively &lt;strong&gt;video clip.&lt;/strong&gt; With these powerful tools, your creativity knows no bounds, offering the freedom to craft truly unique design elements.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-20/boolean-1-20_2.mp4&quot; controls autoplay muted playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h3 id=&quot;refine-images-with-ios-appearance-settings&quot;&gt;Refine Images with iOS Appearance Settings&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-20/720/m_ios-design-editor-sdk.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Breathe life into your content and set the ambiance! We’ve recently updated the user interface on iOS to include Appearance Settings. Now, users can enhance their visual content with advanced improvements. This includes &lt;strong&gt;precise adjustments&lt;/strong&gt;, creative &lt;strong&gt;filters&lt;/strong&gt;, effects, and blur options.&lt;br&gt;
Experience our new features by using the free Demo Design Editor, now live on the &lt;a href=&quot;https://apps.apple.com/us/app/img-ly-design-editor/id1672991141&quot;&gt;App Store&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;optimize-high-quality-asset-imports&quot;&gt;Optimize High-Quality Asset Imports&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;image-source-sets.jpg&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/image-source-sets_2pbluf.webp&quot; srcset=&quot;/_astro/image-source-sets_GfGVV.webp 640w, /_astro/image-source-sets_hvpDV.webp 750w, /_astro/image-source-sets_LVopW.webp 828w, /_astro/image-source-sets_2yxwN.webp 1080w, /_astro/image-source-sets_Z1jdBSr.webp 1280w, /_astro/image-source-sets_2pbluf.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Importing high-quality assets, like photos, can strain your device and impact your editor performance. Imagine editing on your phone and just importing a 5k pixels stock photo. Our smart engine steps in, creating an image source set with different resolutions: The intelligent engine selects the &lt;strong&gt;optimal target resolution&lt;/strong&gt; for your asset and seamlessly switches to it.&lt;/p&gt;
&lt;p&gt;The technical scoop? This update &lt;strong&gt;minimizes network bandwidth&lt;/strong&gt;, &lt;strong&gt;loading times&lt;/strong&gt;, and runtime &lt;strong&gt;memory usage&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;keep-designs-in-focus-on-android&quot;&gt;Keep Designs in Focus on Android&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-20/720/m_android-zoom-limits.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;With our new Camera Zoom Limits, editing a design is simpler. Your page remains in focus to prevent accidental scrolling outside the design. The zoom level is set within a minimum and maximum range for a sensible, usable view at all times. Lastly, when you select an element in your design, it will remain in focus when opening adjustments or any other inspectors. This allows you to instantly view changes made to the selected element.&lt;br&gt;
Try our live Demo Design Editor for free, on &lt;a href=&quot;https://play.google.com/store/apps/details?id=ly.img.cesdk.catalog&quot;&gt;Google Play&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;exclusive-access-integrate-short-form-video-creation&quot;&gt;Exclusive Access: Integrate Short-Form Video Creation&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;integrate-video-editor-SDK.jpg&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/integrate-video-editor-SDK_Z8qrxx.webp&quot; srcset=&quot;/_astro/integrate-video-editor-SDK_ZfoU81.webp 640w, /_astro/integrate-video-editor-SDK_1ST2CK.webp 750w, /_astro/integrate-video-editor-SDK_fFbPA.webp 828w, /_astro/integrate-video-editor-SDK_Z13XbRO.webp 1080w, /_astro/integrate-video-editor-SDK_1VofbM.webp 1280w, /_astro/integrate-video-editor-SDK_Z8qrxx.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Video continues to be the most effective method for attracting and retaining your audience. However, creating a video editor from scratch can be challenging. We’re excited to announce the upcoming release: your &lt;strong&gt;out-of-the-box&lt;/strong&gt; solution for &lt;strong&gt;video content creation&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://2498814.fs1.hubspotusercontent-na1.net/hubfs/2498814/Newsletter-Digest/nov-2023/IMGLY_VIDEO_SDK.pdf?utm_campaign=Video%20Content%20Creation&amp;#x26;utm_medium=releasenotes&amp;#x26;_hsmi=2&amp;#x26;utm_content=2&amp;#x26;utm_source=blog&quot;&gt;Download Feature Exposé (PDF)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Give your users the ability to arrange video, audio, text, and graphics seamlessly on a sleek video timeline with our new &lt;strong&gt;Camera&lt;/strong&gt;. This includes popular features like &lt;strong&gt;Voiceover&lt;/strong&gt;, &lt;strong&gt;Zoom&lt;/strong&gt;, &lt;strong&gt;Tap to Record&lt;/strong&gt;, and more.&lt;/p&gt;
&lt;p&gt;Additionally, we include the beloved &lt;strong&gt;Split Screen Modes&lt;/strong&gt; for Reactions and Duets, similar to those on &lt;strong&gt;TikTok&lt;/strong&gt; and &lt;strong&gt;Instagram&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://share.hsforms.com/1mrIXiBbURn6sMqYgZG9c6A1hk3i&quot;&gt;Gain exclusive early access&lt;/a&gt; to our Video Content Creation Release.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! Join over 3000 app specialists, and &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;subscribe&lt;/a&gt; to our newsletter. We keep you in the loop with brand-new features and updates.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2024/01/creative-editor-sdk-img_ly-v120-2.jpg" medium="image"/><category>Release Notes</category><category>Design Editor</category><category>iOS App Development</category><category>Android</category><category>Photo Editing</category></item><item><title>CE.SDK v1.17 Release Notes</title><link>https://img.ly/blog/creative-editor-sdk-v_1_17_0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v_1_17_0-release-notes/</guid><description>Discover more about our new WebP Support, Seamless Android Video Capture, and Editing!</description><pubDate>Tue, 17 Oct 2023 12:48:42 GMT</pubDate><content:encoded>&lt;p&gt;In the ever-evolving landscape of app development, staying ahead of the curve is crucial. Since our &lt;a href=&quot;https://img.ly/blog/creative-editor-sdk-v_1_16_0-release-notes/&quot;&gt;last release&lt;/a&gt;, we’ve been crafting new features to empower your users’ creative journey. Today, we’re thrilled to introduce CE.SDK v1.17. With this release, you can:&lt;/p&gt;
&lt;h2 id=&quot;support-webp-file-formats&quot;&gt;Support WebP File Formats&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;webp.jpg&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/webp_ZihMfY.webp&quot; srcset=&quot;/_astro/webp_WMgHq.webp 640w, /_astro/webp_Zktgcw.webp 750w, /_astro/webp_ZaozQq.webp 828w, /_astro/webp_lWaNa.webp 1080w, /_astro/webp_1OGgA.webp 1280w, /_astro/webp_ZihMfY.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Interfaces: Engine&lt;br&gt;
Platforms: All&lt;/p&gt;
&lt;p&gt;We’re delighted to announce that our editor now supports WebP images, delivering you an array of benefits. WebP is gaining popularity for its unique ability to strike the perfect balance between high image quality and smaller file sizes. This means your visuals will look stunning while optimizing website performance.&lt;/p&gt;
&lt;h2 id=&quot;capture-and-export-videos-with-camera-for-android&quot;&gt;&lt;strong&gt;Capture and Export Videos with Camera for Android&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;android-camera-clips.jpg&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/android-camera-1_1QwCnP.webp&quot; srcset=&quot;/_astro/android-camera-1_SXytg.webp 640w, /_astro/android-camera-1_Z1uUni0.webp 750w, /_astro/android-camera-1_1eF16r.webp 828w, /_astro/android-camera-1_Z2hfyrm.webp 1080w, /_astro/android-camera-1_2keARH.webp 1280w, /_astro/android-camera-1_1QwCnP.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Interfaces: Engine&lt;br&gt;
Platforms: Android&lt;/p&gt;
&lt;p&gt;Video content creation has become the lifeblood of today’s app ecosystem. Social entertainment, user-generated content, and the demand for immersive experiences have transformed the way we engage with audiences.&lt;/p&gt;
&lt;p&gt;We’re excited to introduce &lt;strong&gt;Camera Recording for Android&lt;/strong&gt;, seamlessly integrating your live camera preview with our engine’s extensive capabilities. This means you can apply a wide range of effects, strokes, and drop shadows to your live camera feed, all while ensuring it harmoniously blends into the composition of your scene.&lt;/p&gt;
&lt;p&gt;This feature allows you to seamlessly capture, edit, and export high-quality videos directly within your applications. Whether you’re building a social platform, a video editing app, or a storytelling tool, this feature will redefine your users’ video creation experience. Learn how to use your camera in our Engine for Android in our &lt;a href=&quot;https://img.ly/docs/cesdk/android/import-media/capture-from-camera/record-video-47819b/&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Stay tuned for more exciting updates and the &lt;strong&gt;full reveal&lt;/strong&gt;: CE.SDK v1.17 is just the beginning of a new era in app-driven video content.&lt;/p&gt;
&lt;p&gt;Thank you for being a part of this journey with us. Subscribe to our &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;newsletter&lt;/a&gt; to ensure you never miss out on updates! &lt;strong&gt;?&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2023/10/white-label-editor-1_17.jpg" medium="image"/><category>Release Notes</category><category>Android</category><category>App Development</category></item><item><title>Elegant Event Handling in Kotlin - A Refactoring Walkthrough</title><link>https://img.ly/blog/kotlin-code-refactoring-for-peak-performance/</link><guid isPermaLink="true">https://img.ly/blog/kotlin-code-refactoring-for-peak-performance/</guid><description>Unlock peak performance in Kotlin code with these expert refactoring tips.</description><pubDate>Fri, 29 Sep 2023 07:00:53 GMT</pubDate><content:encoded>&lt;p&gt;In the world of software development, code refactoring is the hero that rescues us from tangled and inefficient code. In this article, we’ll embark on an adventure to revamp Kotlin code handling diverse events. Our mission? To enhance performance and style, making the code sleeker, more maintainable, and a joy to work with.&lt;/p&gt;
&lt;h2 id=&quot;what-we-aim-to-achieve&quot;&gt;What We Aim to Achieve&lt;/h2&gt;
&lt;p&gt;On this journey to transform Kotlin event handling, our goal is to refine our code to be more efficient, readable, and maintainable. We’re introducing a variety of improvements, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Replacing a convoluted &lt;code&gt;when&lt;/code&gt; statement with a &lt;code&gt;HashMap&lt;/code&gt; for lightning-fast (O(1)) performance.&lt;/li&gt;
&lt;li&gt;Infusing syntactic sweetness with inline functions and &lt;a href=&quot;https://kotlinlang.org/docs/inline-functions.html#reified-type-parameters&quot;&gt;reified&lt;/a&gt; type parameters.&lt;/li&gt;
&lt;li&gt;Employing delegated properties for cleaner dependency injection.&lt;/li&gt;
&lt;li&gt;Adhering to the Single Responsibility Principle by enabling multiple specialized event handler functions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;step-1-the-starting-line&quot;&gt;Step 1: The Starting Line&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;sven2244_a_playful_abstract_artwork_that_represents_the_complex_6e9491ff-8203-4ae2-9a5e-52025e4010be.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1856px) 1856px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1856&quot; height=&quot;656&quot; src=&quot;https://img.ly/_astro/2_1H0FvJ.webp&quot; srcset=&quot;/_astro/2_Z2e3gQ7.webp 640w, /_astro/2_2dHdPv.webp 750w, /_astro/2_ZB5tWb.webp 828w, /_astro/2_Z1Ro7yX.webp 1080w, /_astro/2_Qgvw5.webp 1280w, /_astro/2_NDMYj.webp 1668w, /_astro/2_1H0FvJ.webp 1856w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Our adventure begins with a glance at the original code. This codebase manages a variety of block events through a function named &lt;code&gt;handleBlockEvent&lt;/code&gt; and an event handler function called &lt;code&gt;onEvent&lt;/code&gt;. Let’s unveil the original code:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onEvent&lt;/span&gt;&lt;span&gt;(event: &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    handleBlockEvent&lt;/span&gt;&lt;span&gt;(engine, &lt;/span&gt;&lt;span&gt;getBlockForEvents&lt;/span&gt;&lt;span&gt;(), &lt;/span&gt;&lt;span&gt;checkNotNull&lt;/span&gt;&lt;span&gt;(assetsRepo.fontFamilies.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;getOrThrow&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fun&lt;/span&gt;&lt;span&gt; handleBlockEvent&lt;/span&gt;&lt;span&gt;(engine: &lt;/span&gt;&lt;span&gt;Engine&lt;/span&gt;&lt;span&gt;, block: &lt;/span&gt;&lt;span&gt;DesignBlock&lt;/span&gt;&lt;span&gt;, fontFamilyMap: &lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;FontFamilyData&lt;/span&gt;&lt;span&gt;&gt;, event: &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    when&lt;/span&gt;&lt;span&gt; (event) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnDelete &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; engine.&lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnBackward &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; engine.&lt;/span&gt;&lt;span&gt;sendBackward&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnDuplicate &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; engine.&lt;/span&gt;&lt;span&gt;duplicate&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnForward &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; engine.&lt;/span&gt;&lt;span&gt;bringForward&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.ToBack &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; engine.&lt;/span&gt;&lt;span&gt;sendToBack&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.ToFront &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; engine.&lt;/span&gt;&lt;span&gt;bringToFront&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnChangeFinish &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; engine.editor.&lt;/span&gt;&lt;span&gt;addUndoStep&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        is&lt;/span&gt;&lt;span&gt; BlockEvent.OnChangeBlendMode &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; onChangeBlendMode&lt;/span&gt;&lt;span&gt;(engine, block, event.blendMode)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        is&lt;/span&gt;&lt;span&gt; BlockEvent.OnChangeOpacity &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; engine.block.&lt;/span&gt;&lt;span&gt;setOpacity&lt;/span&gt;&lt;span&gt;(block, event.opacity)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        is&lt;/span&gt;&lt;span&gt; BlockEvent.OnChangeFillColor &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; onChangeFillColor&lt;/span&gt;&lt;span&gt;(engine, block, event.color)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // and so on...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;sealed&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; BlockEvent&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    object&lt;/span&gt;&lt;span&gt; OnChangeFinish&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    object&lt;/span&gt;&lt;span&gt; OnForward&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    object&lt;/span&gt;&lt;span&gt; OnBackward&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    object&lt;/span&gt;&lt;span&gt; OnDuplicate&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    object&lt;/span&gt;&lt;span&gt; OnDelete&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    object&lt;/span&gt;&lt;span&gt; ToFront&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    object&lt;/span&gt;&lt;span&gt; ToBack&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    data&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; OnChangeBlendMode&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; blendMode: &lt;/span&gt;&lt;span&gt;BlendMode&lt;/span&gt;&lt;span&gt;) : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    data&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; OnChangeOpacity&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; opacity: &lt;/span&gt;&lt;span&gt;Float&lt;/span&gt;&lt;span&gt;) : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    data&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; OnChangeFillColor&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; color: &lt;/span&gt;&lt;span&gt;Color&lt;/span&gt;&lt;span&gt;) : &lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // and so on...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To use the original code, you’d typically call the &lt;code&gt;onEvent&lt;/code&gt; function with a specific event:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;onEvent&lt;/span&gt;&lt;span&gt;(BlockEvent.&lt;/span&gt;&lt;span&gt;OnChangeFillColor&lt;/span&gt;&lt;span&gt;(Color.RED))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would then trigger the &lt;code&gt;handleBlockEvent&lt;/code&gt; function to deal with the event at hand. Now, let’s embark on our first refactoring adventure.&lt;/p&gt;
&lt;h2 id=&quot;step-2-unveiling-hashmaps-and-payloads-for-peak-performance&quot;&gt;Step 2: Unveiling HashMaps and Payloads for Peak Performance&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;sven2244_an_abstract_image_ofa_futuristic_cityscape_with_neon-l_fbda2c5b-b80f-430b-9f00-609ceecc1292.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1856px) 1856px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1856&quot; height=&quot;656&quot; src=&quot;https://img.ly/_astro/3_ZK0M1r.webp&quot; srcset=&quot;/_astro/3_Z2iStzt.webp 640w, /_astro/3_28R179.webp 750w, /_astro/3_ZFUGFx.webp 828w, /_astro/3_JLxGM.webp 1080w, /_astro/3_Z1AJW16.webp 1280w, /_astro/3_Z1DmExR.webp 1668w, /_astro/3_ZK0M1r.webp 1856w&quot;&gt;&lt;/p&gt;
&lt;p&gt;In our first act of refactoring, we introduce a trusty &lt;code&gt;HashMap&lt;/code&gt; to map each event type to its corresponding action. This heroic move eliminates the need for the convoluted &lt;code&gt;when&lt;/code&gt; statement, making our code more efficient. We also unveil a payload mechanism to convey essential data to the event handlers.&lt;/p&gt;
&lt;p&gt;Behold the refactored code:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;abstract&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; fillPayload: (&lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;) -&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    abstract&lt;/span&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; payloadCache: &lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    private&lt;/span&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; eventMap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; mutableMapOf&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;KClass&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;out Event&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;, Payloads.(event: &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Unit&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    fun&lt;/span&gt;&lt;span&gt; handleEvent&lt;/span&gt;&lt;span&gt;(event: &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        eventMap[event::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;]?.&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            it.&lt;/span&gt;&lt;span&gt;invoke&lt;/span&gt;&lt;span&gt;(payloadCache.&lt;/span&gt;&lt;span&gt;also&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;fillPayload&lt;/span&gt;&lt;span&gt;(it) }, event)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    operator&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; &amp;#x3C;&lt;/span&gt;&lt;span&gt;EventType&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;(event: &lt;/span&gt;&lt;span&gt;KClass&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;out&lt;/span&gt;&lt;span&gt; EventType&lt;/span&gt;&lt;span&gt;&gt;, lambda: &lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;.(event: &lt;/span&gt;&lt;span&gt;EventType&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Unit) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        eventMap[event] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; lambda &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; Payloads.(event: &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Unit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; BlockEventsHandler&lt;/span&gt;&lt;span&gt;(fillPayload: (&lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;BlockEventsHandler&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;) -&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt;) : &lt;/span&gt;&lt;span&gt;EventsHandler&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEventsHandler&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;&gt;(&lt;/span&gt;&lt;span&gt;fillPayload&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    class&lt;/span&gt;&lt;span&gt; Payloads&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        lateinit&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt; engine: &lt;/span&gt;&lt;span&gt;Engine&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        lateinit&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt; block: &lt;/span&gt;&lt;span&gt;DesignBlock&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        lateinit&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt; fontFamilyMap: &lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;FontFamilyData&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    override&lt;/span&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; payloadCache: &lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; Payloads&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    init&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.OnDelete::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { engine.&lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.OnBackward::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { engine.&lt;/span&gt;&lt;span&gt;sendBackward&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.OnDuplicate::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { engine.&lt;/span&gt;&lt;span&gt;duplicate&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.OnForward::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { engine.&lt;/span&gt;&lt;span&gt;bringForward&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.ToBack::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { engine.&lt;/span&gt;&lt;span&gt;sendToBack&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.ToFront::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { engine.&lt;/span&gt;&lt;span&gt;bringToFront&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.OnChangeFinish::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { engine.editor.&lt;/span&gt;&lt;span&gt;addUndoStep&lt;/span&gt;&lt;span&gt;() }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.OnChangeBlendMode::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;onChangeBlendMode&lt;/span&gt;&lt;span&gt;(engine, block, it.blendMode) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.OnChangeOpacity::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { engine.block.&lt;/span&gt;&lt;span&gt;setOpacity&lt;/span&gt;&lt;span&gt;(block, it.opacity) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        it[BlockEvent.OnChangeFillColor::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;onChangeFillColor&lt;/span&gt;&lt;span&gt;(engine, block, it.color) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // and so on...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; blockEventHandler &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; BlockEventsHandler&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    it.engine &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; engine&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    it.block &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; getBlockForEvents&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    it.fontFamilyMap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; checkNotNull&lt;/span&gt;&lt;span&gt;(assetsRepo.fontFamilies.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;getOrThrow&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onEvent&lt;/span&gt;&lt;span&gt;(event: &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    blockEventHandler.&lt;/span&gt;&lt;span&gt;handleEvent&lt;/span&gt;&lt;span&gt;(event)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;a-performance-boost&quot;&gt;A Performance Boost&lt;/h3&gt;
&lt;p&gt;By harnessing the power of a &lt;code&gt;HashMap&lt;/code&gt;, we’ve turbocharged our event handling. The time complexity for handling an event is now a lightning-fast (O(1)), a monumental improvement over the (O(n)) time complexity of the ponderous &lt;code&gt;when&lt;/code&gt; statement. While our payload mechanism adds a dollop of syntactic sugar. It enables us to bundle all the necessary data into a single object, making our code more legible and maintainable.&lt;/p&gt;
&lt;p&gt;? Note: Using a HashMap instead of a large when() statement provides a significant performance improvement. It can be up to 40 to 150 times faster. However, explaining the details would exceed the scope of this blog post. Therefore, I will cover it, along with other Kotlin performance puzzles, in a future blog post.&lt;/p&gt;
&lt;p&gt;While the refactored code remains as simple as before:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;onEvent&lt;/span&gt;&lt;span&gt;(BlockEvent.&lt;/span&gt;&lt;span&gt;OnChangeFillColor&lt;/span&gt;&lt;span&gt;(Color.RED))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This still triggers the &lt;code&gt;handleEvent&lt;/code&gt; method in &lt;code&gt;BlockEventsHandler&lt;/code&gt;, which in turn performs the appropriate action based on the event type. The &lt;code&gt;BlockEvent&lt;/code&gt; itself is a data object containing all event details, and it serves as the lambda parameter.&lt;/p&gt;
&lt;h3 id=&quot;a-note-on-payloads&quot;&gt;A Note on Payloads&lt;/h3&gt;
&lt;p&gt;The payload creation is a dynamic lambda function that’s executed each time an event is handled. This ensures that all variables not part of the event are consistently up-to-date. Given that we’re dealing with a single thread per event handler, caching the payload is entirely secure.&lt;/p&gt;
&lt;h2 id=&quot;step-3-adding-syntactic-sweetness-with-infix-functions&quot;&gt;Step 3: Adding Syntactic Sweetness with Infix Functions&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;sven2244_an_abstract_composition_resembling_a_candy_store_fille_894e8c68-0a37-4ad0-adc8-c313db1c29da.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1856px) 1856px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1856&quot; height=&quot;656&quot; src=&quot;https://img.ly/_astro/4_1R9Sfj.webp&quot; srcset=&quot;/_astro/4_Z2nIGiP.webp 640w, /_astro/4_241NnM.webp 750w, /_astro/4_ZKKToT.webp 828w, /_astro/4_Z1HeTPo.webp 1080w, /_astro/4_11pIfE.webp 1280w, /_astro/4_XN0HS.webp 1668w, /_astro/4_1R9Sfj.webp 1856w&quot;&gt;&lt;/p&gt;
&lt;p&gt;In our next act, we elevate our syntax to a new level of expressiveness and readability. We introduce an infix function called &lt;code&gt;to&lt;/code&gt;, allowing us to map an event class to its corresponding action elegantly.&lt;/p&gt;
&lt;p&gt;Witness the updated code:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;abstract&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; fillPayload: (&lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;) -&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    infix&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; &amp;#x3C;&lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;EventType&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;KClass&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;out EventType&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;(lambda: &lt;/span&gt;&lt;span&gt;Payloads&lt;/span&gt;&lt;span&gt;.(event: &lt;/span&gt;&lt;span&gt;EventType&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Unit) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        eventMap[event] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; lambda &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; Payloads.(event: &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Unit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // ... (rest of the code remains the same)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; BlockEventsHandler&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    manager: &lt;/span&gt;&lt;span&gt;EventsManager&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    override&lt;/span&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; fillPayload: (&lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;TextBlockEventsHandler&lt;/span&gt;&lt;span&gt;) -&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;) : &lt;/span&gt;&lt;span&gt;EventsHandler&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;TextBlockEventsHandler&lt;/span&gt;&lt;span&gt;&gt;(&lt;/span&gt;&lt;span&gt;manager&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    lateinit&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt; engine: &lt;/span&gt;&lt;span&gt;Engine&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    lateinit&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt; block: &lt;/span&gt;&lt;span&gt;DesignBlock&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    lateinit&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt; fontFamilyMap: &lt;/span&gt;&lt;span&gt;Map&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;FontFamilyData&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    init&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnDelete::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.&lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnBackward::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.&lt;/span&gt;&lt;span&gt;sendBackward&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnDuplicate::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.&lt;/span&gt;&lt;span&gt;duplicate&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnForward::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.&lt;/span&gt;&lt;span&gt;bringForward&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.ToBack::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.&lt;/span&gt;&lt;span&gt;sendToBack&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.ToFront::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.&lt;/span&gt;&lt;span&gt;bringToFront&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnChangeFinish::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.editor.&lt;/span&gt;&lt;span&gt;addUndoStep&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnChangeBlendMode::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            onChangeBlendMode&lt;/span&gt;&lt;span&gt;(engine, block, it.blendMode)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnChangeOpacity::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.block.&lt;/span&gt;&lt;span&gt;setOpacity&lt;/span&gt;&lt;span&gt;(block, it.opacity)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BlockEvent.OnChangeFillColor::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; to&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            onChangeFillColor&lt;/span&gt;&lt;span&gt;(engine, block, it.color)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;syntactic-sweetness-and-performance&quot;&gt;Syntactic Sweetness and Performance&lt;/h3&gt;
&lt;p&gt;The introduction of the &lt;code&gt;to&lt;/code&gt; infix function adds a sprinkle of syntactic sweetness that enhances code expressiveness and enables a more natural usage. This makes it crystal clear what each event is all about. And fear not, the performance remains at a blazing-fast (O(1)), thanks to our trusty HashMap.&lt;/p&gt;
&lt;h3 id=&quot;flexibility-in-syntax&quot;&gt;Flexibility in Syntax&lt;/h3&gt;
&lt;p&gt;While the &lt;code&gt;to&lt;/code&gt; keyword is used here, feel free to substitute it with other terms like &lt;code&gt;handle&lt;/code&gt;, &lt;code&gt;trigger&lt;/code&gt;, or anything that best suits your context. Flexibility is the name of the game.&lt;/p&gt;
&lt;h2 id=&quot;step-4-embracing-inline-functions-for-elegance&quot;&gt;Step 4: Embracing Inline Functions for Elegance&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;sven2244_a_mesmerizing_image_of_a_kaleidoscope_with_vibrant_pat_b53a7e30-820a-4308-a7aa-11222be8a7a1.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1856px) 1856px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1856&quot; height=&quot;656&quot; src=&quot;https://img.ly/_astro/5_ZzQzhR.webp&quot; srcset=&quot;/_astro/5_Z2syT2c.webp 640w, /_astro/5_1YbAEq.webp 750w, /_astro/5_ZPB78g.webp 828w, /_astro/5_TUKqm.webp 1080w, /_astro/5_Z1qAJhw.webp 1280w, /_astro/5_Z1tdrOi.webp 1668w, /_astro/5_ZzQzhR.webp 1856w&quot;&gt;&lt;/p&gt;
&lt;p&gt;However, this is still not perfect because the &lt;code&gt;::class&lt;/code&gt; breaks smooth reading.&lt;/p&gt;
&lt;p&gt;So let’s do it differently. Let us try to introduce a more elegant way to register an event. Let us eliminate the need to specify &lt;code&gt;::class&lt;/code&gt; every time we register an event handler will make our code more concise and readable.&lt;/p&gt;
&lt;p&gt;This is made possible by an inline function with a verified type parameter that maintains the class reference at runtime.&lt;/p&gt;
&lt;p&gt;To do this, we extend the &lt;code&gt;EventsHandler&lt;/code&gt; class with this new &lt;code&gt;register&lt;/code&gt; function:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    register: &lt;/span&gt;&lt;span&gt;EventsHandler&lt;/span&gt;&lt;span&gt;.() &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Unit,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    inline&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; &amp;#x3C;&lt;/span&gt;&lt;span&gt;reified&lt;/span&gt;&lt;span&gt; EventType&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BaseEvent&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;register&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;noinline&lt;/span&gt;&lt;span&gt; lambda: (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;EventType&lt;/span&gt;&lt;span&gt;) -&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt;) : &lt;/span&gt;&lt;span&gt;Any&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        this&lt;/span&gt;&lt;span&gt;[EventType::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; lambda&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; lambda&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   // ... (rest of the code remains the same)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-new-syntax&quot;&gt;The New Syntax&lt;/h3&gt;
&lt;p&gt;This is what registering an event handler looks like with the new syntax:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OnChangeLineWidth&lt;/span&gt;&lt;span&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    engine.block.&lt;/span&gt;&lt;span&gt;setWidth&lt;/span&gt;&lt;span&gt;(block, engine.block.&lt;/span&gt;&lt;span&gt;getFrameWidth&lt;/span&gt;&lt;span&gt;(block))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    engine.block.&lt;/span&gt;&lt;span&gt;setHeight&lt;/span&gt;&lt;span&gt;(block, it.width)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Much better, right? The new syntax is more concise, eliminates redundancy, and is type-safe because the reified type parameters ensure that the event type is known at compile-time and runtime, eliminating the need for unsafe casting.&lt;/p&gt;
&lt;h2 id=&quot;step-5-elevating-register-to-an-extension-function-for-highlighting&quot;&gt;Step 5: Elevating &lt;code&gt;register&lt;/code&gt; to an Extension Function for Highlighting&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;highlighting.jpg&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2000px) 2000px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2000&quot; height=&quot;700&quot; src=&quot;https://img.ly/_astro/6-1_ZpUYI8.webp&quot; srcset=&quot;/_astro/6-1_1WCnM8.webp 640w, /_astro/6-1_Z2lJRV7.webp 750w, /_astro/6-1_1HMAQd.webp 828w, /_astro/6-1_ZlU4Yo.webp 1080w, /_astro/6-1_Z2ruRRK.webp 1280w, /_astro/6-1_Z29YNlp.webp 1668w, /_astro/6-1_ZpUYI8.webp 2000w&quot;&gt;&lt;/p&gt;
&lt;p&gt;To improve code readability, we’ll make a subtle but effective step by converting the &lt;code&gt;register&lt;/code&gt; function from a &lt;code&gt;EventsHandler&lt;/code&gt; class function, into an &lt;code&gt;EventsHandler&lt;/code&gt; extension function.&lt;/p&gt;
&lt;p&gt;Sounds stupid! So why?&lt;/p&gt;
&lt;p&gt;This small change improves code readability by highlighting the &lt;code&gt;register&lt;/code&gt; keyword through syntax highlighting from a Kotlin extension function. This will make it much more colorful, which improves readability.&lt;/p&gt;
&lt;h3 id=&quot;updated-eventshandler-class&quot;&gt;Updated &lt;code&gt;EventsHandler&lt;/code&gt; Class&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;EventsHandler&lt;/code&gt; class remains largely unchanged, but the &lt;code&gt;register&lt;/code&gt; function is now outside the class and transformed into an extension function for the &lt;code&gt;EventsHandler&lt;/code&gt; class:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    register: &lt;/span&gt;&lt;span&gt;EventsHandler&lt;/span&gt;&lt;span&gt;.() &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Unit,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // ... (rest of the code remains the same)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;inline&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; &amp;#x3C;&lt;/span&gt;&lt;span&gt;reified&lt;/span&gt;&lt;span&gt; EventType&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BaseEvent&lt;/span&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;span&gt;EventsHandler&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;register&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;noinline&lt;/span&gt;&lt;span&gt; lambda: (&lt;/span&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;EventType&lt;/span&gt;&lt;span&gt;) -&gt; &lt;/span&gt;&lt;span&gt;Unit&lt;/span&gt;&lt;span&gt;) : &lt;/span&gt;&lt;span&gt;Any&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    this&lt;/span&gt;&lt;span&gt;[EventType::&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; lambda&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; lambda&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By simply shifting &lt;code&gt;register&lt;/code&gt; out of the class, the &lt;code&gt;EventsHandler&lt;/code&gt; class definition now stands out with distinctive syntax highlighting. It’s a clever trick that doesn’t impact runtime or compile performance, since it’s an inline operation anyway.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;**&lt;/span&gt;&lt;span&gt;register&lt;/span&gt;&lt;span&gt;**&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent.OnChangeLineWidth&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    engine.block.&lt;/span&gt;&lt;span&gt;setWidth&lt;/span&gt;&lt;span&gt;(block, engine.block.&lt;/span&gt;&lt;span&gt;getFrameWidth&lt;/span&gt;&lt;span&gt;(block))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    engine.block.&lt;/span&gt;&lt;span&gt;setHeight&lt;/span&gt;&lt;span&gt;(block, it.width)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;step-6-eliminating-lateinit-variables-with-delegated-properties&quot;&gt;Step 6: Eliminating &lt;code&gt;lateinit&lt;/code&gt; Variables with Delegated Properties&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;sven2244_an_abstract_image_of_interconnected_gears_and_cogs_for_3b651ffc-9367-470d-8be3-82c3c19b9ffe.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1856px) 1856px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1856&quot; height=&quot;656&quot; src=&quot;https://img.ly/_astro/7_ZpHmyi.webp&quot; srcset=&quot;/_astro/7_2rVOk1.webp 640w, /_astro/7_1OvbcH.webp 750w, /_astro/7_Z10hwzY.webp 828w, /_astro/7_154X9V.webp 1080w, /_astro/7_Z1grwxW.webp 1280w, /_astro/7_Z1j4f5I.webp 1668w, /_astro/7_ZpHmyi.webp 1856w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now, it’s time to address the enigmatic &lt;code&gt;lateinit&lt;/code&gt; variables and the somewhat convoluted &lt;code&gt;fillPayload&lt;/code&gt; mechanism. Let us introduce a cleaner approach, using delegated properties and lambda functions to inject dependencies.&lt;/p&gt;
&lt;p&gt;Let’s add an &lt;code&gt;Inject&lt;/code&gt; class to wrap a normal lambda as delegable:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Type&lt;/span&gt;&lt;span&gt;&gt;(&lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; inject: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Type) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    operator&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; getValue&lt;/span&gt;&lt;span&gt;(thisRef: &lt;/span&gt;&lt;span&gt;Any&lt;/span&gt;&lt;span&gt;?, property: &lt;/span&gt;&lt;span&gt;KProperty&lt;/span&gt;&lt;span&gt;&amp;#x3C;*&gt;): &lt;/span&gt;&lt;span&gt;Type&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; inject&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this newfound power, our event handler code becomes cleaner and more intuitive. It takes on the style of Jetpack Compose’s declarative syntax:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fun&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;textBlockEvents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    engine: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Engine,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    block: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; DesignBlock,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    fontFamilyMap: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Map&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;String, FontFamilyData&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // Inject the dependencies&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; engine &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(engine)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; block &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; fontFamilyMap &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(fontFamilyMap)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // Event handling logic here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Whenever one of the variables is accessed, the lambda is called, and you always get the current variable.&lt;/p&gt;
&lt;p&gt;Also, the creation of the “payload” becomes more straightforward, clean, and type-safe. It kinda looks like passing a variable:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; eventHandler &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    textBlockEvents&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        engine &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ::&lt;/span&gt;&lt;span&gt;engine&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        block &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ::&lt;/span&gt;&lt;span&gt;getBlockForEvents&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        fontFamilyMap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;checkNotNull&lt;/span&gt;&lt;span&gt;(assetsRepo.fontFamilies.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;getOrThrow&lt;/span&gt;&lt;span&gt;() },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looks and feels like magic! Pretty cool, right?&lt;/p&gt;
&lt;h2 id=&quot;step-7-multiple-event-handlers-for-single-responsibility-principle&quot;&gt;Step 7: Multiple Event Handlers for Single Responsibility Principle&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;sven2244_code_as_a_magical_candy-filled_wonderland._Syntax_Suga_a50bcd92-bea2-4fea-aa92-e1cd557277b6.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1856px) 1856px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1856&quot; height=&quot;656&quot; src=&quot;https://img.ly/_astro/8_2csiHs.webp&quot; srcset=&quot;/_astro/8_2n6BAE.webp 640w, /_astro/8_1JEXtl.webp 750w, /_astro/8_Z157Jjl.webp 828w, /_astro/8_Z1mVunf.webp 1080w, /_astro/8_1lI8HN.webp 1280w, /_astro/8_1j6qb2.webp 1668w, /_astro/8_2csiHs.webp 1856w&quot;&gt;&lt;/p&gt;
&lt;p&gt;In our grand finale, we harness the newfound flexibility from our previous changes to register multiple event handler functions. Each event handler registration function now has a specific topic, aligning perfectly with the Single Responsibility Principle (SRP).&lt;/p&gt;
&lt;h3 id=&quot;enhanced-event-handler-registration&quot;&gt;Enhanced Event Handler Registration&lt;/h3&gt;
&lt;p&gt;We can now register multiple event handler functions within the same &lt;code&gt;EventsHandler&lt;/code&gt; instance. Each function can specialize in handling a particular type of event, making the code more modular and manageable. Behold the grand design:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; eventHandler &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    cropEvents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        engine &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ::&lt;/span&gt;&lt;span&gt;engine&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        block &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ::&lt;/span&gt;&lt;span&gt;getBlockForEvents&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    blockEvents&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        engine &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ::&lt;/span&gt;&lt;span&gt;engine&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        block &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ::&lt;/span&gt;&lt;span&gt;getBlockForEvents&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    textBlockEvents&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        engine &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ::&lt;/span&gt;&lt;span&gt;engine&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        block &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ::&lt;/span&gt;&lt;span&gt;getBlockForEvents&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        fontFamilyMap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;checkNotNull&lt;/span&gt;&lt;span&gt;(assetsRepo.fontFamilies.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;getOrThrow&lt;/span&gt;&lt;span&gt;() },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fun&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;blockEvents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    engine: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Engine,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    block: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; DesignBlock&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; engine: &lt;/span&gt;&lt;span&gt;Engine&lt;/span&gt;&lt;span&gt; by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;engine&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; block: &lt;/span&gt;&lt;span&gt;DesignBlock&lt;/span&gt;&lt;span&gt; by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OnDelete&lt;/span&gt;&lt;span&gt;&gt; { engine.&lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OnBackward&lt;/span&gt;&lt;span&gt;&gt; { engine.&lt;/span&gt;&lt;span&gt;sendBackward&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OnDuplicate&lt;/span&gt;&lt;span&gt;&gt; { engine.&lt;/span&gt;&lt;span&gt;duplicate&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OnForward&lt;/span&gt;&lt;span&gt;&gt; { engine.&lt;/span&gt;&lt;span&gt;bringForward&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ToBack&lt;/span&gt;&lt;span&gt;&gt; { engine.&lt;/span&gt;&lt;span&gt;sendToBack&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ToFront&lt;/span&gt;&lt;span&gt;&gt; { engine.&lt;/span&gt;&lt;span&gt;bringToFront&lt;/span&gt;&lt;span&gt;(block) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OnChangeFinish&lt;/span&gt;&lt;span&gt;&gt; { engine.editor.&lt;/span&gt;&lt;span&gt;addUndoStep&lt;/span&gt;&lt;span&gt;() }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OnChangeBlendMode&lt;/span&gt;&lt;span&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        if&lt;/span&gt;&lt;span&gt; (engine.block.&lt;/span&gt;&lt;span&gt;getBlendMode&lt;/span&gt;&lt;span&gt;(block) &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; it.blendMode) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.block.&lt;/span&gt;&lt;span&gt;setBlendMode&lt;/span&gt;&lt;span&gt;(block, it.blendMode)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            engine.editor.&lt;/span&gt;&lt;span&gt;addUndoStep&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    register&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;BlockEvent&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;OnChangeOpacity&lt;/span&gt;&lt;span&gt;&gt; { engine.block.&lt;/span&gt;&lt;span&gt;setOpacity&lt;/span&gt;&lt;span&gt;(block, it.opacity) }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fun&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cropEvents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    engine: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Engine,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    block: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; DesignBlock&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; engine: &lt;/span&gt;&lt;span&gt;Engine&lt;/span&gt;&lt;span&gt; by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;engine&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; block: &lt;/span&gt;&lt;span&gt;DesignBlock&lt;/span&gt;&lt;span&gt; by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // ... (event handling logic for cropping events)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fun&lt;/span&gt;&lt;span&gt; EventsHandler&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;textBlockEvents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    engine: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Engine,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    block: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; DesignBlock,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    fontFamilyMap: () &lt;/span&gt;&lt;span&gt;-&gt;&lt;/span&gt;&lt;span&gt; Map&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;String, FontFamilyData&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; engine &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(engine)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; block &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(block)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    val&lt;/span&gt;&lt;span&gt; fontFamilyMap &lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt; Inject&lt;/span&gt;&lt;span&gt;(fontFamilyMap)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // ... (event handling logic for text block events)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While the triggering and its API remain unchanged, and no extra parameters need to be passed:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onEvent&lt;/span&gt;&lt;span&gt;(event: &lt;/span&gt;&lt;span&gt;Event&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    eventHandler.&lt;/span&gt;&lt;span&gt;handleEvent&lt;/span&gt;&lt;span&gt;(event)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;final-words&quot;&gt;Final Words&lt;/h2&gt;
&lt;p&gt;As we conclude our journey through Kotlin code refactoring, we’ve unlocked the secrets to enhanced performance and style. By embracing techniques such as HashMaps, infix functions, and inline functions with reified type parameters, we’ve elevated our code to new heights. The benefits are clear: improved efficiency, readability, and adherence to the Single Responsibility Principle. Armed with these tools, you’re now ready to embark on your own coding adventures, transforming messy code into elegant masterpieces.&lt;/p&gt;
&lt;p&gt;If you’d like to try it out, I’ve created a working &lt;a href=&quot;https://pl.kotl.in/tEDorvc04&quot;&gt;example code on the Kotlin Playground&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thank you for accompanying, and happy coding!&lt;/strong&gt; &lt;strong&gt;Never miss out on updates and &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;subscribe&lt;/a&gt; to our newsletter.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Sven</dc:creator><media:content url="https://blog.img.ly/2023/09/hero_01.jpg" medium="image"/><category>How-To</category><category>Android</category><category>Android App Development</category><category>Kotlin</category><category>Insights</category></item><item><title>CE.SDK v1.10 Release</title><link>https://img.ly/blog/creative-editor-sdk-v_1_10_0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v_1_10_0-release-notes/</guid><description>Elevate your app development game with Android Platform Support, and discover more updates with CE.SDK v.1.10.</description><pubDate>Tue, 14 Mar 2023 15:30:46 GMT</pubDate><content:encoded>&lt;p&gt;We are pleased to announce the latest release of CE.SDK v1.10 and excited to share the newest features and updates with you. For those not yet familiar with our engine, CreativeEditor SDK is a powerful design editor that enables customizable workflows and automations for brands, agencies, and printers, all with just a few lines of code. In this release, we have introduced several new features and updates that are set to elevate the functionality and usability of the SDK, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Android Platform Support, making native Android application development seamless&lt;/li&gt;
&lt;li&gt;Library Improvements, including Create, Update &amp;#x26; Delete for Asset Libraries and Asset Libraries Generalization&lt;/li&gt;
&lt;li&gt;Zoom Improvements, providing greater precision and accuracy when zooming to 100% and fitting specific pages&lt;/li&gt;
&lt;li&gt;Arrange API, simplifying the visual order of items and providing common arrange methods for easier use&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;android-platform-support&quot;&gt;Android Platform Support&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/Android_Platform_Support_rtfB1.webp&quot; srcset=&quot;/_astro/Android_Platform_Support_ZuFl9r.webp 640w, /_astro/Android_Platform_Support_vFF2J.webp 750w, /_astro/Android_Platform_Support_1r0E1d.webp 828w, /_astro/Android_Platform_Support_1YPco7.webp 1080w, /_astro/Android_Platform_Support_Z1jqPoT.webp 1280w, /_astro/Android_Platform_Support_rtfB1.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://img.ly/blog/creative-editor-sdk-v_1_9_0-release-notes/&quot;&gt;Our last release&lt;/a&gt; included iOS Native Support. So now we are thrilled to share that CE.SDK has expanded its capabilities to include full support for developing native Android applications. With this latest feature, you can seamlessly integrate the power and versatility of CE.SDK into your Android app development projects, enhancing the user experience and boosting customer engagement. Our team has worked tirelessly to make it incredibly easy for you to get started, with full Kotlin API support and plenty of integration examples to help you hit the ground running.&lt;/p&gt;
&lt;p&gt;In today’s ever-evolving digital landscape, cross-platform app development has become essential for businesses to stay competitive. By leveraging CE.SDK’s cross-platform capabilities, you can develop and deploy apps across multiple platforms with ease, saving time and resources. This not only increases your reach, but it also allows you to provide a consistent experience to your customers, regardless of their device of choice.&lt;/p&gt;
&lt;p&gt;Whether you’re a seasoned developer or new to the game, our &lt;a href=&quot;https://img.ly/docs/cesdk/android/get-started/overview-e18f40/&quot;&gt;Get Started Guide&lt;/a&gt; will equip you with the knowledge and tools necessary to begin your Android app development journey with CE.SDK.&lt;/p&gt;
&lt;h2 id=&quot;library-improvements&quot;&gt;Library Improvements&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;Stay organized: You can create, upload, and delete your uploaded media.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/Delete-Images-fromuploads_17oW4x.webp&quot; srcset=&quot;/_astro/Delete-Images-fromuploads_Z1AW0En.webp 640w, /_astro/Delete-Images-fromuploads_Z2rKlVh.webp 750w, /_astro/Delete-Images-fromuploads_2vHqrX.webp 828w, /_astro/Delete-Images-fromuploads_2gEBJt.webp 1080w, /_astro/Delete-Images-fromuploads_ZPyhus.webp 1280w, /_astro/Delete-Images-fromuploads_17oW4x.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;create-update-and-delete-for-asset-libraries&quot;&gt;Create, Update and Delete for Asset Libraries&lt;/h3&gt;
&lt;p&gt;To streamline your workflow and save valuable time, we’ve added a much-requested feature that allows you to delete uploaded images directly from inside the asset libraries, giving you greater control and flexibility over your content.&lt;/p&gt;
&lt;p&gt;We have updated our engine so that uploaded assets are no longer automatically tied to scenes.&lt;/p&gt;
&lt;h3 id=&quot;asset-libraries-generalization&quot;&gt;Asset Libraries Generalization&lt;/h3&gt;
&lt;p&gt;We have migrated all our internal and external asset handling to be based on asset sources, eliminating the need to maintain different paths for adding and displaying assets. All visible assets are now queried from asset sources, which can be modified by adding or removing assets from a source. Additionally, we have introduced convenient helpers to create IMG.LY’s default asset sources, which contain assets that you can use to jumpstart your integration.&lt;/p&gt;
&lt;h2 id=&quot;zoom-improvements&quot;&gt;Zoom Improvements&lt;/h2&gt;
&lt;p&gt;Zooming has always been an integral part of editing, and our latest release makes it even more versatile and precise. With our new zoom feature, users can now zoom to fit specific pages, providing greater control and accuracy when editing complex designs. Additionally, zooming to 100% will now automatically adjust the zoom level to match the accurate export pixel size. This means that the size of pages on the screen will match exactly the size of the printed product, ensuring a seamless transition from editing to the final product. With this latest enhancement, CE.SDK is committed to providing businesses, agencies, and printers with a comprehensive and intuitive design editor.&lt;/p&gt;
&lt;h2 id=&quot;arrange-api&quot;&gt;Arrange API&lt;/h2&gt;
&lt;p&gt;We are introducing enhanced functionality for controlling the visual order of items within blocks. Previously, this could only be achieved by manually rearranging the order of the children of a block. With our latest release, we’ve extended the API with new convenience methods that provide common arrange methods for easier use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! Sign up for our&lt;/strong&gt; &lt;a href=&quot;https://img.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;&lt;strong&gt;Newsletter&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;to receive our latest news straight to your inbox.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2023/03/cesdk1_1-10.jpg" medium="image"/><category>Release Notes</category><category>Android</category><category>Android App Development</category><category>Creative Editor</category></item><item><title>Time-Based Sprites for VE.SDK on iOS and Android</title><link>https://img.ly/blog/time-based-sprites-for-ve-sdk-on-ios-and-android/</link><guid isPermaLink="true">https://img.ly/blog/time-based-sprites-for-ve-sdk-on-ios-and-android/</guid><description>Time-Based Sprites allow basic keyframing by setting the duration of your Text and Stickers in Videos. </description><pubDate>Mon, 05 Sep 2022 07:06:05 GMT</pubDate><content:encoded>&lt;p&gt;We are happy to extend VideoEditor SDK with a highly-requested feature: &lt;strong&gt;Time-Based Sprites&lt;/strong&gt;. This new feature sets the &lt;strong&gt;duration of text&lt;/strong&gt; and &lt;strong&gt;stickers&lt;/strong&gt; in your video timeline. Users may now place fun stickers and text at the right moment, and give their videos a special touch.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/basic-keyframe-video-editing.MP4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h2 id=&quot;time-based-sprites&quot;&gt;Time-Based Sprites&lt;/h2&gt;
&lt;p&gt;The popular feature known from TikTok and Instagram Reels is now available in VE.SDK: set the starting and end point of your text or sticker. Tap your text or sticker and select &lt;em&gt;Duration&lt;/em&gt; to determine the duration of your asset.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/video-editor-sdk-white-label-edit.MOV&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Unless you have specified a custom set of sticker or text actions, this feature is &lt;strong&gt;enabled by default&lt;/strong&gt; since VE.SDK v10.3.0 for Android and v11.3.0 for iOS. See the official &lt;a href=&quot;https://img.ly/docs/vesdk/ios/guides/trim/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes&quot;&gt;documentation for iOS&lt;/a&gt; or &lt;a href=&quot;https://img.ly/docs/vesdk/android/guides/trim/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes#trim-sprite-duration&quot;&gt;Android&lt;/a&gt; on Time-Based Sprites.&lt;/p&gt;
&lt;h3 id=&quot;why-is-video-important&quot;&gt;Why is Video Important?&lt;/h3&gt;
&lt;p&gt;Video content has become an essential medium for social media, marketing, sales, and support teams. According to marketing &lt;a href=&quot;https://www.wyzowl.com/video-marketing-statistics/&quot;&gt;statistics&lt;/a&gt;, people are watching an average of &lt;strong&gt;19 hours&lt;/strong&gt; of online video per week in 2022. &lt;strong&gt;88%&lt;/strong&gt; of people say that they felt convinced to buy a product or service by watching a brand’s video. The growing trend and preference of consumers are why businesses are shifting their attention toward including videos in their strategies and applications.&lt;/p&gt;
&lt;p&gt;We commit to extending our video editing features to help developers meet the demand, save resources and streamline the process of building great applications. Let us have a look at our &lt;strong&gt;previous VE.SDK releases&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Video Composition&lt;/strong&gt;&lt;br&gt;
Users may seamlessly edit their footage by trimming and adjusting video files with advanced filters. Finally, they can set the correct order of their video sequences to create a single composition.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Audio Support&lt;/strong&gt;&lt;br&gt;
Replace or add sound in videos by loading audio files. Users can trim their audio according to their footage. Developers can provide media libraries for audio and video files. That way, users may access media by choosing from labeled folders, such as genre, theme, or artist names.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Force Trim&lt;/strong&gt;&lt;br&gt;
The VE.SDK trim tool allows users to determine the start and end frame of a video clip and change the duration of their footage. Now you can enforce a &lt;strong&gt;minimum and maximum length&lt;/strong&gt; of videos. Force Trim will come in handy for use cases, such as social media stories and posts popularly limited to bite size 15 or 60 seconds by widely loved apps – see TikTok or Instagram. Adopting a ready-to-use solution like &lt;a href=&quot;https://img.ly/products/video-sdk/&quot;&gt;VE.SDK&lt;/a&gt; will set you on your path to creating equally beautiful apps.&lt;/p&gt;
&lt;p&gt;Thanks for reading! Let us know what you think on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt; – or check out our &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;Newsletter&lt;/a&gt; for more accelerating updates.&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2022/08/video-editor-sdk-duration-trim-stickers-keyframes.png" medium="image"/><category>Release Notes</category><category>VE.SDK</category><category>Android</category><category>Android App Development</category><category>iOS</category><category>iOS App Development</category><category>Video Editing</category><category>Video App</category><category>Keyframe</category><category>Company</category></item><item><title>How To Crop and Trim Videos In Kotlin for Android</title><link>https://img.ly/blog/how-to-crop-and-trim-videos-in-kotlin-for-android/</link><guid isPermaLink="true">https://img.ly/blog/how-to-crop-and-trim-videos-in-kotlin-for-android/</guid><description>In this beginner-friendly tutorial you will learn how to crop and trim videos in Android with FFmpeg.</description><pubDate>Fri, 02 Sep 2022 10:18:21 GMT</pubDate><content:encoded>&lt;p&gt;Cropping and trimming videos is a notoriously difficult task to achieve on Android. One way to implement this functionality is by using FFmpeg a free open-source suite of tools that can perform a wide range of tasks, from video converting to editing. Normally FFmpeg is used from the command line, to use it correctly in Android you have to understand its underlying APIs and how to use them.&lt;/p&gt;
&lt;p&gt;In this tutorial, you will learn how to crop and trim videos in Android by using FFmpeg. Even if you are a beginner you should be able to follow the steps to achieve the desired results.&lt;/p&gt;
&lt;p&gt;I try to summarise the most important basics that you need to know to manipulate videos with FFmpeg. After reading this article you should be able to use it in your own applications. Furthermore, I have developed a sample application and library that can be used to trim and crop videos with an Android device.&lt;/p&gt;
&lt;h2 id=&quot;what-is-ffmpeg&quot;&gt;What is FFmpeg&lt;/h2&gt;
&lt;p&gt;FFmpeg is a great multimedia framework that is able to &lt;strong&gt;mux&lt;/strong&gt;, &lt;strong&gt;demux&lt;/strong&gt;, &lt;strong&gt;decode&lt;/strong&gt;, &lt;strong&gt;encode&lt;/strong&gt;, &lt;strong&gt;transcode&lt;/strong&gt;, &lt;strong&gt;filter&lt;/strong&gt;, &lt;strong&gt;stream&lt;/strong&gt;, &lt;strong&gt;and&lt;/strong&gt; &lt;strong&gt;play&lt;/strong&gt; most media content that exists. FFmpeg supports different tools that can be used to develop an application to manipulate any kind of media to the desired output. You do not have any limitations on what to do with multimedia when using FFmpeg.&lt;/p&gt;
&lt;p&gt;Unfortunately, to use FFmpeg in an Android App using Kotlin you have to compile and build the libraries to use them as a dependency in your project. This process is not straightforward because you have to manually compile a C/C++ library with help of the Android NDK.&lt;/p&gt;
&lt;p&gt;Luckily, many people already have done this and we are able to use their compiled library in our project. However, if you want to compile FFmpeg from scratch you can do this. All necessary information should be available within the &lt;a href=&quot;https://developer.android.com/studio/projects/add-native-code&quot;&gt;android developer documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To show you how we can use FFmpeg in our app I will use a compiled FFmpeg library that can be found &lt;a href=&quot;https://github.com/WritingMinds/ffmpeg-android-java&quot;&gt;here&lt;/a&gt;. To find other FFmpeg libraries you can have a look at the &lt;a href=&quot;https://trac.ffmpeg.org/wiki/CompilationGuide/Android&quot;&gt;official FFmpeg wiki&lt;/a&gt; where several pre-packaged sources are listed.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-the-project&quot;&gt;Setting up the project&lt;/h2&gt;
&lt;p&gt;In order to use the FFmpeg library of WritingMinds (or any other library), we have to follow some simple steps.&lt;/p&gt;
&lt;h3 id=&quot;1-add-dependency-for-the-ffmpeg-library-to-your-app-level-buildgradle&quot;&gt;1. Add dependency for the FFmpeg library to your app-level build.gradle&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;xml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dependencies {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    implementation fileTree(dir: &apos;libs&apos;, include: [&apos;*.jar&apos;])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    implementation &apos;com.writingminds:FFmpegAndroid:0.3.2&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;2-check-if-your-device-supports-the-current-implementation-of-ffmpeg-or-not&quot;&gt;2. Check if your device supports the current implementation of FFmpeg or not&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fun&lt;/span&gt;&lt;span&gt; initialize&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        val&lt;/span&gt;&lt;span&gt; ffmpeg &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; FFmpeg.&lt;/span&gt;&lt;span&gt;getInstance&lt;/span&gt;&lt;span&gt;(ctx.applicationContext)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            ffmpeg.&lt;/span&gt;&lt;span&gt;loadBinary&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;LoadBinaryResponseHandler&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onFinish&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onFinish&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onSuccess&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onSuccess&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onFailure&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onFailure&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onStart&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onStart&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        } &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (e: &lt;/span&gt;&lt;span&gt;FFmpegNotSupportedException&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            Log.&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;FFmpeg&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;Your device does not support FFmpeg&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;3-initialize-the-ffmpeg-module-leave-command-blank&quot;&gt;3. Initialize the FFmpeg module (leave command blank)&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; ffmpeg &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; FFmpeg.&lt;/span&gt;&lt;span&gt;getInstance&lt;/span&gt;&lt;span&gt;(ctx)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;       ffmpeg.&lt;/span&gt;&lt;span&gt;loadBinary&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;FFmpegLoadBinaryResponseHandler&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onFinish&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                Log.&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;FFmpeg&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;onFinish&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onSuccess&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                Log.&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;FFmpeg&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;onSuccess&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                val&lt;/span&gt;&lt;span&gt; command &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; //TODO: the command will added here later&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    ffmpeg.&lt;/span&gt;&lt;span&gt;execute&lt;/span&gt;&lt;span&gt;(command, &lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;ExecuteBinaryResponseHandler&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onSuccess&lt;/span&gt;&lt;span&gt;(message: &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;?) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onSuccess&lt;/span&gt;&lt;span&gt;(message)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            Log.&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;(TAG, &lt;/span&gt;&lt;span&gt;&quot;onSuccess: &quot;&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; message&lt;/span&gt;&lt;span&gt;!!&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onProgress&lt;/span&gt;&lt;span&gt;(message: &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;?) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onProgress&lt;/span&gt;&lt;span&gt;(message)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            Log.&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;(TAG, &lt;/span&gt;&lt;span&gt;&quot;onProgress: &quot;&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; message&lt;/span&gt;&lt;span&gt;!!&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onFailure&lt;/span&gt;&lt;span&gt;(message: &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt;?) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onFailure&lt;/span&gt;&lt;span&gt;(message)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            Log.&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;(TAG, &lt;/span&gt;&lt;span&gt;&quot;onFailure: &quot;&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; message&lt;/span&gt;&lt;span&gt;!!&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onStart&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onStart&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            Log.&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;(TAG, &lt;/span&gt;&lt;span&gt;&quot;onStart&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onFinish&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onFinish&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            Log.&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;(TAG, &lt;/span&gt;&lt;span&gt;&quot;onFinish&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                } &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (e: &lt;/span&gt;&lt;span&gt;FFmpegCommandAlreadyRunningException&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            Log.&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;FFmpeg&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;FFmpeg runs already&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onFailure&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                Log.&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;FFmpeg&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;onFailure&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onStart&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;4-implement-the-commands-you-want-to-use-in-your-app&quot;&gt;4. Implement the commands you want to use in your app&lt;/h3&gt;
&lt;p&gt;All commands that are supported by FFmpeg can be included in your app with help of an array. This is done by passing every command line argument as a single element within the array. The array will then be translated into an FFmpeg command using the execute method from FFmpeg:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ffmpeg.execute(command, object : ExecuteBinaryResponseHandler() { ... }&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;As we want to implement Trim and Crop I will show how this can be done using the &lt;code&gt;arrayOf&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Trim:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; command &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; arrayOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-y&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-i&quot;&lt;/span&gt;&lt;span&gt;, input, &lt;/span&gt;&lt;span&gt;&quot;-ss&quot;&lt;/span&gt;&lt;span&gt;, startPos, &lt;/span&gt;&lt;span&gt;&quot;-to&quot;&lt;/span&gt;&lt;span&gt;, endPos, &lt;/span&gt;&lt;span&gt;&quot;-c&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;copy&quot;&lt;/span&gt;&lt;span&gt;, output)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;“-y”:&lt;/strong&gt; overwrites output files without asking&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-i”:&lt;/strong&gt; specifies an input file&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;input:&lt;/strong&gt; the path of the source video to trim&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-ss”:&lt;/strong&gt; specifies that the next value will be the starting point of the resulting video&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;startPos:&lt;/strong&gt; the starting position in “%d:%02d:%02d” format&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-to”:&lt;/strong&gt; specifies that the next value will be the end position of the resulting video&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;endPos:&lt;/strong&gt; the end position in “%d:%02d:%02d” format&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-c” AND “copy”&lt;/strong&gt;: defines that the stream will not be encoded. The resulting video will only be saved.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;output:&lt;/strong&gt; the path of the resulting video&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To better understand every argument you can have a look at &lt;a href=&quot;https://www.ffmpeg.org/ffmpeg.html#Main-options&quot;&gt;the official FFmpeg documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Additionally, to provide arguments for &lt;code&gt;startPos&lt;/code&gt; and &lt;code&gt;endPos&lt;/code&gt; you normally would have to use a utility function that converts the timestamp of the start and end position into the desired “%d:%02d:%02d” format (a string):&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fun&lt;/span&gt;&lt;span&gt; convertTimestampToString&lt;/span&gt;&lt;span&gt;(timeInMs: &lt;/span&gt;&lt;span&gt;Float&lt;/span&gt;&lt;span&gt;): &lt;/span&gt;&lt;span&gt;String&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        val&lt;/span&gt;&lt;span&gt; totalSeconds &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (timeInMs &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; 1000&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;toInt&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        val&lt;/span&gt;&lt;span&gt; seconds &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; totalSeconds &lt;/span&gt;&lt;span&gt;%&lt;/span&gt;&lt;span&gt; 60&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        val&lt;/span&gt;&lt;span&gt; minutes &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; totalSeconds &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; 60&lt;/span&gt;&lt;span&gt; %&lt;/span&gt;&lt;span&gt; 60&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        val&lt;/span&gt;&lt;span&gt; hours &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; totalSeconds &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; 3600&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        val&lt;/span&gt;&lt;span&gt; formatter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Formatter&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        return&lt;/span&gt;&lt;span&gt; if&lt;/span&gt;&lt;span&gt; (hours &lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            formatter.&lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;%d:%02d:%02d&quot;&lt;/span&gt;&lt;span&gt;, hours, minutes, seconds).&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            formatter.&lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;%02d:%02d&quot;&lt;/span&gt;&lt;span&gt;, minutes, seconds).&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Crop:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; command &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; arrayOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-i&quot;&lt;/span&gt;&lt;span&gt;, input, &lt;/span&gt;&lt;span&gt;&quot;-filter:v&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;crop=&lt;/span&gt;&lt;span&gt;$w&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;$h&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;$x&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;$y&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-threads&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;5&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-preset&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;ultrafast&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-strict&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-2&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-c:a&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;copy&quot;&lt;/span&gt;&lt;span&gt;, output)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;“-i”:&lt;/strong&gt; specifies an input file&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;input:&lt;/strong&gt; the path of the source video to trim&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-filter:v”:&lt;/strong&gt; defines that a filtergraph is used&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“crop=$w:$h:$x:$y”:&lt;/strong&gt; use crop functionality to crop a part from the video start at point x:y and having a width(w) and height(h).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-threads”:&lt;/strong&gt; specifies that the next value will set the thread count&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“5”:&lt;/strong&gt; the number of threads to use&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-preset”:&lt;/strong&gt; specifies that the next value will set the encoding preset&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“ultrafast”:&lt;/strong&gt; the encoding preset to use&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-strict”:&lt;/strong&gt; specifies how strictly the standards should be followed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-2”:&lt;/strong&gt; the strict value&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;“-c:a” AND “copy”&lt;/strong&gt; defines that the stream will not be encoded and ALL audio streams will also be used.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;output:&lt;/strong&gt; the path of the resulting video&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, check &lt;a href=&quot;https://www.ffmpeg.org/ffmpeg.html#Main-options&quot;&gt;the official FFmpeg documentation&lt;/a&gt; to better understand every argument.&lt;/p&gt;
&lt;h3 id=&quot;5-implement-the-ui&quot;&gt;5. Implement the UI&lt;/h3&gt;
&lt;p&gt;To create a fancy UI you should create a custom view that shows some of the frames available in the video (&lt;code&gt;VideoPreviewView&lt;/code&gt;). Also, it should contain a &lt;code&gt;SeekBar&lt;/code&gt; for &lt;strong&gt;cropping&lt;/strong&gt; and a &lt;code&gt;RangeSeekBar&lt;/code&gt; for &lt;strong&gt;trimming&lt;/strong&gt;. The &lt;code&gt;SeekBar&lt;/code&gt; will be used to switch to a certain timestamp within the video to see what is actually trimmed or cropped. The RangeSeekBar will only be used within the trimming UI to define the start and end position of the resulting video.&lt;/p&gt;
&lt;p&gt;While &lt;code&gt;SeekBar&lt;/code&gt; is &lt;a href=&quot;https://developer.android.com/reference/android/widget/SeekBar&quot;&gt;an Android Widget&lt;/a&gt; the &lt;code&gt;RangeSeekBar&lt;/code&gt; is more complicated but there exist several implementations that can be used: By &lt;a href=&quot;https://github.com/tizisdeepan/VideoEditor/blob/master/video-editor/src/main/java/com/video/trimmer/view/RangeSeekBarView.kt&quot;&gt;Tiszideepan&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can implement the &lt;code&gt;VideoPreviewView&lt;/code&gt; with an easy approach. Divide the video into a specific number of frames that are based on the view’s width and display every frame sequentially within a single view to have a preview. To do this you need the width of the view and the duration of the video that can be found using the &lt;a href=&quot;https://developer.android.com/reference/android/media/MediaMetadataRetriever&quot;&gt;MediaMetadataRetriever&lt;/a&gt;. With the &lt;code&gt;MediaMetadataRetriever&lt;/code&gt;, you can use &lt;code&gt;getFrameAtTime()&lt;/code&gt; to fetch a single frame at a specific timestamp. If you now want to display a complete video preview you need to display &lt;code&gt;viewWidth / frameWidth&lt;/code&gt; frames. Unfortunately, depending on the video length and video width, it could happen that only a few frames will be present within the &lt;code&gt;VideoPreviewView&lt;/code&gt;. To fix this problem you have to maintain a threshold to ensure that a specific number of frames are displayed. This means that you have to crop the frames to a certain width until calculated frames equal the threshold.&lt;/p&gt;
&lt;p&gt;The following code snippet will show how you can achieve this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; createPreview&lt;/span&gt;&lt;span&gt;(viewWidth: &lt;/span&gt;&lt;span&gt;Int&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        BackgroundExecutor.&lt;/span&gt;&lt;span&gt;execute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;BackgroundExecutor&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;Task&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0L&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; execute&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; threshold &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; 11&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; thumbnails &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; LongSparseArray&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Bitmap&lt;/span&gt;&lt;span&gt;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; mediaMetadataRetriever &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; MediaMetadataRetriever&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    mediaMetadataRetriever.&lt;/span&gt;&lt;span&gt;setDataSource&lt;/span&gt;&lt;span&gt;(context, videoUri)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; videoLengthInMs &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; (Integer.&lt;/span&gt;&lt;span&gt;parseInt&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        mediaMetadataRetriever.&lt;/span&gt;&lt;span&gt;extractMetadata&lt;/span&gt;&lt;span&gt;(MediaMetadataRetriever.METADATA_KEY_DURATION)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    ) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; 1000&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;toLong&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; frameHeight &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; viewHeight&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; initialBitmap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; mediaMetadataRetriever.&lt;/span&gt;&lt;span&gt;getFrameAtTime&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        0&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        MediaMetadataRetriever.OPTION_CLOSEST_SYNC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; frameWidth &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        ((initialBitmap.width.&lt;/span&gt;&lt;span&gt;toFloat&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; initialBitmap.height.&lt;/span&gt;&lt;span&gt;toFloat&lt;/span&gt;&lt;span&gt;()) &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; frameHeight.&lt;/span&gt;&lt;span&gt;toFloat&lt;/span&gt;&lt;span&gt;()).&lt;/span&gt;&lt;span&gt;toInt&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    var&lt;/span&gt;&lt;span&gt; numThumbs &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ceil&lt;/span&gt;&lt;span&gt;((viewWidth.&lt;/span&gt;&lt;span&gt;toFloat&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; frameWidth)).&lt;/span&gt;&lt;span&gt;toInt&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    if&lt;/span&gt;&lt;span&gt; (numThumbs &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt; threshold) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        numThumbs &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; threshold&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; cropWidth &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; viewWidth &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; threshold&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    val&lt;/span&gt;&lt;span&gt; interval &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; videoLengthInMs &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; numThumbs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    for&lt;/span&gt;&lt;span&gt; (i &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt; until numThumbs) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        var&lt;/span&gt;&lt;span&gt; bitmap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; mediaMetadataRetriever.&lt;/span&gt;&lt;span&gt;getFrameAtTime&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            i &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; interval,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            MediaMetadataRetriever.OPTION_CLOSEST_SYNC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        bitmap?.&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                bitmap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Bitmap.&lt;/span&gt;&lt;span&gt;createScaledBitmap&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                    bitmap,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                    frameWidth,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                    frameHeight,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                    false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                bitmap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Bitmap.&lt;/span&gt;&lt;span&gt;createBitmap&lt;/span&gt;&lt;span&gt;(bitmap, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, cropWidth, bitmap.height)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            } &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (e: &lt;/span&gt;&lt;span&gt;Exception&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                Log.&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;(TAG, &lt;/span&gt;&lt;span&gt;&quot;error while create bitmap: &lt;/span&gt;&lt;span&gt;$e&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            thumbnails.&lt;/span&gt;&lt;span&gt;put&lt;/span&gt;&lt;span&gt;(i.&lt;/span&gt;&lt;span&gt;toLong&lt;/span&gt;&lt;span&gt;(), bitmap)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    mediaMetadataRetriever.&lt;/span&gt;&lt;span&gt;release&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    returnBitmaps&lt;/span&gt;&lt;span&gt;(thumbnails)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                } &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (e: &lt;/span&gt;&lt;span&gt;Throwable&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                    Thread.&lt;/span&gt;&lt;span&gt;getDefaultUncaughtExceptionHandler&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                        .&lt;/span&gt;&lt;span&gt;uncaughtException&lt;/span&gt;&lt;span&gt;(Thread.&lt;/span&gt;&lt;span&gt;currentThread&lt;/span&gt;&lt;span&gt;(), e)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Additionally, for cropping a video you need to display a &lt;em&gt;crop rectangle&lt;/em&gt; within the UI to let users decide which part of the video they want to crop. Also, you have to give users the ability to reposition the crop area. To do this, you can use &lt;a href=&quot;https://github.com/ArthurHub/Android-Image-Cropper&quot;&gt;the ImageCropper library from ArthurHub&lt;/a&gt; which is initially designed to crop an image but can also be used for videos as you only need the values from the rectangle. Add the &lt;code&gt;ImageCropper&lt;/code&gt; to your project:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dependencies {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    implementation &apos;com.theartofdev.edmodo:android-image-cropper:2.8.0&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, you can extend a &lt;code&gt;CropperActivity&lt;/code&gt; which loads the current frame of the video (that you can extract with the &lt;code&gt;VideoPreviewView&lt;/code&gt;) by inserting the &lt;code&gt;CropImageView&lt;/code&gt; layout to get the cropping bounds. Once, you select an area in the UI, the rectangle values can be extracted and used to calculate the needed values for video cropping with this function:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; rect &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; cropFrame.cropRect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; w &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; abs&lt;/span&gt;&lt;span&gt;(rect.left &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; rect.right)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; h &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; abs&lt;/span&gt;&lt;span&gt;(rect.top &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; rect.bottom)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; x &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; rect.left&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; y &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; rect.top&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These values will then be used as input variables for the FFmpeg crop command.&lt;/p&gt;
&lt;p&gt;To help you use all this information I created a project that uses all explained snippets to create a sample app that can crop and trim videos. The source code can be found &lt;a href=&quot;https://github.com/paulknulst/VideoTrimmer&quot;&gt;on my personal GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;fixing-error13-permission-denied&quot;&gt;Fixing error=13, Permission denied&lt;/h2&gt;
&lt;p&gt;Unfortunately, if you implement the described functionality and use the compiled FFmpeg library you will run into this problem if you compile your app for SDK 29 and onwards.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.knulst.de/content/images/2022/08/image-4.png&quot; alt=&quot;Permission denied error due to wrong target SDK&quot;&gt;&lt;/p&gt;
&lt;p&gt;Permission denied error due to wrong target SDK The problem is caused because from Android Q onwards it is not allowed to execute binaries in your app’s private data directory. The error is described in &lt;a href=&quot;https://issuetracker.google.com/issues/128554619?pli=1&quot;&gt;the official issue tracker from google&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One solution will be that you compile your app for SDK 28 but this could lead to problems if you try to put your app into the Android Play Store.&lt;/p&gt;
&lt;p&gt;Another solution will be that you compile your app for SDK 29 and stop putting binaries in any not supported directory. Unfortunately, the code that moves the binaries is within the 3rd party lib from WritingMinds and has to be removed by you (with a Pull Request) or by the maintainer of the library. A future-proof solution will be that you stop using external binaries and start compiling dependencies as an NDK project. This will be a lot of work but you can find help within a famous repository that compiles CPP to Java and also has &lt;a href=&quot;https://github.com/bytedeco/javacpp-presets/tree/master/ffmpeg&quot;&gt;FFmpeg&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;add-more-commands&quot;&gt;Add more commands&lt;/h2&gt;
&lt;p&gt;After you implemented crop and trim you maybe want to add more cool features to your Android app. To do this with FFmpeg and the previously described code you can do this by simply creating a new function within the &lt;code&gt;VideoCommands()&lt;/code&gt; and translate the FFmpeg command into a &lt;code&gt;VideoCommands()&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;The next sections will describe some fancy functions that would improve the usability of your app. Just add a new function with this command and fill the callbacks with a toast or anything else. Then call the function from anywhere within your app (because you do not need UI for this).&lt;/p&gt;
&lt;h3 id=&quot;removing-audio-from-videos&quot;&gt;Removing audio from videos&lt;/h3&gt;
&lt;p&gt;You might run into a scenario where you’d only like to keep the visuals of a video and remove the audio track, for instance for voicing over certain footage or removing background noise:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; command &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; arrayOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-i&quot;&lt;/span&gt;&lt;span&gt;, input, &lt;/span&gt;&lt;span&gt;&quot;-an&quot;&lt;/span&gt;&lt;span&gt;, output)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;compress-a-video&quot;&gt;Compress a video&lt;/h3&gt;
&lt;p&gt;Make big videos smaller to save your valuable disk resources.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;val&lt;/span&gt;&lt;span&gt; command &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; arrayOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-i&quot;&lt;/span&gt;&lt;span&gt;, input, &lt;/span&gt;&lt;span&gt;&quot;-vf&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;scale=&lt;/span&gt;&lt;span&gt;$w&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;$h&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-c:v&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;libx264&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-preset&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;veryslow&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;-crf&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;24&quot;&lt;/span&gt;&lt;span&gt;, output)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;$w and $h&lt;/strong&gt; are the new sizes (should be smaller). If one is -1 it will keep the ratio and is automatically adjusted in relation to the other one.&lt;/p&gt;
&lt;h3 id=&quot;change-playback-speed&quot;&gt;Change playback speed&lt;/h3&gt;
&lt;p&gt;Increase the playback speed to make long videos faster or decrease it for showing something really awesome very slowly.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; command &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; arrayOf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;-i&quot;&lt;/span&gt;&lt;span&gt;, input, &lt;/span&gt;&lt;span&gt;&quot;-vf&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;setpts=&lt;/span&gt;&lt;span&gt;$scale&lt;/span&gt;&lt;span&gt;*PTS&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, output)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;$scale&lt;/strong&gt; should be &gt; 1 for slowing down a video or &amp;#x3C; 1 for fastening a video.&lt;/p&gt;
&lt;h3 id=&quot;more&quot;&gt;More&lt;/h3&gt;
&lt;p&gt;To find more cool commands search &lt;a href=&quot;https://www.ffmpeg.org/ffmpeg.html&quot;&gt;the official argument documentation&lt;/a&gt; and implement them by putting every argument into a value of the array function as described earlier.&lt;/p&gt;
&lt;p&gt;For production-grade application you may be better served by using our &lt;a href=&quot;https://img.ly/products/video-sdk/&quot;&gt;VideoEditorSDK&lt;/a&gt; which in addition to professional trim and crop features allows your users to add filters, text and audio overlays. Recently, we released time-based sprites as well allowing users to time the appearance of text and stickers in videos.&lt;/p&gt;
&lt;h3 id=&quot;closing-notes&quot;&gt;Closing Notes&lt;/h3&gt;
&lt;p&gt;In this article, you learned that you can use FFmpeg to crop and trim videos on Android. Unfortunately, it does not work out of the box and you have to compile the library using complex techniques. Luckily, there exist some useful libraries that do the heavy lifting for you.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/paulknulst/VideoTrimmer&quot;&gt;Using my app as a baseline&lt;/a&gt; you are able to crop and trim videos. Additionally, you can implement more commands easily if they do not need a GUI because creating a fancy-looking GUI was a complicated part of developing a cropping and trimming app.&lt;/p&gt;
&lt;p&gt;Unfortunately, there are problems with the library that was used because Android Q introduces a security fix that forbids apps to execute binaries within their data folder. But, this is only a problem if you want to create a “Google Play Store” ready app because you can avoid this problem if compile your app for SDK 28 (and lower). Keep in mind that if you want to create a private app that you distribute on your website instead of using the Play Store you can do this. But, if you want to upload your app to the Google Play Store you have to search for &lt;a href=&quot;https://trac.ffmpeg.org/wiki/CompilationGuide/Android&quot;&gt;another FFmpeg library within the documentation&lt;/a&gt; or build one from scratch so you can compile your app for the current needed SDK.&lt;/p&gt;
&lt;p&gt;Looking to integrate video capabilities into your app? Check out our &lt;a href=&quot;https://img.ly/products/video-sdk/&quot;&gt;Video Editor SDK&lt;/a&gt;, &lt;a href=&quot;https://img.ly/use-cases/story-reels-short-video-creation/&quot;&gt;Short Video Creation&lt;/a&gt;, and &lt;a href=&quot;https://img.ly/products/video-sdk-mobile/&quot;&gt;Camera SDK&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;I hope you enjoyed reading this article and are able to create an Android app that can crop and trim videos. If you have any questions, need help, or want to give feedback @ us on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt;. We are happy to help.&lt;/p&gt;</content:encoded><dc:creator>Paul</dc:creator><media:content url="https://blog.img.ly/2022/09/Android-trimmimg-with-Kotlin.png" medium="image"/><category>How-To</category><category>Tech</category><category>Android</category><category>Android App Development</category><category>Tutorial</category></item><item><title>How to Apply Filter Effects to Video using VidEffects library on Android</title><link>https://img.ly/blog/how-to-apply-filter-effects-to-video-using-videffects-on-android/</link><guid isPermaLink="true">https://img.ly/blog/how-to-apply-filter-effects-to-video-using-videffects-on-android/</guid><description>Learn about the benefits and limitations of VidEffects for video editing on Android.</description><pubDate>Tue, 24 May 2022 11:41:23 GMT</pubDate><content:encoded>&lt;p&gt;Video manipulation is a complex topic on Android because there is no out-of-the-box support from the Android SDK, and using FFmpeg requires not just a very elaborate setup but having to climb a steep learning curve to learn its CLI commands.&lt;/p&gt;
&lt;p&gt;As an aside, we have also put together how-to blogs to elaborate on other video manipulation operations like merging still images to form videos, &lt;a href=&quot;https://img.ly/blog/working-with-large-video-and-image-files-on-ios-with-swift/&quot;&gt;compressing and resizing videos&lt;/a&gt;, or background removal.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/krazykira/VidEffects&quot;&gt;VidEffects&lt;/a&gt; library is much faster to set up than FFmpeg, and it works by applying effects to &lt;code&gt;GLSurfaceView&lt;/code&gt;. This blog post will demonstrate how to apply Filter effects to a video in an Android app using the VidEffects library.&lt;/p&gt;
&lt;p&gt;VidEffects does support storing videos on the disk, but it has limitations in this regard which we will discuss in a moment.&lt;/p&gt;
&lt;p&gt;Before we begin, keep in mind that the VidEffects library differentiates between &lt;strong&gt;Effects&lt;/strong&gt; and &lt;strong&gt;Filters&lt;/strong&gt;. As an example, we will apply both and discuss the crucial difference between the two.&lt;/p&gt;
&lt;p&gt;Without much further ado, let’s start coding.&lt;/p&gt;
&lt;h2 id=&quot;create-a-videffects-example-app&quot;&gt;Create a VidEffects Example App&lt;/h2&gt;
&lt;p&gt;Create a &lt;strong&gt;New Project&lt;/strong&gt; with an Empty Activity in Android Studio. Select &lt;em&gt;Kotlin&lt;/em&gt; as the language (or Java if you wish), and set **Minimum SDK to API 21.&lt;/p&gt;
&lt;p&gt;You can also download the final code from &lt;a href=&quot;https://github.com/numerative/videffects_sample&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;add-dependency&quot;&gt;Add Dependency&lt;/h3&gt;
&lt;p&gt;In your module-level &lt;strong&gt;build.gradle&lt;/strong&gt; (app/build.gradle) file add the latest VidEffects dependency.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;groovy&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dependencies {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ..&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    implementation &lt;/span&gt;&lt;span&gt;&quot;com.github.krazykira:videffects:1.1.1&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ..&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, enable &lt;code&gt;viewBinding&lt;/code&gt; by adding the following lines within the &lt;code&gt;android{}&lt;/code&gt; closure.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;groovy&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;android {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ..&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    buildFeatures {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        viewBinding &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By using View Binding, we can avoid the &lt;code&gt;findViewById()&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;Click &lt;strong&gt;Sync Now&lt;/strong&gt; to sync the project with gradle files.&lt;/p&gt;
&lt;h3 id=&quot;add-video-asset-file&quot;&gt;Add Video Asset File&lt;/h3&gt;
&lt;p&gt;Place the sample video file in &lt;strong&gt;app/src/main/assets/&lt;/strong&gt; directory. Ideally, your app will load the video file from disk – but to avoid complexity, we are placing the video in the assets directory.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;directory_tree_android_video-effect&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 304px) 304px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;304&quot; height=&quot;299&quot; src=&quot;https://img.ly/_astro/directory_tree_android_video-effect_4vaxk.webp&quot; srcset=&quot;/_astro/directory_tree_android_video-effect_4vaxk.webp 304w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;create-a-layout&quot;&gt;Create a Layout&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;screen_no_filter-1&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 500px) 500px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;500&quot; height=&quot;889&quot; src=&quot;https://img.ly/_astro/screen_no_filter-1_g8KRl.webp&quot; srcset=&quot;/_astro/screen_no_filter-1_g8KRl.webp 500w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Create the above layout with a &lt;code&gt;VideoSurfaceView&lt;/code&gt;, three Filter buttons, and one Reset Button.&lt;/p&gt;
&lt;p&gt;Replace your &lt;strong&gt;activity_main.xml&lt;/strong&gt;`s code with the following.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;xml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;androidx.constraintlayout.widget.ConstraintLayout&lt;/span&gt;&lt;span&gt; xmlns:android&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;http://schemas.android.com/apk/res/android&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    xmlns:app&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;http://schemas.android.com/apk/res-auto&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    xmlns:tools&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;http://schemas.android.com/tools&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    android:layout_width&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;match_parent&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    android:layout_height&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;match_parent&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    tools:context&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;.MainActivity&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;com.sherazkhilji.videffects.view.VideoSurfaceView&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        android:id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;@+id/mVideoSurfaceView&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        android:layout_width&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;match_parent&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        android:layout_height&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;300dp&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        app:layout_constraintTop_toTopOf&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;parent&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;androidx.constraintlayout.widget.ConstraintLayout&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        android:id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;@+id/wrapper&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        android:layout_width&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wrap_content&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        android:layout_height&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wrap_content&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        app:layout_constraintEnd_toEndOf&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;parent&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        app:layout_constraintStart_toStartOf&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;parent&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        app:layout_constraintTop_toBottomOf&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;@id/mVideoSurfaceView&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;&lt;/span&gt;&lt;span&gt;androidx.appcompat.widget.AppCompatButton&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            android:id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;@+id/bwButton&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            android:layout_width&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wrap_content&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            android:layout_height&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wrap_content&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            android:text&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Black and White&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            app:layout_constraintStart_toStartOf&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;parent&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            app:layout_constraintTop_toTopOf&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;parent&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;&lt;/span&gt;&lt;span&gt;androidx.appcompat.widget.AppCompatButton&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            android:id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;@+id/grainButton&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            android:layout_width&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wrap_content&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            android:layout_height&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wrap_content&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            android:text&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Grain&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            app:layout_constraintStart_toEndOf&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;@id/bwButton&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            app:layout_constraintTop_toTopOf&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;parent&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;&lt;/span&gt;&lt;span&gt;androidx.appcompat.widget.AppCompatButton&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            android:id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;@+id/duotoneButton&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            android:layout_width&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wrap_content&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            android:layout_height&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wrap_content&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            android:text&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;DuoTone&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            app:layout_constraintStart_toEndOf&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;@id/grainButton&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            app:layout_constraintTop_toTopOf&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;parent&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span&gt;androidx.constraintlayout.widget.ConstraintLayout&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;&lt;/span&gt;&lt;span&gt;androidx.appcompat.widget.AppCompatButton&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        android:id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;@+id/resetButton&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        android:layout_width&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wrap_content&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        android:layout_height&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;wrap_content&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        android:text&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;Reset&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        app:layout_constraintEnd_toEndOf&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;parent&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        app:layout_constraintStart_toStartOf&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;parent&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        app:layout_constraintTop_toBottomOf&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;@+id/wrapper&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;androidx.constraintlayout.widget.ConstraintLayout&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;initialize-the-video&quot;&gt;Initialize the video&lt;/h2&gt;
&lt;p&gt;Before initializing the video, set up an instance of the Binding class to use it within the Activity.&lt;/p&gt;
&lt;h3 id=&quot;setup-view-binding&quot;&gt;Setup View Binding&lt;/h3&gt;
&lt;p&gt;Add the following code in &lt;strong&gt;MainActivity.kt&lt;/strong&gt; before calling &lt;code&gt;setContentView()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you do not wish to use View Binding and instead prefer using &lt;code&gt;findViewById()&lt;/code&gt;, skip to the next step &lt;em&gt;Initializing the Sample Video&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; lateinit&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt; binding: &lt;/span&gt;&lt;span&gt;ActivityMainBinding&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; MainActivity&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;AppCompatActivity&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;		private&lt;/span&gt;&lt;span&gt; lateinit&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt; binding: &lt;/span&gt;&lt;span&gt;ActivityMainBinding&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onCreate&lt;/span&gt;&lt;span&gt;(savedInstanceState: &lt;/span&gt;&lt;span&gt;Bundle&lt;/span&gt;&lt;span&gt;?) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        super&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;onCreate&lt;/span&gt;&lt;span&gt;(savedInstanceState)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        binding &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; ActivityMainBinding.&lt;/span&gt;&lt;span&gt;inflate&lt;/span&gt;&lt;span&gt;(layoutInflater)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        ..&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    companion&lt;/span&gt;&lt;span&gt; object&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        private&lt;/span&gt;&lt;span&gt; const&lt;/span&gt;&lt;span&gt; val&lt;/span&gt;&lt;span&gt; TAG &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &quot;MainActivity&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;ActivityMainBinding&lt;/code&gt; class is auto-generated. If it did not get auto-generated, then sync the project with the gradle file from &lt;strong&gt;File &gt; Sync Project with Gradle Files.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Next, pass the &lt;em&gt;root view&lt;/em&gt; to the &lt;code&gt;setContentView()&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; MainActivity&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;AppCompatActivity&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ..&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onCreate&lt;/span&gt;&lt;span&gt;(savedInstanceState: &lt;/span&gt;&lt;span&gt;Bundle&lt;/span&gt;&lt;span&gt;?) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        ..&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        setContentView&lt;/span&gt;&lt;span&gt;(binding.root)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;initializing-the-sample-video&quot;&gt;Initializing the Sample Video&lt;/h3&gt;
&lt;p&gt;Next, declare and initialize the &lt;code&gt;MediaPlayer&lt;/code&gt; variable.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; MainActivity&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;AppCompatActivity&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ..&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    private&lt;/span&gt;&lt;span&gt; lateinit&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt; mMediaPlayer: &lt;/span&gt;&lt;span&gt;MediaPlayer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onCreate&lt;/span&gt;&lt;span&gt;(savedInstanceState: &lt;/span&gt;&lt;span&gt;Bundle&lt;/span&gt;&lt;span&gt;?) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            ..&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            mMediaPlayer &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; MediaPlayer&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then open the video file from our assets directory and pass it as the data source to the Media Player.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onCreate&lt;/span&gt;&lt;span&gt;(savedInstanceState: &lt;/span&gt;&lt;span&gt;Bundle&lt;/span&gt;&lt;span&gt;?) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ..&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        val&lt;/span&gt;&lt;span&gt; afd &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; assets.&lt;/span&gt;&lt;span&gt;openFd&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;sample.mp4&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        mMediaPlayer.&lt;/span&gt;&lt;span&gt;setDataSource&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            afd.fileDescriptor,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            afd.startOffset, afd.length&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    } &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (e: &lt;/span&gt;&lt;span&gt;Exception&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        Log.&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;(TAG, e.message, e)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, all that is remaining is to initialize the &lt;code&gt;VideoSurfaceView&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onCreate&lt;/span&gt;&lt;span&gt;(savedInstanceState: &lt;/span&gt;&lt;span&gt;Bundle&lt;/span&gt;&lt;span&gt;?) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        ..&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        binding.mVideoSurfaceView.&lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt;(mMediaPlayer, &lt;/span&gt;&lt;span&gt;NoEffectFilter&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your app should run. While the buttons still do not work, we will &lt;code&gt;setOnClickListeners()&lt;/code&gt; next.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;screen_no_filter-2&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 500px) 500px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;500&quot; height=&quot;889&quot; src=&quot;https://img.ly/_astro/screen_no_filter-2_biy8Y.webp&quot; srcset=&quot;/_astro/screen_no_filter-2_biy8Y.webp 500w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;apply-videffects-effects&quot;&gt;Apply VidEffects Effects&lt;/h2&gt;
&lt;p&gt;The VidEffects library offers &lt;a href=&quot;https://github.com/krazykira/VidEffects#supported-effects&quot;&gt;multiple effects&lt;/a&gt;, but we will be applying three: Black and White, Grain, and Duotone.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;MainActivity&lt;/strong&gt;’s &lt;code&gt;onCreate()&lt;/code&gt;, copy the following code to setup all the &lt;code&gt;setOnClickListeners&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;kotlin&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;override&lt;/span&gt;&lt;span&gt; fun&lt;/span&gt;&lt;span&gt; onCreate&lt;/span&gt;&lt;span&gt;(savedInstanceState: &lt;/span&gt;&lt;span&gt;Bundle&lt;/span&gt;&lt;span&gt;?) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    ..&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //Black and White Effect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    binding.bwButton.&lt;/span&gt;&lt;span&gt;setOnClickListener&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        binding.mVideoSurfaceView.shader &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; BlackAndWhiteEffect&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //Grain Filter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    binding.grainButton.&lt;/span&gt;&lt;span&gt;setOnClickListener&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        val&lt;/span&gt;&lt;span&gt; grainFilter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; GrainFilter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        grainFilter.&lt;/span&gt;&lt;span&gt;setIntensity&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0.5f&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        binding.mVideoSurfaceView.filter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; grainFilter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //Duotone Effect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    binding.duotoneButton.&lt;/span&gt;&lt;span&gt;setOnClickListener&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        binding.mVideoSurfaceView.shader &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            DuotoneEffect&lt;/span&gt;&lt;span&gt;(Color.BLUE, Color.YELLOW)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //Reset with AutoFixFilter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    binding.resetButton.&lt;/span&gt;&lt;span&gt;setOnClickListener&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        binding.mVideoSurfaceView.filter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; NoEffectFilter&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the app.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;screen-bw-duo-grain&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1500px) 1500px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1500&quot; height=&quot;889&quot; src=&quot;https://img.ly/_astro/screen-bw-duo-grain_U18c2.webp&quot; srcset=&quot;/_astro/screen-bw-duo-grain_6sBsy.webp 640w, /_astro/screen-bw-duo-grain_1q99jx.webp 750w, /_astro/screen-bw-duo-grain_Z1jzBKe.webp 828w, /_astro/screen-bw-duo-grain_ZowVa8.webp 1080w, /_astro/screen-bw-duo-grain_Z2lItwG.webp 1280w, /_astro/screen-bw-duo-grain_U18c2.webp 1500w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;difference-between-effects-and-filters&quot;&gt;Difference Between Effects and Filters&lt;/h2&gt;
&lt;p&gt;As mentioned before, Effects and Filters are two separate functions in the VidEffects library.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Effects&lt;/em&gt; are temporary, view-only overlays that cannot be stored on the disk. &lt;em&gt;Filters&lt;/em&gt; can be viewed and stored on the disk. Unfortunately, a majority of the overlays offered by VidEffects are &lt;a href=&quot;https://github.com/krazykira/VidEffects/tree/master/videffects/src/main/java/com/sherazkhilji/videffects&quot;&gt;Effects&lt;/a&gt; and therefore only useful for playback only. At the time of writing this article, only three &lt;a href=&quot;https://github.com/krazykira/VidEffects/tree/master/videffects/src/main/java/com/sherazkhilji/videffects/filter&quot;&gt;Filters&lt;/a&gt; are available.&lt;/p&gt;
&lt;h2 id=&quot;limitations&quot;&gt;Limitations&lt;/h2&gt;
&lt;p&gt;One of the biggest limitations of this library, as mentioned above, is that the majority of the effects provided by the library are just view-only.&lt;/p&gt;
&lt;p&gt;Moreover, to save videos with filters, the app must target &lt;code&gt;minSdk 23&lt;/code&gt; at least or use FFmpeg.&lt;/p&gt;
&lt;p&gt;Many of these Effects do not have an intensity parameter and therefore we cannot fine-tune these effects.&lt;/p&gt;
&lt;h2 id=&quot;alternative&quot;&gt;Alternative&lt;/h2&gt;
&lt;p&gt;If you are looking for an easy-to-integrate solution that overcomes these limitations, take a look at IMG.LY’s &lt;a href=&quot;https://img.ly/docs/vesdk/android/introduction/getting_started?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;VideoEditor SDK&lt;/a&gt;. It provides more than 60 high-quality adjustable filters out-of-the-box. Moreover, you can easily add &lt;a href=&quot;https://img.ly/docs/vesdk/android/features/filters/#add-custom-lut-filters?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;custom filters&lt;/a&gt; using LUT files. VideoEditor SDK works for Android SDK version 21 and above and thus targets more devices.&lt;/p&gt;
&lt;p&gt;If the purpose is to apply effects during the playback, the VidEffects library is a solid and free open-source solution. While it handles all the complex video editing, developers will have to spend some time developing the layout.&lt;/p&gt;
&lt;h3 id=&quot;thanks-for-reading-let-us-know-what-you-think-on-twitter-or-stay-in-the-loop-with-our-newsletter&quot;&gt;Thanks for reading! Let us know what you think on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt;, or stay in the loop with our &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;Newsletter&lt;/a&gt;.&lt;/h3&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2022/05/videffects-add-filter-to-video-android.png" medium="image"/><category>How-To</category><category>Video Editing</category><category>Android</category><category>Android App Development</category><category>Mobile App Development</category><category>Tutorial</category></item><item><title>Flutter: Our new plugins made for Dart developers</title><link>https://img.ly/blog/flutter-native-plugins-made-for-dart-developers/</link><guid isPermaLink="true">https://img.ly/blog/flutter-native-plugins-made-for-dart-developers/</guid><description>We are happy to announce our new official Flutter plugins wrapping our native PhotoEditor SDK and VideoEditor SDK for one of the most popular cross-platform frameworks.</description><pubDate>Thu, 11 Mar 2021 14:12:09 GMT</pubDate><content:encoded>&lt;p&gt;Flutter enables developers to build native iOS and Android applications with a single Dart codebase whilst the application’s performance is kept at a very high native-like level. This is even more ensured by the huge amount of Flutter plugins, which wrap native code for the use in Dart.&lt;/p&gt;
&lt;p&gt;With the rising demand for photo and video editing solutions, we were committed to provide plugins that enable developers to use our &lt;a href=&quot;https://img.ly/docs/pesdk/&quot;&gt;native PhotoEditor SDK&lt;/a&gt; and our &lt;a href=&quot;https://img.ly/docs/vesdk/&quot;&gt;native VideoEditor SDK&lt;/a&gt; within their Flutter applications. This said we are proud to announce that the Flutter plugins &lt;a href=&quot;https://pub.dev/packages/photo_editor_sdk&quot;&gt;&lt;code&gt;photo_editor_sdk&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://pub.dev/packages/video_editor_sdk&quot;&gt;&lt;code&gt;video_editor_sdk&lt;/code&gt;&lt;/a&gt; are now extending our list of cross-platform modules. The combination of both products allows you to add comprehensive image and video editing tools to your Flutter application for iOS and Android - within minutes.&lt;/p&gt;
&lt;h2 id=&quot;usage&quot;&gt;Usage&lt;/h2&gt;
&lt;p&gt;While building the plugins, we focused on making the use and integration of our existing SDKs as easy as possible and minimizing the required platform-specific knowledge and skillset. Therefore, using the plugins is as easy as writing a single line of Dart code:&lt;/p&gt;

&lt;h3 id=&quot;customization&quot;&gt;Customization&lt;/h3&gt;
&lt;p&gt;Furthermore, we wanted to keep the high level of customization options like cropping or resizing that our customers are used to in our products. In this sense, we created a dedicated &lt;a href=&quot;https://pub.dev/documentation/imgly_sdk/latest/imgly_sdk/Configuration-class.html&quot;&gt;&lt;code&gt;Configuration&lt;/code&gt;&lt;/a&gt; Dart class which serves the option to customize the SDK to your needs without ever leaving the Flutter world.&lt;br&gt;
Using the configuration is fast and easy:&lt;/p&gt;

&lt;h2 id=&quot;where-to-go-from-here&quot;&gt;Where to go from here?&lt;/h2&gt;
&lt;p&gt;Feel free to explore our packages, usage examples as well as the API documentation on &lt;a href=&quot;https://pub.dev/publishers/img.ly/packages&quot;&gt;pub.dev&lt;/a&gt; and/ or &lt;a href=&quot;https://github.com/imgly?q=flutter&quot;&gt;GitHub&lt;/a&gt;.&lt;br&gt;
We released our new Flutter plugins under open-source licenses, so feedback and pull requests are welcome.&lt;/p&gt;</content:encoded><dc:creator>Leon</dc:creator><media:content url="https://blog.img.ly/2021/03/flutter-plugin-for-photo-video-editor-dart.jpg" medium="image"/><category>Flutter</category><category>Photo Editor</category><category>Developer</category><category>Video Editor</category><category>Android</category><category>iOS</category><category>Stickers</category><category>Tech</category><category>How-To</category><category>Company</category></item><item><title>Make it pop - introducing Animated and Smart Stickers</title><link>https://img.ly/blog/smart-animated-stickers/</link><guid isPermaLink="true">https://img.ly/blog/smart-animated-stickers/</guid><description>Stickers have become an essential part of digital expression and a popular feature across use cases, from marketing campaigns and social media posts to annotation. We are happy to announce our newest feature.</description><pubDate>Thu, 13 Aug 2020 18:31:18 GMT</pubDate><content:encoded>&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Our latest release: brand-new sticker types for iOS and Android&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We are always on the lookout to improve our editor tools to engage users to be creative and add beautifully designed content.&lt;/p&gt;
&lt;p&gt;Our latest release for both mobile PhotoEditor SDK and VideoEditor SDK takes our stickers to the next level: With animated and smart stickers, we added popular features best known from apps like Instagram and TikTok – making editing more dynamic, expressive, and engaging. You can now capture your favorite moments by adding more context to your photos and videos. Maybe you finally landed that kickflip on a warm Monday morning, so you add the date and weather information. You can now also add your animated logo.  &lt;/p&gt;
&lt;p&gt;Our animated stickers not only provide you with a set of premade assets that come with the latest version of the editor but also let you integrate your custom ones tailored to your company and brand.&lt;/p&gt;
&lt;p&gt;Smart Stickers on the other hand open up a whole range of new possibilities, visualizing dynamic information like weather data, location, or dates. Like our text design feature, it’s the ease of use that makes these stickers special – simply cycle through different variations with a tap.&lt;/p&gt;
&lt;p&gt;Take a look at all updates and assets on our Mobile SDK page.    &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://photoeditorsdk.com/mobile-sdk/?utm_campaign=Projects&amp;#x26;utm_medium=Blog&amp;#x26;utm_source=Main%20Blog&amp;#x26;utm_content=Sticker%20Types&quot;&gt;&lt;img alt=&quot;Check it out!&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 150px) 150px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;150&quot; height=&quot;47&quot; src=&quot;https://img.ly/_astro/check_it_out_Z21PgLn.webp&quot; srcset=&quot;/_astro/check_it_out_Z21PgLn.webp 150w&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;PS: Do you have feedback or suggestions for new smart stickers you want to see from us? Let us know in our &lt;a href=&quot;https://sales20200811.typeform.com/to/sP6dhXMc&quot;&gt;Quick-Survey&lt;/a&gt;. We’re happy to hear from you!&lt;/p&gt;</content:encoded><dc:creator>Michael K.</dc:creator><media:content url="https://blog.img.ly/2020/08/6mb-smartsticker-animatedsticker-pesdk.gif" medium="image"/><category>iOS</category><category>Android</category><category>Video Editing</category><category>Photo Editing</category><category>SDK</category><category>Colors</category><category>Stickers</category><category>Company</category></item><item><title>React Native: Native Modules made for React developers</title><link>https://img.ly/blog/react-native-native-modules-made-for-react-developers-59ca93c41541/</link><guid isPermaLink="true">https://img.ly/blog/react-native-native-modules-made-for-react-developers-59ca93c41541/</guid><description>On the developer experience with 3rd-party libraries for RN 0.60+</description><pubDate>Thu, 20 Feb 2020 23:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;*&lt;/strong&gt;TL;DR******: How much of “native” does a React Native developer need? “Nativ”, “nati”, “nat”, “na”, “n”, or none? Version 0.60 drastically shifts this requirement towards none, even when native modules depend on external native libraries!*&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ideally, React Native (RN) should enable web developers to write React code once and thus magically create truly native Android and iOS apps without ever having to touch a single line of platform-specific native code. In reality, developers that use RN to build cross-platform apps need to be quite experienced with the respective development tool stacks and programming languages of the native platforms. This unfortunate truth is particularly the case when integrating native third-party libraries from scratch as we detailed in a &lt;a href=&quot;https://img.ly/blog/photoeditor-sdk-react-native-15179c589a55/&quot;&gt;previous blog post&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;react-native-060--a-relief-for-ios&quot;&gt;React Native 0.60+ — A relief for iOS&lt;/h2&gt;
&lt;p&gt;There are two ingredients to the 0.60 release that will save non-native developers countless hours of integration trouble and also make experienced native developers very happy.&lt;/p&gt;
&lt;p&gt;First, RN 0.60 reconsidered CocoaPods as the primary build and dependency management system for the iOS platform. Using &lt;a href=&quot;https://reactnative.dev/blog/2019/07/03/version-60#cocoapods-by-default&quot;&gt;CocoaPods is now the default&lt;/a&gt; for every new React Native app. This choice greatly simplifies the installation and integration process of native third-party frameworks.&lt;/p&gt;
&lt;p&gt;Second, &lt;a href=&quot;https://reactnative.dev/blog/2019/07/03/version-60#native-modules-are-now-autolinked&quot;&gt;autolinking native modules&lt;/a&gt; per default finally removes the need to remember the &lt;code&gt;react-native link&lt;/code&gt; command after installing any React Native library with &lt;code&gt;npm&lt;/code&gt; or &lt;code&gt;yarn&lt;/code&gt; in favor of &lt;a href=&quot;https://github.com/react-native-community/cli/blob/main/docs/autolinking.md&quot;&gt;manually issuing another command&lt;/a&gt;, namely &lt;code&gt;(cd ios &amp;#x26;&amp;#x26; pod install)&lt;/code&gt; to trigger CocoaPods. This change highlights the new integral role of CocoaPods for RN. The utmost benefit of this transition is that all native dependencies of your project are installed in a fully automatic manner. Even nested native dependencies are properly resolved and installed as one would expect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No&lt;/strong&gt; more juggling with &lt;code&gt;.framework&lt;/code&gt; bundles for third-party libraries with CocoaPods support!&lt;/p&gt;
&lt;p&gt;Eventually, even the “burden” of manually pulling the CocoaPods trigger might be gone in future RN versions…&lt;/p&gt;
&lt;p&gt;Technically, if writing headlines with Unicode characters would be a good idea, the title of this blog post should have been:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;N͟a͟t͟i͟v͟e͟ M͟o͟d͟u͟l͟e͟s͟ made for React N̶a̶t̶i̶v̶e̶ developers&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;to underline the vanishingly required attribute “native” for developers that are capable of creating stunning React Native apps by using powerful external native libraries without the native tooling hassle.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;A React Native app with 28 lines of code showcasing the customization features of the all-new React Native module for PhotoEditor SDK&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2000px) 2000px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2000&quot; height=&quot;1460&quot; src=&quot;https://img.ly/_astro/image-24_15C0Mt.webp&quot; srcset=&quot;/_astro/image-24_ZseCaJ.webp 640w, /_astro/image-24_ZDKILL.webp 750w, /_astro/image-24_1PbNnB.webp 828w, /_astro/image-24_1PhIzE.webp 1080w, /_astro/image-24_uKBpL.webp 1280w, /_astro/image-24_211IR.webp 1668w, /_astro/image-24_15C0Mt.webp 2000w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;certificate-of-happiness&quot;&gt;Certificate of happiness&lt;/h2&gt;
&lt;p&gt;Building on this strong and developer-friendly foundation of RN 0.60, we are proud to introduce the all-new React Native integrations &lt;code&gt;[react-native-photoeditorsdk](https://www.npmjs.com/package/react-native-photoeditorsdk)&lt;/code&gt; and &lt;code&gt;[react-native-videoeditorsdk](https://www.npmjs.com/package/react-native-videoeditorsdk)&lt;/code&gt; for our lineup of native SDKs — &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; and &lt;a href=&quot;https://img.ly/products/video-sdk/&quot;&gt;VideoEditor SDK&lt;/a&gt;. The interplay of both products allows you to add comprehensive image and video editing tools to your React Native app for iOS and Android — within minutes.&lt;/p&gt;
&lt;p&gt;While crafting our new modules, the primary objective was and always will be to foster their adoption by reducing the required platform-specific knowledge and skill set to a bare minimum. We are thrilled to put a wealth of &lt;a href=&quot;https://github.com/imgly/pesdk-react-native/blob/master/configuration.ts&quot;&gt;configuration and customization options&lt;/a&gt; at the developers’ finger tips without having them to leave the JavaScript tooling and programming environment. The complete &lt;a href=&quot;https://github.com/imgly/pesdk-react-native/blob/master/index.d.ts&quot;&gt;API&lt;/a&gt; is typed and thoroughly documented so that any decent source code editor will auto-import missing types and display context-sensitive quick help pop-ups alongside your code.&lt;/p&gt;
&lt;h2 id=&quot;bye-bye-native-platform-specific-asset-management&quot;&gt;Bye-bye native platform-specific asset management&lt;/h2&gt;
&lt;p&gt;Hello &lt;code&gt;require&lt;/code&gt;! Our new JavaScript API heavily relies on this well-known pseudo-keyword-like function which makes handling &lt;a href=&quot;https://archive.reactnative.dev/docs/images#static-image-resources&quot;&gt;static images&lt;/a&gt; as well as &lt;a href=&quot;https://archive.reactnative.dev/docs/images#static-non-image-resources&quot;&gt;static non-image resources&lt;/a&gt; a breeze. By using it, e.g., in your &lt;code&gt;App.js&lt;/code&gt; file, RN will do the heavy lifting and bundle all static assets with the native apps for you. The following snippet demonstrates this convenience for customizing the sticker tool (line &lt;code&gt;8&lt;/code&gt;, &lt;code&gt;11&lt;/code&gt;, &lt;code&gt;14&lt;/code&gt;) and for passing an input image to the editor (line &lt;code&gt;24&lt;/code&gt;). It presumes that the corresponding images are available in your app’s project folder.&lt;/p&gt;

&lt;h2 id=&quot;further-examples&quot;&gt;Further examples&lt;/h2&gt;
&lt;p&gt;We created two separate repositories for a &lt;a href=&quot;https://github.com/imgly/pesdk-react-native-demo&quot;&gt;photo editor example project&lt;/a&gt; and a &lt;a href=&quot;https://github.com/imgly/vesdk-react-native-demo&quot;&gt;video editor example project&lt;/a&gt;, so that our sample assets won’t increase the size of your &lt;code&gt;node_modules&lt;/code&gt; folder when installing our modules.&lt;/p&gt;
&lt;p&gt;If you are looking for a step-by-step guide that explains in detail how to supercharge your RN app with image and video editing capabilities, then stay tuned for upcoming additions to our &lt;a href=&quot;https://www.youtube.com/playlist?list=PLTchY1qyq9BvoVKpMkMWmAOII7NvWp0KS&quot;&gt;screencasts&lt;/a&gt;. You will also learn how to use advanced SDK features, such as serializing and reusing previously applied editing operations directly in your RN app.&lt;/p&gt;
&lt;h2 id=&quot;open-source-community&quot;&gt;Open-source community&lt;/h2&gt;
&lt;p&gt;We release our new RN modules that expose our native SDKs to the RN ecosystem under open-source licenses. Feedback and pull requests are welcome. The sources are also a valuable basis for bridging other cross-platform frameworks that we are currently not supporting with official integrations.&lt;/p&gt;
&lt;p&gt;*&lt;strong&gt;*Thanks for reading! To stay in the loop, subscribe to our&lt;/strong&gt; &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;&lt;strong&gt;Newsletter&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;.**&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Alexander</dc:creator><media:content url="https://blog.img.ly/2020/04/1-BON-euRy0GuMYNqdf3pgRw.jpeg" medium="image"/><category>React Native</category><category>React</category><category>iOS</category><category>Android</category><category>Photo Editing</category><category>Tech</category><category>How-To</category><category>Insights</category></item><item><title>The future is Video — the future is now!</title><link>https://img.ly/blog/the-future-is-video-the-future-is-now-f5b3911023f6/</link><guid isPermaLink="true">https://img.ly/blog/the-future-is-video-the-future-is-now-f5b3911023f6/</guid><description>Introducing VideoEditor SDK for iOS and Android</description><pubDate>Tue, 13 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://img.ly/products/video-sdk/&quot;&gt;VideoEditor SDK&lt;/a&gt; expands the powerful tools and features of &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; into the realm of mobile video creation, empowering you to seamlessly integrate a cinematic experience into your mobile applications.With an intuitive and elegant UI, an extensive filter gallery, advanced adjustment tools, and crops for social aspect ratios you’ll gift your users with the ability to create engaging and professional-looking videos on the fly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/embed/FDuwEtBf8N8?feature=oembed&quot;&gt;https://www.youtube.com/embed/FDuwEtBf8N8?feature=oembed&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Video is one of the most effective means of communication in our era. According to &lt;a href=&quot;https://blog.hubspot.com/marketing/state-of-video-marketing-new-data&quot;&gt;Hubspot&lt;/a&gt;, the usage and consumption of video content continue to grow and still haven’t reached their saturation point. Video is changing the way we interact on a daily basis, and it’s here to stay.&lt;/p&gt;
&lt;p&gt;Although most videos are shot spontaneously and then posted instantly, even slight editing can have a tremendous effect on the content’s impact. VideoEditor SDK lets your users create professional-looking footage without having to rely on external apps while using your solution. The SDK is packed with intuitive yet powerful tools that allow for the creation of an endless variety of stunning visual effects. All tools come with an instant preview and can be tailored to fit your app perfectly.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;trimming--transformation-keeping-itrelevant&quot;&gt;Trimming &amp;#x26; Transformation: Keeping it relevant&lt;/h2&gt;
&lt;p&gt;The Trimming tool helps your users to keep their content on point and to get rid of unnecessary, boring, or unwanted parts with ease. Furthermore, the SDK is equipped with a Transform tool kit that unifies cropping, flipping, and rotating operations. Featuring various preset video aspect ratios most popular for Instagram (1:1), YouTube (16:9) or other platforms like Snapchat (9:16) — the tool lets your users present their content in the most appropriate format.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;video-grading-the-hollywood-look-andfeel&quot;&gt;Video grading: The Hollywood look and feel&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://img.ly/products/video-sdk/&quot;&gt;VideoEditor SDK&lt;/a&gt; ships with over 60 video filters that let your users achieve a cinematic look for their videos with a single tap. Additionally, the Adjustment section holds a variety of handy tools to tweak and fine-tune video content ranging from basic operations like brightness and contrast to more sophisticated options like exposure and gamma. Through instant preview, your users can directly see how their changes affect the final video.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;captioning-and-assets-compelling-storytelling&quot;&gt;Captioning and Assets: Compelling storytelling&lt;/h2&gt;
&lt;p&gt;Video is a narrational medium and VideoEditor SDK provides all necessary functions for your users to quickly turn their footage into engaging and attention-grabbing stories. They can customize their videos with captions using the Text tool or add a stunning typography design with the Text Design tool that automatically merges input text with designer-grade typography layouts.&lt;/p&gt;
&lt;p&gt;On top of that, the SDK ships with various asset libraries for your users to personalize their content with Stickers, Frames, or Overlays to create something they’ll love to share.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;learn-more-about-videoeditor-sdk-and-delight-your-users-with-powerful-video-editing-capabilities&quot;&gt;Learn more about &lt;a href=&quot;https://img.ly/products/video-sdk/&quot;&gt;VideoEditor SDK&lt;/a&gt; and delight your users with powerful video editing capabilities.&lt;/h2&gt;</content:encoded><dc:creator>Felix</dc:creator><media:content url="https://blog.img.ly/2020/04/image-30.png" medium="image"/><category>Videos</category><category>iOS</category><category>Android</category><category>App Development</category><category>Video Editing</category><category>Tech</category><category>How-To</category><category>Learning</category></item><item><title>Updates to the PhotoEditor SDK</title><link>https://img.ly/blog/updates-to-the-photoeditor-sdk-7df794757979/</link><guid isPermaLink="true">https://img.ly/blog/updates-to-the-photoeditor-sdk-7df794757979/</guid><description>Color Pipette, DuoTone Filters, Folders, Snapping, Light Theme</description><pubDate>Tue, 28 May 2019 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Over the course of the last weeks, we’ve integrated a couple of handy features into the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt;. It’s now easier than ever for you to equip your service with the tools that make it the one-stop creative solution for your users.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;color-pipette&quot;&gt;Color Pipette&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 300px) 300px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;300&quot; height=&quot;636&quot; src=&quot;https://img.ly/_astro/1-GwN_5xBj2mqiOVnc8TZBiA_Zmv8Fh.webp&quot; srcset=&quot;/_astro/1-GwN_5xBj2mqiOVnc8TZBiA_Zmv8Fh.webp 300w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Colors are important for virtually every aspect of the digital world and a powerful tool when it comes to design, branding, social media and marketing. Color is one of the most important things to consider when communicating online. If used right these visual cues harmonize the impression and emphasize the message. But if used wrong, colors may also wreck design and content just as thoroughly as Comic Sans.&lt;/p&gt;
&lt;p&gt;Whether it is for creating content, ads, special offers or a brand logo, finding the right color is just too important a decision to be taken lightly. The PhotoEditor SDK’s color pipette helps your users to discover and marry their content with the right colors for any purpose.&lt;/p&gt;
&lt;p&gt;The Color Pipette is available for iOS and Android.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;duotone-filters&quot;&gt;DuoTone Filters&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 400px) 400px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;400&quot; height=&quot;500&quot; src=&quot;https://img.ly/_astro/1-n-7aDM2hWU8oEpJKzB0fsQ_Sk1AA.webp&quot; srcset=&quot;/_astro/1-n-7aDM2hWU8oEpJKzB0fsQ_Sk1AA.webp 400w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Duotone is amongst the big trends in modern web design. What once served as a way of saving money on printed materials, today, adds boldness to designs and can be used to many ends. Duotone keeps things minimalistic yet captivating. In a world of full-color imagery, duotone images are a refreshing novelty as they add visual appeal by subtracting colors.&lt;/p&gt;
&lt;p&gt;We added 8 filters to the PhotoEditor SDK’s gallery that map contrasting colors to an image based on its luminance values. Via a slider, your users can control which color the creative should lean to.&lt;/p&gt;
&lt;p&gt;DuoTone filters are available for iOS, Android, and HTML5.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;folders&quot;&gt;Folders&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 200px) 200px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;200&quot; height=&quot;400&quot; src=&quot;https://img.ly/_astro/1-iED1WxauYJPb5B2SrTFRlw_1aWfHm.webp&quot; srcset=&quot;/_astro/1-iED1WxauYJPb5B2SrTFRlw_1aWfHm.webp 200w&quot;&gt;&lt;/p&gt;
&lt;p&gt;To streamline your users’ workflow and facilitate the exploration of the PhotoEditor SDK’s vast filter library, we categorized them into 7 folders. We also exchanged all filter names for more descriptive ones.&lt;/p&gt;
&lt;p&gt;Folders are available for all platforms and completely customizable, you can rename the folders, add your own and rearrange the filters.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;snapping&quot;&gt;Snapping&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 400px) 400px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;400&quot; height=&quot;711&quot; src=&quot;https://img.ly/_astro/1-zQX5j0j67HMC3K-w16gRTQ_1ItsWj.webp&quot; srcset=&quot;/_astro/1-zQX5j0j67HMC3K-w16gRTQ_1ItsWj.webp 400w&quot;&gt;&lt;/p&gt;
&lt;p&gt;To facilitate the precise layout of creatives, we added a snapping feature to our Text, Text Design, and Sticker tool. The visual elements snap to the center and borders as well as to certain angles.&lt;/p&gt;
&lt;p&gt;You can customize the snapping properties to your wishes. Snapping is available for HTML5 iOS and Android.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;light-theme&quot;&gt;Light Theme&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 400px) 400px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;400&quot; height=&quot;577&quot; src=&quot;https://img.ly/_astro/1-1L2FsQgLNJyP5YzlHL9zOQ_12lG5X.webp&quot; srcset=&quot;/_astro/1-1L2FsQgLNJyP5YzlHL9zOQ_12lG5X.webp 400w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Rumor has it that Apple is going to introduce light and dark theming in iOS 13. With our latest update, we complemented our standard dark themed UI with a light counterpart. Without any customization efforts, you can switch from dark to light theming with a single line of code.&lt;/p&gt;
&lt;p&gt;Light theme is available for iOS and Android. For more on that, please check out our documentation.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;if-you-think-that-the-photoeditor-sdk-would-be-the-right-fit-for-your-project-let-us-know-and-well-be-intouch&quot;&gt;If you think that the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; would be the right fit for your project, let us know and we’ll be in touch.&lt;/h2&gt;
&lt;p&gt;*&lt;strong&gt;*Thanks for reading! To stay in the loop, subscribe to our&lt;/strong&gt; &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;&lt;strong&gt;Newsletter&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;.**&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Felix</dc:creator><media:content url="https://blog.img.ly/downloaded_images/Updates-to-the-PhotoEditor-SDK/1-6e8ra3u3F139pgLKSRMBEQ.png" medium="image"/><category>Design</category><category>Colors</category><category>Photo Editing</category><category>iOS</category><category>Android</category><category>Tech</category><category>How-To</category><category>Company</category></item><item><title>How to integrate a Photo Editor into your Android app</title><link>https://img.ly/blog/how-to-integrate-a-photo-editor-into-your-android-app-ee4148816e25/</link><guid isPermaLink="true">https://img.ly/blog/how-to-integrate-a-photo-editor-into-your-android-app-ee4148816e25/</guid><description>6-minute Video Tutorial </description><pubDate>Tue, 11 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;As developer friendliness is paramount to us, we created a short video tutorial to guide you through our integration process. We created similar tutorials for &lt;a href=&quot;https://img.ly/blog/how-to-integrate-a-photo-editor-into-your-ios-app-ced008a7088b/&quot;&gt;iOS&lt;/a&gt; and &lt;a href=&quot;https://img.ly/blog/how-to-integrate-a-photo-editor-into-your-website-4578cc6ef6f3/&quot;&gt;HTML5&lt;/a&gt;, so make sure to check those out as well.(Please make sure to get a trial license for the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; before integrating it.)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/embed/FpmASFvgUdM?feature=oembed&quot;&gt;https://www.youtube.com/embed/FpmASFvgUdM?feature=oembed&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;video-transcript&quot;&gt;Video Transcript&lt;/h3&gt;
&lt;p&gt;Today we’re going to show you how to integrate the PhotoEditor SDK into your Android app. We’re going to need Android Studio and the PhotoEditor SDK’s &lt;a href=&quot;https://img.ly/docs/pesdk/android/getting-started/integration/&quot;&gt;documentation&lt;/a&gt; to do so.&lt;/p&gt;
&lt;p&gt;First, we’re going to launch a new Android Studio project. Here, we can choose a name for our application. Then, we’re going to insert our company domain and our package name. Inserting a package name is very important as you need a license that is issued for your specific package name to run the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We’re also going to include Kotlin support, as every modern Android app should do that.&lt;/p&gt;
&lt;p&gt;We then choose Phone and Tablet and API 16 because this is the oldest API that the SDK currently supports. Please note, that this is eventually going to change.&lt;/p&gt;
&lt;p&gt;We then choose Empty Activity. Here you can set a name. For this tutorial, however, we’re going to leave it as it is.&lt;/p&gt;
&lt;p&gt;Now, a new Android Studio project is created.&lt;/p&gt;
&lt;p&gt;For the sake of clarity, we’re going to use the project view.&lt;/p&gt;
&lt;p&gt;First, we have to include the license. To do so, we open the &lt;code&gt;PhotoeditorApp&lt;/code&gt; folder and navigate through the subfolders &lt;code&gt;app&lt;/code&gt;, &lt;code&gt;src&lt;/code&gt; and &lt;code&gt;main&lt;/code&gt;. In this subfolder, we create yet another folder called &lt;code&gt;assets&lt;/code&gt;. We then simply paste our license into it.&lt;/p&gt;
&lt;p&gt;We head over to the documentation, find the section for Android and make sure that the latest version is selected. As of the time of this recording that is version 6 or V6. Then we navigate to the “Getting Started” section. Here you can also look up how to include the license. We head over to the project’s &lt;code&gt;build.gradle&lt;/code&gt; file and paste this line which is the path to our SDK repository under &lt;code&gt;buildscript&lt;/code&gt; and &lt;code&gt;repositories&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;maven { url &apos;https://artifactory.img.ly/artifactory/imgly&apos; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We then copy this line and paste it under dependencies, and thus, we have linked the SDK.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;classpath &apos;ly.img.android.pesdk:plugin:6.0.8&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we’re going to apply the PESDK plugin. So, we find the app’s build.gradle folder and paste this line under the other plugins.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;apply plugin: &apos;ly.img.android.pesdk&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we’re going to insert the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt;’s configuration that contains all tools. We copy this block and paste it under the PESDK plugin. It is important that the license naming here matches the name of the license file we previously imported.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;pesdkConfig {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    licencePath &quot;LICENSE&quot; // Name of the Licence file in the asset folder&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // If you use another supportLibVersion (&apos;com.android.support&apos;), change this version here to update your own supportLibVersion&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    supportLibVersion &quot;27.1.1&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // Define the modules you need&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    modules {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // Add all the backend modules you need&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.operation:text&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.operation:frame&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.operation:focus&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.operation:brush&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.operation:camera&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.operation:filter&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.operation:sticker&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.operation:overlay&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.operation:adjustment&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.operation:text-design&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.operation:abstract-sticker&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // Add all the UI modules you need&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.ui.mobile_ui:core&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.ui.mobile_ui:text&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.ui.mobile_ui:focus&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.ui.mobile_ui:frame&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.ui.mobile_ui:brush&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.ui.mobile_ui:filter&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.ui.mobile_ui:camera&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.ui.mobile_ui:sticker&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.ui.mobile_ui:overlay&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.ui.mobile_ui:transform&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.ui.mobile_ui:adjustment&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.ui.mobile_ui:text-design&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // Add the serializer if you want&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk:serializer&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // Add asset packs if you want&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.assets:font-basic&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.assets:font-text-design&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.assets:frame-basic&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.assets:filter-basic&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.assets:overlay-basic&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.assets:sticker-shapes&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        include &apos;ly.img.android.pesdk.assets:sticker-emoticons&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we copy the &lt;code&gt;buildToolsVersion&lt;/code&gt; und paste it in the &lt;code&gt;android&lt;/code&gt; category. And for the app to compile in the correct Java version, we copy this code block and paste it at the end of the &lt;code&gt;android&lt;/code&gt; category.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;compileOptions {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        sourceCompatibility JavaVersion.VERSION_1_8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        targetCompatibility JavaVersion.VERSION_1_8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We hit “Sync Now” to save our settings. That often takes a while, so we sped it up for you.&lt;/p&gt;
&lt;p&gt;We want to build a simple application that just consists of a single button that starts the photo editor. So first, we create a button. To do so, we navigate to the &lt;code&gt;app&lt;/code&gt; folder and open the subfolders &lt;code&gt;src&lt;/code&gt;, &lt;code&gt;res&lt;/code&gt; and &lt;code&gt;layout&lt;/code&gt; to find the &lt;code&gt;activity_main&lt;/code&gt; (or whichever way you decided to name it). We click on “Text” to edit the file and add a button. For height and width, we enter &lt;code&gt;wrap_content&lt;/code&gt;. Now, we set an ID for the button and call it &lt;code&gt;sdk_button&lt;/code&gt;. We then set a display text for the button. For this tutorial, we’ll use the string &lt;code&gt;app_name&lt;/code&gt;, but you, of course, can choose whichever string you like.&lt;/p&gt;
&lt;p&gt;We return to the &lt;code&gt;app&lt;/code&gt; folder and find the &lt;code&gt;MainActivity&lt;/code&gt; in the subfolders &lt;code&gt;src&lt;/code&gt; and &lt;code&gt;java&lt;/code&gt;. We head over to the documentation, where we are presented with two options. We can either start the editor in camera mode or in the device’s gallery. We’ll go with the camera mode. As we’re writing our &lt;code&gt;MainActivity&lt;/code&gt; in Kotlin, we choose the Kotlin code example and paste it in the &lt;code&gt;MainActivity&lt;/code&gt;. We can overwrite the &lt;code&gt;onCreate&lt;/code&gt; as it is also included in the code example.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;companion object {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        const val PESDK_RESULT = 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // Important permission request for Android 6.0 and above, don&apos;t forget to add this!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array&amp;#x3C;String&gt;, grantResults: IntArray) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        PermissionRequest.onRequestPermissionsResult(requestCode, permissions, grantResults)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        super.onRequestPermissionsResult(requestCode, permissions, grantResults)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    override fun permissionGranted() {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    override fun permissionDenied() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        /* TODO: The Permission was rejected by the user. The Editor was not opened,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;         * Show a hint to the user and try again. */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // Create an empty new SettingsList and apply the changes on this reference.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    private fun createPesdkSettingsList() = SettingsList().apply {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // If you include our asset packs and use our UI you also have to add them to the UI,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // otherwise, they are only available for the backend&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // See the specific feature sections of our guides if you want to know how to add your own Assets.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        getSettingsModel(UiConfigFilter::class.java).apply {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            setFilterList(FilterPackBasic.getFilterPack())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        getSettingsModel(UiConfigText::class.java).apply {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            setFontList(FontPackBasic.getFontPack())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        getSettingsModel(UiConfigFrame::class.java).apply {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            setFrameList(FramePackBasic.getFramePack())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        getSettingsModel(UiConfigOverlay::class.java).apply {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            setOverlayList(OverlayPackBasic.getOverlayPack())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        getSettingsModel(UiConfigSticker::class.java).apply {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            setStickerLists(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              StickerPackEmoticons.getStickerCategory(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;              StickerPackShapes.getStickerCategory()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // Set custom camera image export settings&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        getSettingsModel(CameraSettings::class.java).apply {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            setExportDir(Directory.DCIM, &quot;SomeFolderName&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            setExportPrefix(&quot;camera_&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        // Set custom editor image export settings&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        getSettingsModel(EditorSaveSettings::class.java).apply{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            setExportDir(Directory.DCIM, &quot;SomeFolderName&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            setExportPrefix(&quot;result_&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            savePolicy = EditorSaveSettings.SavePolicy.RETURN_ALWAYS_ONLY_OUTPUT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    override fun onCreate(savedInstanceState: Bundle?) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        super.onCreate(savedInstanceState)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        setContentView(R.layout.activity_main)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        openCamera()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    private fun openCamera() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        val settingsList = createPesdkSettingsList()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        CameraPreviewBuilder(this)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          .setSettingsList(settingsList)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;          .startActivityForResult(this, PESDK_RESULT)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    override fun onActivityResult(requestCode: Int, resultCode: Int, data: android.content.Intent) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        super.onActivityResult(requestCode, resultCode, data)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        if (resultCode == Activity.RESULT_OK &amp;#x26;&amp;#x26; requestCode == PESDK_RESULT) { // Editor has saved an Image.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            val resultURI = data.getParcelableExtra&amp;#x3C;Uri?&gt;(ImgLyIntent.RESULT_IMAGE_URI)?.also {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                // Scan result uri to show it up in the Gallery&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE).setData(it))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            val sourceURI = data.getParcelableExtra&amp;#x3C;Uri?&gt;(ImgLyIntent.SOURCE_IMAGE_URI)?.also {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                // Scan source uri to show it up in the Gallery&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE).setData(it))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            Log.i(&quot;PESDK&quot;, &quot;Source image is located here $sourceURI&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            Log.i(&quot;PESDK&quot;, &quot;Result image is located here $resultURI&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            // TODO: Do something with the result image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            // OPTIONAL: read the latest state to save it as a serialisation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            val lastState = data.getParcelableExtra&amp;#x3C;SettingsList&gt;(ImgLyIntent.SETTINGS_LIST)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            try {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                PESDKFileWriter(lastState).writeJson(File(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                  Environment.getExternalStorageDirectory(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                  &quot;serialisationReadyToReadWithPESDKFileReader.json&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                ))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            } catch (e: IOException) { e.printStackTrace() }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        } else if (resultCode == Activity.RESULT_CANCELED &amp;#x26;&amp;#x26; requestCode == PESDK_RESULT) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            // Editor was canceled&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            val sourceURI = data.getParcelableExtra&amp;#x3C;Uri?&gt;(ImgLyIntent.SOURCE_IMAGE_URI)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;            // TODO: Do something...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Right now, we are presented with quite some error warnings, but that’s no problem at all because we still need to implement the interface, as you can see here: &lt;code&gt;PermissionRequest.Response&lt;/code&gt;. Here, we must choose the dedicated &lt;code&gt;ly.img.android.pesdk.ui.utils&lt;/code&gt; and add &lt;code&gt;.response&lt;/code&gt; at the end.&lt;/p&gt;
&lt;p&gt;After that, we’re down to only two errors because we still need to integrate the correct directory. Again, we choose the dedicated &lt;code&gt;ly.img.android.pesdk.backend.model.constant&lt;/code&gt;. And thus all errors are resolved.&lt;/p&gt;
&lt;p&gt;If we were to start the app now it would automatically run the editor in camera mode. But we don’t want the editor to start automatically, hence the button we previously added. So, in order to change that, we scroll to the onCreate method and as you can see here, the app would instantly open the camera.&lt;/p&gt;
&lt;p&gt;We create a new variable that we’re going to call &lt;code&gt;sdkButton&lt;/code&gt; and set it equal with &lt;code&gt;findViewbyId&lt;/code&gt;. We then set the ID to &lt;code&gt;sdk_button&lt;/code&gt;, set an &lt;code&gt;OnClickListener&lt;/code&gt; and paste &lt;code&gt;openCamera&lt;/code&gt; beneath it.&lt;/p&gt;
&lt;p&gt;And, we have a working little app that contains the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt;. You could take a deeper dive into PhotoEditor SDK integration in other environments and frameworks in the official &lt;a href=&quot;https://img.ly/docs/pesdk/&quot;&gt;documentation&lt;/a&gt; put together by developers themselves.&lt;/p&gt;
&lt;p&gt;*&lt;strong&gt;*Thanks for reading! To stay in the loop, subscribe to our&lt;/strong&gt; &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;&lt;strong&gt;Newsletter&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;.**&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Felix</dc:creator><media:content url="https://blog.img.ly/2020/04/image-33.png" medium="image"/><category>App Development</category><category>Android</category><category>Tutorial</category><category>Technology</category><category>Photo Editing</category><category>Tech</category><category>How-To</category><category>Learning</category></item><item><title>Text ❤ Design [Pt. II]</title><link>https://img.ly/blog/text-design-pt-ii-97ce7b752147/</link><guid isPermaLink="true">https://img.ly/blog/text-design-pt-ii-97ce7b752147/</guid><description>Let images do the talking </description><pubDate>Wed, 28 Nov 2018 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;After giving you a sneak peek at the Text Design tool in a previous &lt;a href=&quot;https://img.ly/blog/text-design-ef84fe708d02/&quot;&gt;blog post&lt;/a&gt;, we can now finally announce that it is available as a part of the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; for all platforms. Over the last weeks, we finalized the Text Design tool and added functionalities that make it a handy creative tool for many industries and use cases.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One of our main goals is to make good design more accessible to everyone by developing technology that facilitates and streamlines creative processes so that even people without previous knowledge of sophisticated editing tools can yield appealing results. The Text Design tool enables you to provide your users with the power and features necessary to give speech to their ideas and create a narrative for their visual communication.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 750px) 750px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;750&quot; height=&quot;370&quot; src=&quot;https://img.ly/_astro/1-ftHB1QK5wuwkHg8PRoSHHw_T8Eqv.webp&quot; srcset=&quot;/_astro/1-ftHB1QK5wuwkHg8PRoSHHw_1eqsdy.webp 640w, /_astro/1-ftHB1QK5wuwkHg8PRoSHHw_T8Eqv.webp 750w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Attention-grabbing and precise visual communication is key to engaging one’s target audience. Our novel tool automates typesetting and lets your users combine photos and text fast and easy to create stunning imagery for any tone and task. The Text Design tool features complex text layouts based on recipes crafted by professional designers. Their balanced combination of fonts, sizes, alignments, and decorations allow for the fast creation of assets with a designer look for various purposes like social media, online ads, short-term offers, flyers, product images, posters, promo material and so on. Especially for small companies and teams, without the budget for a professional designer or a dedicated design department, the Text Design Tool can save a lot of time and stress by reducing what would usually take hours to a single tap.&lt;/p&gt;
&lt;h2 id=&quot;all-it-takes-is-an-idea-the-text-design-tool-tends-to-90-of-the-execution&quot;&gt;All it takes is an idea. The Text Design Tool tends to 90% of the execution.&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 300px) 300px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;300&quot; height=&quot;634&quot; src=&quot;https://img.ly/_astro/1-RY7-Zm8SSkyjE4kBKygPnw_Z1f5tJI.webp&quot; srcset=&quot;/_astro/1-RY7-Zm8SSkyjE4kBKygPnw_Z1f5tJI.webp 300w&quot;&gt;&lt;/p&gt;
&lt;p&gt;The tool makes design just as fast as your users’ ideas. Combined with the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK’s&lt;/a&gt; extensive feature suite, branding opportunities through sticker upload and the photo roll with endless source material, your users can achieve a harmonious and nuanced visual communication off the bat to stand out against their competitors. The designs help convey your users’ message and can be used to create a textural hierarchy to guide the spectators view to the essential infos for your users’ goals. Furthermore, the shuffle functionality randomizes fonts, alignments, and decorations thus offering endless possibilities for creative expression.&lt;/p&gt;
&lt;p&gt;A recent change we made to the mobile versions of the Text Design tool is lifting the limitation of one design per creative to allow further artistic expression and get the most out of the interplay and combination of the various designs. On top of that, we optimized the inverting feature of the tool to allow for the adjustment of the inverted designs’ padding and size.&lt;/p&gt;
&lt;h2 id=&quot;if-you-want-to-empower-your-users-to-give-a-voice-to-their-creatives-head-over-to-imgly-and-get-in-touch-with-our-salesteam&quot;&gt;If you want to empower your users to give a voice to their creatives head over to &lt;a href=&quot;https://img.ly/&quot;&gt;IMG.LY&lt;/a&gt; and get in touch with our sales team.&lt;/h2&gt;
&lt;p&gt;*&lt;strong&gt;*Thanks for reading! To stay in the loop, subscribe to our&lt;/strong&gt; &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;&lt;strong&gt;Newsletter&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;.**&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Felix</dc:creator><media:content url="https://blog.img.ly/downloaded_images/Text---Design--Pt--II-/1-a-fU1Z1hZwEV1xctiTudcA.png" medium="image"/><category>Design</category><category>Typography</category><category>Android</category><category>iOS</category><category>App Development</category><category>Web Development</category><category>Insights</category></item><item><title>How to recreate Snapchat’s UI within a single day with the PhotoEditor SDK for Android</title><link>https://img.ly/blog/how-to-recreate-snapchats-ui-within-a-single-day-with-the-photoeditor-sdk-for-android-e985b348c52c/</link><guid isPermaLink="true">https://img.ly/blog/how-to-recreate-snapchats-ui-within-a-single-day-with-the-photoeditor-sdk-for-android-e985b348c52c/</guid><pubDate>Sun, 18 Feb 2018 23:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Is it possible to completely rejig the UI of the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; within a day? Principally, everything is possible, however, for me as a prospective Java developer this whole undertaking seemed rather tough at first. The task at hand was to rebuild the SDK’s UI to match popular apps like Snapchat or Instagram Stories in only a few steps. A few weeks ago, we already did something similar with the iOS SDK, however, as there are some differences between the platforms the whole process had to be repeated for Android.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Our default UI vs Story UI&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 900px) 900px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;900&quot; height=&quot;600&quot; src=&quot;https://img.ly/_astro/image-13_Z155gLz.webp&quot; srcset=&quot;/_astro/image-13_SB6P9.webp 640w, /_astro/image-13_Z1YEFsp.webp 750w, /_astro/image-13_16X6CG.webp 828w, /_astro/image-13_Z155gLz.webp 900w&quot;&gt;&lt;/p&gt;
&lt;p&gt;As my colleague Malte Baumann already pointed out in his &lt;a href=&quot;https://img.ly/blog/how-to-build-instagrams-story-editor-in-a-day-23be9adff9b/&quot;&gt;blog post&lt;/a&gt;, the main idea behind this endeavor is to explore the possibilities and limitations of the SDK’s customizable UI. But above that, it is also a great way of making oneself familiar with the SDK’s architecture and functionalities. Below, I’m going to explain some of the technical aspects as well as the setup of the story UI in a little more detail.&lt;/p&gt;
&lt;h2 id=&quot;the-architecture&quot;&gt;The Architecture&lt;/h2&gt;
&lt;p&gt;The interface is composed of several layers. The first layer is the &lt;code&gt;EditorRootView&lt;/code&gt; that contains all other views and is, therefore, their parent object. Beneath that lies the &lt;code&gt;EditorPreview&lt;/code&gt; that is responsible for the display of the selected image. Following comes the &lt;code&gt;BrushLayer&lt;/code&gt; that can be drawn upon. In this specific layer array, the brush cannot be placed in front of a sticker. The stickers, however, are all generated in dedicated layers which makes it easy to adjust their order. Once selected, a sticker will automatically be brought to the front. The UI elements can be switched visible and invisible at will; they lie at the very top so that no sticker can cover them.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;The view hierarchy. The user facing controls are on the bottom, the root window is on the top.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 473px) 473px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;473&quot; height=&quot;579&quot; src=&quot;https://img.ly/_astro/image-14_Z35Yqi.webp&quot; srcset=&quot;/_astro/image-14_Z35Yqi.webp 473w&quot;&gt;&lt;/p&gt;
&lt;p&gt;At the heart of the UI lies the &lt;code&gt;StateHandler&lt;/code&gt; that functions as the communication interface with the SDK. The &lt;code&gt;StateHandler&lt;/code&gt; includes the &lt;code&gt;EditorShowState&lt;/code&gt; and the &lt;code&gt;HistoryState&lt;/code&gt;. The &lt;code&gt;HistoryState&lt;/code&gt; allows for the retrieval of methods that are necessary for the function of the “undo” button. For example, every new brush stroke adds a new memory state that can be retrieved by the &lt;code&gt;HistoryState&lt;/code&gt; and hence reversed. The &lt;code&gt;EditorShowState&lt;/code&gt; manages the &lt;code&gt;EditMode&lt;/code&gt; that allows for the activation of the different SDK tools. To draw with the brush tool, the &lt;code&gt;EditMode&lt;/code&gt; simply has to be set to brush. Color and stroke width are then controlled by the &lt;code&gt;BrushLayerSettings&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There is a dedicated button to add emojis that displays a &lt;code&gt;RecyclerView&lt;/code&gt; filled with every sticker available in the config upon press. Once a sticker is selected a new layer with matching &lt;code&gt;StickerLayerSettings&lt;/code&gt; is added to the image.&lt;/p&gt;
&lt;p&gt;Although text is realized as a sticker as well within the SDK, it is a little more complicated to generate a text sticker as the required &lt;code&gt;TextStickerConfig&lt;/code&gt; has to be generated first. The text tool can create text stickers through keyboard entries. The text and background color, as well as the text alignment, can be adjusted separately. Those functions can be controlled via different buttons. However, as the SDK has no canned methods for these operations, I had to write widgets that control the buttons. The different button states are interpreted and executed in the &lt;code&gt;TextPanel&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To export an image, you simply have to launch the &lt;code&gt;saveImage&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;The customized Story UI in action.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 300px) 300px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;300&quot; height=&quot;581&quot; src=&quot;https://img.ly/_astro/1-o1meOAWBo6YIzb_xRishgQ_O6YP6.webp&quot; srcset=&quot;/_astro/1-o1meOAWBo6YIzb_xRishgQ_O6YP6.webp 300w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-result&quot;&gt;The Result&lt;/h2&gt;
&lt;p&gt;Thanks to the documentation, the execution of the UI revamp was pretty straightforward even without previous knowledge or source code access. As main functionalities like the brush tool or the undo function are already built into the SDK, it was a manageable task to integrate those into the new UI. Against that, the styling of the &lt;code&gt;SeekBar&lt;/code&gt; that is needed to adjust the brush size was a little more time-consuming.&lt;/p&gt;
&lt;p&gt;The design was mainly realized through layouts with fairly simple code. Even though there were some minor hurdles to overcome, the allotted time of a working day should be adequate for a more experienced developer.&lt;/p&gt;
&lt;p&gt;You can find the code for this example UI in &lt;a href=&quot;https://github.com/imgly/pesdk-blog-instagram-ui&quot;&gt;our repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks to &lt;a href=&quot;https://medium.com/@Phoenix_Raw&quot;&gt;Felix Rau&lt;/a&gt; and &lt;a href=&quot;https://medium.com/@codingdivision&quot;&gt;Malte Baumann&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! To stay in the loop, subscribe to our &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;Newsletter&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Niklas</dc:creator><media:content url="https://blog.img.ly/2020/04/image-46.png" medium="image"/><category>iOS</category><category>Android</category><category>Photo Editor</category><category>User Interface</category><category>JavaScript</category><category>Tech</category><category>How-To</category><category>Learning</category></item><item><title>PhotoEditor SDK + Cordova &amp; Ionic</title><link>https://img.ly/blog/photoeditor-sdk-cordova-dabe146e6c13/</link><guid isPermaLink="true">https://img.ly/blog/photoeditor-sdk-cordova-dabe146e6c13/</guid><pubDate>Wed, 14 Jun 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Despite the growing trend towards React Native, Cordova (formerly PhoneGap) is still going strong and creates frequent plugin requests from our customers. Although we can’t offer full support, we set out to create a demo app and put together a guide on how to easily set up the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; with Cordova. And as Ionic is built on top of Cordova, the finished plugin is usable in Ionic as well.&lt;/p&gt;
&lt;p&gt;The idea is to create a Cordova app that allows the user to open an image from their photo library and edit this image using the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; on iOS and Android. This is done by using an existing library to access native photo pickers on both platforms and passing the path to the found image to a plugin. The plugin manages the configuration and opening of the PhotoEditor SDK’s editor and passes the edited image back to Cordova.&lt;/p&gt;
&lt;p&gt;In this article we’ll start with the basic setup of the plugin and example app code, explain the plugin code in depth and cover how to integrate the plugin into your app. All code is available in the corresponding &lt;a href=&quot;https://github.com/imgly/pesdk-cordova-demo&quot;&gt;GitHub repository&lt;/a&gt; and after reading this article, you’ll be able to add a the PhotoEditor SDK to your Cordova app, customize it to your needs and delight your customers.&lt;/p&gt;
&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;
&lt;p&gt;As we’ll have to build a Cordova plugin that wraps the PhotoEditor SDK into a JavaScript interface, we start with creating the following folder structure within our project:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;demo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- src&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- example&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;src&lt;/code&gt; folder contains the actual Cordova plugin consisting of JavaScript, Objective-C and Java code, while the &lt;code&gt;example&lt;/code&gt; folder contains a conventional Cordova project that uses our plugin to open the PhotoEditor SDK and manages the image selection from the users library.&lt;/p&gt;
&lt;h2 id=&quot;the-demoapp&quot;&gt;The demo app&lt;/h2&gt;
&lt;p&gt;To start with our demo, we just use the &lt;code&gt;cordova create example&lt;/code&gt; command, to create a new Cordova application. We add our target platforms, iOS and Android, using the &lt;code&gt;cordova platform add &amp;#x3C;platform&gt;&lt;/code&gt; command and are ready to head into actual coding.&lt;/p&gt;
&lt;p&gt;Due to the focus on integrating the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; in this blog post, we’ll skip the details on how to open the native image pickers and dive directly into plugin development. For now we’re just left with the following snippet in our example app, that’s supposed to open the PhotoEditor SDK at some point in the future:&lt;/p&gt;

&lt;h2 id=&quot;creating-a-cordovaplugin&quot;&gt;Creating a Cordova Plugin&lt;/h2&gt;
&lt;p&gt;Any Cordova plugin starts with a &lt;code&gt;plugin.xml&lt;/code&gt; manifest file and then covers the supported platforms in separate files. All these files get wrapped in a JavaScript interface, that exposes the functionality to Cordova. The bridging between JavaScript and the native source files is managed by Cordova and made possible by subclassing &lt;code&gt;CDVPlugin&lt;/code&gt; on iOS and &lt;code&gt;CordovaPlugin&lt;/code&gt; on Android.&lt;/p&gt;
&lt;h3 id=&quot;plugin-configuration&quot;&gt;Plugin Configuration&lt;/h3&gt;
&lt;p&gt;Our &lt;code&gt;plugin.xml&lt;/code&gt; becomes quite long due to the involved platforms, so we’ll just &lt;a href=&quot;https://gist.github.com/imgly-gists/3cb6ffd562471008e3bb82c57480716b&quot;&gt;link it&lt;/a&gt; here and explain some specific details.&lt;/p&gt;
&lt;p&gt;The first lines configure the plugins name, version etc. and define the main pesdk.js file containing the interface we’ll later call from our Cordova application. The &lt;code&gt;&amp;#x3C;clobbers&gt;&lt;/code&gt; tag specifies the namespace under which the exports are later available in the &lt;code&gt;window&lt;/code&gt; object.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;js-module src=&quot;src/www/pesdk.js&quot; name=&quot;PESDK&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;#x3C;clobbers target=&quot;PESDK&quot; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;/js-module&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The file then defines the plugin details for both platforms. Starting with Android, we list the parameters the plugin will add to the &lt;code&gt;config.xml&lt;/code&gt; and &lt;code&gt;AndroidManifest.xml&lt;/code&gt; files. These specify the Java package, when the plugin should load, register activities etc. The remaining lines for Android embed a custom &lt;code&gt;build.gradle&lt;/code&gt; file, ensuring all features are enabled for the PhotoEditor SDK and finally copy our source files &lt;code&gt;PESDKPlugin.java&lt;/code&gt; and &lt;code&gt;CameraActivity.java&lt;/code&gt; into the application folder.&lt;/p&gt;
&lt;p&gt;Moving on with iOS, which behaves a little more picky in this case, we repeat the initial Android steps like defining the package, copying files and linking all frameworks required by the plugin. The last embedded framework is the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;framework src=&quot;src/ios/PhotoEditorSDK.framework&quot; custom=&quot;true&quot; embed=&quot;true&quot; /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sadly Cordova doesn’t correctly handle the embedding of our SDK, as it doesn’t add the SDK to the embedded binaries tab in the Xcode project. Therefore we adapted a &lt;code&gt;resolveDependencies.js&lt;/code&gt; script from &lt;a href=&quot;https://stackoverflow.com/questions/36650522/custom-cordova-plugin-add-framework-to-embedded-binaries&quot;&gt;StackOverflow&lt;/a&gt; that uses &lt;code&gt;node-xcode&lt;/code&gt; to configure the project correctly during plugin installation. This script is saved in the &lt;code&gt;/hooks/resolveDependencies.js&lt;/code&gt; file and called using specific hooks:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;hook type=&quot;after_platform_add&quot; src=&quot;hooks/resolveDependencies.js&quot; /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last iOS specific step is setting two keys in the applications &lt;code&gt;Info.plist&lt;/code&gt; file that describe our camera and photo library usage when asking the user for access permissions. In this example we won’t actually be requesting these permissions from the PhotoEditor SDK, but when using the integrated camera implementation without these keys, the plugin would never gain access to the iOS camera and photo roll:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;config-file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  parent=&quot;NSCameraUsageDescription&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  target=&quot;*-Info.plist&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;#x3C;string&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    Uses your camera to snap pictures.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;#x3C;/string&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;/config-file&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;config-file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  parent=&quot;NSPhotoLibraryUsageDescription&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  target=&quot;*-Info.plist&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;#x3C;string&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    Accesses your photo library to save and open pictures.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;#x3C;/string&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;/config-file&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this &lt;code&gt;plugin.xml&lt;/code&gt; file in place, we can actually start implementing our plugin for iOS and Android.&lt;/p&gt;
&lt;h3 id=&quot;platform-specific-implementations&quot;&gt;Platform Specific Implementations&lt;/h3&gt;
&lt;p&gt;As we fiddled around with iOS last, we’ll start with the iOS specific code. The &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; is written in Swift, but Cordova prefers Objective-C instead. We could write our plugin in Swift as well and add an Objective-C wrapper, but for the sake of simplicity we’ll just stick with Objective-C for our plugin, as the PhotoEditor SDK is &lt;a href=&quot;https://img.ly/docs/pesdk/ios/getting-started/integration/swift-package-manager/&quot;&gt;fully usable&lt;/a&gt; from Objective-C anyway.&lt;/p&gt;
&lt;p&gt;We need to add three files to our &lt;code&gt;ios&lt;/code&gt; directory: The framework itself and our plugin classes header and implementation files &lt;code&gt;PESDKPlugin.h&lt;/code&gt; &amp;#x26; &lt;code&gt;PESDKPlugin.m&lt;/code&gt;. The plugin exposes a single method, &lt;code&gt;present:&lt;/code&gt;, taking a filepath as a simple string. The method then loads the image at the given path, configures the PhotoEditor SDK and creates and presents a &lt;code&gt;PESDKPhotoEditViewController&lt;/code&gt; that takes the image:&lt;/p&gt;

&lt;p&gt;To ensure the SDK is fully loaded and unlocked, we need to add a small &lt;code&gt;initialize&lt;/code&gt; method as well. And as you can see, we need to add our iOS license as well. This has to be done by opening the Xcode project and dragging the license file into the sidebar, as it won’t be copied into the app bundle otherwise. Once this is done, we can initialize the SDK in the following way:&lt;/p&gt;

&lt;p&gt;These methods contain all the native Objective-C code you need in order to open the PhotoEditor SDK from a Cordova plugin. Except for some boilerplate code for bridging between JavaScript and Objective-C. If you want to configure the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK’s&lt;/a&gt; style or available functionality you may modify the &lt;code&gt;configuration&lt;/code&gt; object according to our &lt;a href=&quot;https://img.ly/docs/pesdk/ios/getting-started/integration/swift-package-manager/&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Moving on with Android, we create a &lt;code&gt;build.gradle&lt;/code&gt; according to our &lt;a href=&quot;https://img.ly/docs/pesdk/&quot;&gt;docs&lt;/a&gt; and the &lt;code&gt;PESDKPlugin.java&lt;/code&gt; file that will contain our plugin code. Once again the native code boils down to opening the PhotoEditor SDK and passing a file path and some boiler plate code required by Cordova:&lt;/p&gt;

&lt;p&gt;The code extracts the file path we passed from Cordova and prepares the PhotoEditor SDKs editor activity using a &lt;code&gt;PhotoEditorBuilder&lt;/code&gt;. Important, but easily overseen parts are saving the &lt;code&gt;callbackContext&lt;/code&gt; for notifying Cordova when the editor closes in line 2 and registering the plugin for activity callbacks in line 18. As we did on iOS, we need to initialize the SDK and pass our license file, but in this case we can just copy the file to the &lt;code&gt;example/platforms/android/assets&lt;/code&gt; folder:&lt;/p&gt;

&lt;hr&gt;
&lt;p&gt;To finish up, we now need to define an interface using JavaScript. This interface defines the methods available in the plugin and will later be called from our Cordova application. Cordova then handles the delegation to the native implementations for us. In our case, the interface is rather minimal and only consists of our &lt;code&gt;present&lt;/code&gt; method:&lt;/p&gt;

&lt;p&gt;And thats it! We now have successfully created a Cordova plugin, which will allow us to open the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; editor from our own application, while passing an already existing image. Now we just need to wire everything in order to get our final result, the PhotoEditor SDK running on both platforms:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;The PhotoEditor SDK running on iOS and Android.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 630px) 630px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;630&quot; height=&quot;600&quot; src=&quot;https://img.ly/_astro/1-pt26bZ-jVXzyHkWsZGm5cA_vBxjz.webp&quot; srcset=&quot;/_astro/1-pt26bZ-jVXzyHkWsZGm5cA_vBxjz.webp 630w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;wiring-it-allup&quot;&gt;Wiring it all up&lt;/h2&gt;
&lt;p&gt;We’re now ready to put our fresh plugin to good use. We use the &lt;a href=&quot;https://github.com/danielehrhardt/cordova-image-picker&quot;&gt;&lt;code&gt;cordova-image-picker&lt;/code&gt;&lt;/a&gt; plugin to let our users pick an image from their photo library and pass the path of the image to our plugin. To add the plugin to our example project, we just need to call &lt;code&gt;cordova plugin add .. --link&lt;/code&gt;. This will use the &lt;code&gt;plugin.xml&lt;/code&gt; file to add the plugin and its files to our example project. Now we just need to replace the &lt;code&gt;TODO&lt;/code&gt; from the beginning of this article, with the following:&lt;/p&gt;

&lt;p&gt;This looks a little crazy at first, but doesn’t do anything magical: We first use the &lt;code&gt;imagePicker&lt;/code&gt; object to open the native image pickers and take the first image path from the returned results array. We then use the &lt;code&gt;present:&lt;/code&gt; method of our &lt;code&gt;PESDK&lt;/code&gt; plugin to open the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; and pass the found image path. Everything else is handling errors and returns by presenting them using a conventional JavaScript alert.&lt;/p&gt;
&lt;p&gt;When called, the plugin prepares the inputs using native Objective-C or Java and presents the PhotoEditor SDK’s editor with the loaded image. A user can then add filters, stickers and frames, adjust the images colors or transform it to a different size. All with native performance and a polished UI. Once finished, the image is returned by the editor and our plugin saves the file to the native photo rolls. Finally, the plugin returns the path to the edited image to Cordova. From there you could display the image, offer sharing functionality or anything else. On iOS, the running example looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Our example app, opening the PhotoEditor SDK from Cordova.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 241px) 241px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;241&quot; height=&quot;500&quot; src=&quot;https://img.ly/_astro/1-4daA2PAMShT1c3RlIXEROA_2pfyJM.webp&quot; srcset=&quot;/_astro/1-4daA2PAMShT1c3RlIXEROA_2pfyJM.webp 241w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;And thats it!&lt;/strong&gt; You have now successfully created a Cordova plugin that wraps the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK’s&lt;/a&gt; native components for use with Cordova. If you need to configure the SDK, e.g. changing behaviour and colors or adding/removing stickers, you can adjust the plugin to modify the configuration object. Take a look at our configuration docs for &lt;a href=&quot;https://img.ly/docs/pesdk/ios/getting-started/integration/swift-package-manager/&quot;&gt;iOS&lt;/a&gt; and &lt;a href=&quot;https://img.ly/docs/pesdk/android/getting-started/integration/&quot;&gt;Android&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h2 id=&quot;ionic&quot;&gt;Ionic&lt;/h2&gt;
&lt;p&gt;Ionic is built on Cordova, so you’re able to use the plugin from your Ionic application as well. Just take a look at our demo repository to get started. The repository contains a Ionic demo app that uses the plugin we just built to open the PhotoEditor SDK.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Creating a Cordova plugin that wraps the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; requires some upfront work, mostly caused by Cordova quirks, but quickly yields very usable results. Combined with the SDK’s configuration options, one can easily embed a photo editor into his Cordova or Ionic app and move on to other parts. For all details, take a look at the corresponding &lt;a href=&quot;https://github.com/imgly/pesdk-cordova-demo&quot;&gt;GitHub repository&lt;/a&gt;, our &lt;a href=&quot;https://img.ly/docs/pesdk/ios/getting-started/integration/swift-package-manager/&quot;&gt;iOS&lt;/a&gt; and &lt;a href=&quot;https://img.ly/docs/pesdk/android/getting-started/integration/&quot;&gt;Android&lt;/a&gt; docs and the &lt;a href=&quot;https://github.com/imgly/pesdk-ionic-demo&quot;&gt;Ionic demo repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! To stay in the loop, subscribe to our &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;Newsletter&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Malte</dc:creator><media:content url="https://blog.img.ly/2020/03/1-jnS6C6he1NrKGOdwkZEubg.png" medium="image"/><category>JavaScript</category><category>Cordova</category><category>Ionic</category><category>iOS</category><category>Android</category><category>Tech</category><category>How-To</category><category>Company</category></item><item><title>PhotoEditor SDK + React Native</title><link>https://img.ly/blog/photoeditor-sdk-react-native-15179c589a55/</link><guid isPermaLink="true">https://img.ly/blog/photoeditor-sdk-react-native-15179c589a55/</guid><pubDate>Mon, 20 Mar 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Often our users ask whether it’s possible to use the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; for iOS and Android with React Native (the good news right away: Yes, it is possible and fairly easy as well). So, we set out to create a demo app and put together a guide on how to easily set up the PhotoEditor SDK with React Native and how to avoid eventual pitfalls.&lt;/p&gt;
&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;
&lt;p&gt;We started our demo project by creating a new React Native project using &lt;code&gt;react-native init PESDKDemo&lt;/code&gt;. We then added &lt;a href=&quot;https://github.com/wix/react-native-navigation&quot;&gt;&lt;code&gt;react-native-navigation&lt;/code&gt;&lt;/a&gt;, a navigation library from the awesome folks at Wix, to get a base for our demo app. However, &lt;code&gt;react-native-navigation&lt;/code&gt; is not required for embedding the PESDK into your React Native application.&lt;/p&gt;
&lt;h2 id=&quot;launching-the-photoeditor-sdk-from-reactnative&quot;&gt;Launching the PhotoEditor SDK from React Native&lt;/h2&gt;
&lt;p&gt;To successfully launch our editor from React Native we needed to do three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add the PESDK library to our iOS project.&lt;/li&gt;
&lt;li&gt;Create a native module that bridges between React Native and the PhotoEditor SDK.&lt;/li&gt;
&lt;li&gt;Add a method to create a &lt;code&gt;ToolbarController&lt;/code&gt;, push a &lt;code&gt;PhotoEditController&lt;/code&gt; and present them from the current view controller.&lt;/li&gt;
&lt;li&gt;Call the method, wherever we want to edit an image in our React Native code&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first step was rather easy. We just followed the &lt;a href=&quot;https://img.ly/docs/pesdk/ios/getting-started/integration/swift-package-manager/&quot;&gt;steps for a manual installation&lt;/a&gt; and had the PESDK library ready in minutes.&lt;/p&gt;
&lt;p&gt;Creating a native module in React Native was fairly easy as well. We simply created &lt;code&gt;PESDKModule.h&lt;/code&gt; and &lt;code&gt;PESDKModule.m&lt;/code&gt; and defined a &lt;code&gt;PESDKModule&lt;/code&gt; class that inherits from NSObject and implements the &lt;code&gt;RCTBridgeModule&lt;/code&gt; protocol. In the classes implementation we registered our module with React Native by calling &lt;code&gt;RCT_EXPORT_MODULE(PESDK)&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;In order to create a new photo editor view controller we needed to create a new &lt;code&gt;ToolbarController&lt;/code&gt; and push a &lt;code&gt;PhotoEditController&lt;/code&gt; that loads a sample image. As we’ll always use such a hierarchy, we bundled this setup into the &lt;code&gt;present()&lt;/code&gt; method of our native module and exported it to React Native using the &lt;code&gt;RCT_EXPORT_METHOD&lt;/code&gt; macro:&lt;/p&gt;

&lt;p&gt;The final step was putting our newly created module to good use and actually pushing our photo editor from React Native. To do so, we imported the &lt;code&gt;PESDK&lt;/code&gt; native module using &lt;code&gt;var PESDK = NativeModules.PESDK&lt;/code&gt;. We could then call &lt;code&gt;PESDK.present()&lt;/code&gt; and the native module created the &lt;code&gt;ToolbarController&lt;/code&gt; and &lt;code&gt;PhotoEditViewController&lt;/code&gt; objects and pushed them from the current view controller. If you were to use a simple &lt;code&gt;TouchableHighlight&lt;/code&gt;, this could look like this:&lt;/p&gt;

&lt;p&gt;Bringing it all together, we were now able to open the native iOS PESDK &lt;code&gt;PhotoEditController&lt;/code&gt; from our React Native code. Surprisingly easy, isn’t it?&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Our initial result: The button pushes our SDK with a sample image.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 800px) 800px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;800&quot; height=&quot;314&quot; src=&quot;https://img.ly/_astro/1-wyxWgOjJbTS4nR4Jgk8ugg_1ALLeW.webp&quot; srcset=&quot;/_astro/1-wyxWgOjJbTS4nR4Jgk8ugg_Z1Io0kC.webp 640w, /_astro/1-wyxWgOjJbTS4nR4Jgk8ugg_ZvNqbf.webp 750w, /_astro/1-wyxWgOjJbTS4nR4Jgk8ugg_1ALLeW.webp 800w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;passing-an-image-to-theeditor&quot;&gt;Passing an image to the editor&lt;/h2&gt;
&lt;p&gt;Opening an image editor with a sample image is nice but wasn’t really our intention, when we started integrating the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt;. Instead, we wanted to be able to edit &lt;strong&gt;any&lt;/strong&gt; image and save the result into our camera roll or share it with our friends. For our little demo, we decided to present a grid of images from unsplash.com to the user, so he can choose any image from the list and edit it with our editor.&lt;/p&gt;
&lt;p&gt;The JavaScript for creating the image grid is a little out of scope for this article, so we won’t cover it in detail. It basically uses a &lt;code&gt;ListView&lt;/code&gt; to create rows of three cells and fills them with images loaded from the unsplash.it service. All image fetching, scrolling, etc. is handled by React Native, so we only needed to handle the user’s taps on an image:&lt;/p&gt;

&lt;p&gt;We used &lt;code&gt;[react-native-fs](https://github.com/itinance/react-native-fs)&lt;/code&gt; to download a larger resolution image to the local filesystem, pass the path of the local file to our &lt;code&gt;present()&lt;/code&gt; call and modify our iOS native module:&lt;/p&gt;

&lt;p&gt;We then had a nice little app, that shows a grid of images, loads a high-resolution image upon selection and opens the PhotoEditor SDK:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;The iOS demo app running on a device.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 326px) 326px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;326&quot; height=&quot;600&quot; src=&quot;https://img.ly/_astro/1-rb4iEgDv941RQpQvESCMlw_Wq8wK.webp&quot; srcset=&quot;/_astro/1-rb4iEgDv941RQpQvESCMlw_Wq8wK.webp 326w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;android-implementation&quot;&gt;Android implementation&lt;/h2&gt;
&lt;p&gt;As we have seen, opening the PESDK from React Native can easily be done on iOS. But why use React Native, if the app only runs on iOS? It was time to add Android support to our little demo app. To accomplish this we needed to repeat some of the previous steps for Android:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add the PESDK to our Android project.&lt;/li&gt;
&lt;li&gt;Create a native module that bridges between React Native and the PESDK.&lt;/li&gt;
&lt;li&gt;Add a method to launch an &lt;code&gt;ImglyIntent&lt;/code&gt; using the &lt;code&gt;PhotoEditorBuilder&lt;/code&gt; from the current &lt;code&gt;Activity&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Installing the SDK is again done by following the instructions for integrating the PESDK and shouldn’t take more than a few minutes. Creating a native module on Android is quite similar to iOS, although a little more setup code is required: We created our &lt;code&gt;PESDKModule&lt;/code&gt; that recreates the &lt;code&gt;present(path)&lt;/code&gt;method from iOS, a &lt;code&gt;PESDKPackage&lt;/code&gt; containing our module and finally added the package to our &lt;code&gt;Application&lt;/code&gt;:&lt;/p&gt;



&lt;p&gt;This time, we prepared the desired settings for our editor, added our image path and passed everything to a PhotoEditorBuilder. The builder creates an intent and we launch it from our current &lt;code&gt;Activity&lt;/code&gt;. That’s it! Running our sample app on an Android device now leads to this:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;The example app running on an Android device.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 800px) 800px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;800&quot; height=&quot;345&quot; src=&quot;https://img.ly/_astro/1-vsocOovAQ85pAWggvd9KuQ_Oz3Ce.webp&quot; srcset=&quot;/_astro/1-vsocOovAQ85pAWggvd9KuQ_ZKHc7R.webp 640w, /_astro/1-vsocOovAQ85pAWggvd9KuQ_ZOSbAT.webp 750w, /_astro/1-vsocOovAQ85pAWggvd9KuQ_Oz3Ce.webp 800w&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;nifty-details&quot;&gt;Nifty details&lt;/h3&gt;
&lt;p&gt;Experimenting with our new app, we noticed an issue: While the Android app closed the editor just fine when canceling the editor, the iOS app did nothing at all. That is because the iOS module doesn’t handle the PESDKs delegate methods. However, this can be fixed easily:&lt;/p&gt;

&lt;p&gt;It could be taken even further by turning our native iOS module into a &lt;code&gt;RCTEventEmitter&lt;/code&gt; and sending events to React Native when the delegate methods are called. Just take a look into our &lt;a href=&quot;https://github.com/imgly/pesdk-react-native-demo&quot;&gt;GitHub repository&lt;/a&gt; to find the corresponding code.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;To sum it up: Using the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt; from React Native was rather easy to do and we were very happy with the results. Handling the data flows between React Native and the Java or Objective-C classes can be tricky, but when dealing with a specific use case, it should be easy to adjust the native modules to meet your requirements. Feel free to adapt &lt;a href=&quot;https://github.com/imgly/pesdk-react-native-demo&quot;&gt;our code&lt;/a&gt; and add the PhotoEditor SDK to your React Native app. We’re looking forward to your feedback and any pull requests, that further optimize our implementation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! To stay in the loop, subscribe to our &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;Newsletter&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Malte</dc:creator><media:content url="https://blog.img.ly/2020/03/1-OHCSavYLUI1iSouT83EHhg.png" medium="image"/><category>React Native</category><category>iOS</category><category>Android</category><category>Photo Editing</category><category>Photo Editor</category><category>Tech</category><category>How-To</category><category>Company</category></item><item><title>Lightweight Image comparison with Rembrandt.JS</title><link>https://img.ly/blog/lightweight-image-comparison-with-rembrandt-js-b189276d03c4/</link><guid isPermaLink="true">https://img.ly/blog/lightweight-image-comparison-with-rembrandt-js-b189276d03c4/</guid><pubDate>Wed, 08 Mar 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://github.com/imgly/rembrandt&quot;&gt;Rembrandt.JS&lt;/a&gt; is a lightweight, client- and server-side image comparison library that we developed for our internal tests for the &lt;a href=&quot;https://img.ly/products/photo-sdk/&quot;&gt;PhotoEditor SDK&lt;/a&gt;. Since we’re adherents of test-driven development, we devote a lot of time and effort to writing robust tests and finding the right tools. A lot can go wrong in the process of refactoring and adding new features to our product. Since we want to make sure that our SDK performs at a top level 100% of the time, solid unit tests are crucial to our work. However, when it came to image comparison, we were not happy with the existing options. We tried to work with Blink-Diff from Yahoo, but since the tool isn’t maintained anymore and furthermore has a dependency on another unmaintained library, we chose to develop our own solution. Rembrandt.JS has no dependencies because it works client-side with the Canvas API of the browser and server-side with Node-Canvas.&lt;/p&gt;
&lt;p&gt;The primary function of Rembrandt is quite simple; &lt;strong&gt;it compares two pictures&lt;/strong&gt;. One image is the expected output of our SDK and the other one is the actual output of the PhotoEditor. After the comparison, Rembrandt outputs a numeric value between 0 and 1 indicating the percentual error rate. With an additional feature of Rembrandt, it is also possible to highlight the incorrect pixels on the image which helps to see instantly where the errors occur. Furthermore, Rembrandt.JS can compensate defective pixels that originate through compression artifacts. By comparing every pixel with its adjacent pixels with an adjustable tolerance, Rembrandt evaluates whether there’s a discrepancy or not. Said feature was critical to us since due to the sheer amount of features and filters in PhotoEditor SDK we’re always testing on a large scale. If we would’ve been using loss-free test material (like .png for example) the amount of disk space that the images would’ve been consuming would have been quite noticeable.&lt;/p&gt;
&lt;p&gt;Rembrandt.JS could also be leveraged for UI tests. With screenshots of both the design and the actual website or app, you could verify whether every object loads and behaves correctly. With the rendering option, you could quickly detect smallest discrepancies in even large sites or apps. Or, you could solve every spot the difference game within seconds.&lt;/p&gt;
&lt;p&gt;If Rembrandt is something potentially useful for your project, check out the Github repositories for &lt;a href=&quot;https://github.com/imgly/rembrandt&quot;&gt;Javascript&lt;/a&gt;, iOS and Android.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! To stay in the loop, subscribe to our &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;Newsletter&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Felix</dc:creator><media:content url="https://blog.img.ly/2020/04/1-nN176Cbkfr8ZA48HOY7JFA.png" medium="image"/><category>JavaScript</category><category>Software Development</category><category>Image Processing</category><category>iOS</category><category>Android</category><category>Tech</category><category>How-To</category><category>Insights</category></item></channel></rss>