{"id":21953,"date":"2019-10-09T16:22:22","date_gmt":"2019-10-09T16:22:22","guid":{"rendered":"http:\/\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary"},"modified":"2022-02-25T19:08:41","modified_gmt":"2022-02-25T19:08:41","slug":"converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary","title":{"rendered":"Converting Videos to GIFs in Kotlin-Based Android Apps With Cloudinary"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>Among the image formats available today, Graphics Interchange Format (GIF) works for both static and animated images. A popular use of GIF images\u2014commonly called GIFs\u2014is to repeatedly loop motions, which you cannot in real life. Many people have turned themselves into online celebrities through effective application of GIFs.<\/p>\n<p>Cloudinary is a robust, cloud-based, end-to-end platform for uploads, storage, manipulations, optimizations, and delivery of images and <a href=\"https:\/\/cloudinary.com\/video_api\">videos<\/a>. Notably for manipulations, you can add appeal to visual media by transforming them through resizing and application of various effects.<\/p>\n<p>This article steps you through the procedure of leveraging Cloudinary to convert a video in an Android demo app, written in Kotlin, to a GIF. It\u2019s a helpful task well worth learning.<\/p>\n<h2>Fulfilling the Preliminary Requirements<\/h2>\n<p>First, <a href=\"https:\/\/cloudinary.com\/users\/register\/free\">create a Cloudinary account<\/a>.<\/p>\n<p>Second, <a href=\"https:\/\/developer.android.com\/studio\/archive.html\">download Android Studio 3.4+<\/a>, which works well with Kotlin, as the IDE for developing the demo app.<\/p>\n<h2>Setting Up an Android Client<\/h2>\n<p>Now set up an Android client by following the steps in the subsections below.<\/p>\n<h3>Creating an Android Project<\/h3>\n<p>Open Android Studio and create an Android project.<\/p>\n<ol>\n<li>In the <strong>Create Android Project<\/strong> screen, select the <strong>Empty Activity<\/strong> template. Click <strong>Next<\/strong>.\n<img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/image\/upload\/w_700,c_fill,f_auto,q_auto,dpr_2.0\/Web_Assets\/blog\/image_preview.png\" alt=\"Create new project\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"1120\"\/>\n<\/li>\n<li>In the <strong>Configure your project<\/strong> screen that is displayed, type the application name, package name, and so forth in the text fields. Select <strong>Kotlin<\/strong> as the language. Select <strong>API 16: Android 4.1 (Jelly Bean)<\/strong> from the pull-down menu as your minimum SDK, i.e., the lowest Android version supported by your app. Click <strong>Finish<\/strong>.\n<img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/image\/upload\/w_700,c_fill,f_auto,q_auto,dpr_2.0\/Web_Assets\/blog\/image_preview_1.png\" alt=\"Configure your project\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"1123\"\/>\n<\/li>\n<\/ol>\n<h3>Configuring Cloudinary<\/h3>\n<p>Configure Cloudinary, as follows:<\/p>\n<ul>\n<li>\n<p>Edit your project\u2019s <code>build.gradle<\/code> file to add the Cloudinary dependency so as to gain access to Cloudinary\u2019s features:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>implementation group: 'com.cloudinary', name: 'cloudinary-android', version: '1.24.0'\n<\/code><\/pre>\n<\/li>\n<li>\n<p>Edit the AndroidManifest.xml file and add the storage permissions under the uses-permission tag and the configurations under the application tag:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>&lt;manifest xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;\n    package=&quot;com.example.android.cloudinarysample&quot;&gt;\n\n    &lt;uses-permission android:name=&quot;android.permission.READ_EXTERNAL_STORAGE&quot;\/&gt;\n    &lt;uses-permission android:name=&quot;android.permission.WRITE_EXTERNAL_STORAGE&quot;\/&gt;\n\n    &lt;application\n        ...\n        &gt;\n\n        &lt;meta-data\n            android:name=&quot;CLOUDINARY_URL&quot;\n            android:value=&quot;cloudinary:\/\/@myCloudName&quot;\/&gt;\n\n    &lt;\/application&gt;\n\n&lt;\/manifest&gt;\n<\/code><\/pre>\n<\/li>\n<li>\n<p>Create a class called <code>AppController<\/code> to extend the <code>Application<\/code> class:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>public class AppController : Application() {\n    override fun onCreate() {\n        super.onCreate()\n        \/\/ Initialize Cloudinary\n        MediaManager.init(this)\n    }\n}\n<\/code><\/pre>\n<p><code>AppController<\/code> initializes  <code>MediaManager<\/code> during the app\u2019s life cycle, setting up the library with the required parameters, such as the cloud name that you specified in the <code>AndroidManifest.xml<\/code> file in step 2.<\/p>\n<\/li>\n<li>\n<p>Add the <code>AppController<\/code> class as the name of the application tag to the <code>AndroidManifest.xml<\/code> file:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>&lt;application\n    android:name=&quot;.AppController&quot; &gt;\n<\/code><\/pre>\n<\/li>\n<\/ul>\n<h3>Examining the App Layout<\/h3>\n<p>Here is the code snippet of your layout:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;LinearLayout xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;\n    xmlns:tools=&quot;http:\/\/schemas.android.com\/tools&quot;\n    android:layout_width=&quot;match_parent&quot;\n    android:layout_height=&quot;match_parent&quot;\n    android:layout_gravity=&quot;center&quot;\n    android:layout_margin=&quot;16dp&quot;\n    android:gravity=&quot;center&quot;\n    android:orientation=&quot;vertical&quot;\n    tools:context=&quot;com.example.android.cloudinarysample.MainActivity&quot;&gt;\n    \n    &lt;ProgressBar\n        android:id=&quot;@+id\/progress_bar&quot;\n        android:layout_width=&quot;wrap_content&quot;\n        android:layout_height=&quot;wrap_content&quot;\n        android:visibility=&quot;invisible&quot; \/&gt;\n\n    &lt;Button\n        android:id=&quot;@+id\/button_upload_video&quot;\n        android:layout_width=&quot;wrap_content&quot;\n        android:layout_height=&quot;wrap_content&quot;\n        android:layout_marginTop=&quot;10dp&quot;\n        android:text=&quot;Upload Video&quot; \/&gt;\n\n\n&lt;\/LinearLayout&gt;\n<\/code><\/pre>\n<p>Note these two UI elements:<\/p>\n<ul>\n<li>A progress bar that slides forward while you are uploading or downloading media.<\/li>\n<li>A button that selects a video from your phone.<\/li>\n<\/ul>\n<h3>Uploading to Cloudinary<\/h3>\n<p>Cloudinary offers two types of uploads:<\/p>\n<ul>\n<li>\n<strong>Signed uploads<\/strong>, which require an authentication signature from a back-end. For these uploads, your images and videos are signed with the API and a secret key is displayed in your Cloudinary console. Since using that key on the client side is insecure, which could easily cause a decompilation, a back-end is mandatory.<\/li>\n<li>\n<strong>Unsigned uploads<\/strong>, which are less secure than signed ones, require no signatures. You can set the options, such as the file-size limit and file types, in an unsigned preset for unsigned uploads. A few limitations apply, for example, you cannot overwrite the existing media.<\/li>\n<\/ul>\n<p>Specify unsigned uploads for the demo app by enabling them on your Cloudinary console:<\/p>\n<ol>\n<li>\n<p>Click <strong>Dashboard<\/strong> and then the <strong>gear-wheel-shaped<\/strong> icon at the top for the settings.<\/p>\n<\/li>\n<li>\n<p>Click the <strong>Upload<\/strong> tab near the top, scroll down to the <strong>Upload presets<\/strong> section, and click <strong>Enable unsigned uploading<\/strong>.<\/p>\n<p>Cloudinary then generates a preset named with a random string. Copy that string and set it aside for later use.\n<img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/image\/upload\/w_700,c_fill,f_auto,q_auto,dpr_2.0\/Web_Assets\/blog\/dashboard.jpg\" alt=\"Dashboard\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1400\" height=\"719\"\/><\/p>\n<\/li>\n<\/ol>\n<p>Now upload a video from your phone to Cloudinary, transform the video to a GIF, and download the GIF file. Here is the related code in the <code>MainAcivity.kt<\/code> file:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>private val SELECT_VIDEO: Int = 100\nlateinit var TAG:String\n\noverride fun onCreate(savedInstanceState: Bundle?) {\n    super.onCreate(savedInstanceState)\n    setContentView(R.layout.activity_main)\n    this.TAG = localClassName\n    button_upload_video.setOnClickListener {\n        if (checkStoragePermission()) {\n            openMediaChooser()\n        } else {\n            requestPermission()\n        }\n    }\n    \n}\n\nfun openMediaChooser() {\n    val intent = Intent(Intent.ACTION_PICK, MediaStore.Video.Media.EXTERNAL_CONTENT_URI)\n    startActivityForResult(intent, SELECT_VIDEO)\n}\n\noverride fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {\n\n    if (requestCode == SELECT_VIDEO &amp;&amp; resultCode == Activity.RESULT_OK) {\n        progress_bar.visibility = VISIBLE\n        button_upload_video.visibility = INVISIBLE\n        MediaManager.get()\n                .upload(data!!.data)\n                .unsigned(&quot;YOUR_PRESET&quot;)\n                .option(&quot;resource_type&quot;, &quot;video&quot;)\n                .callback(object : UploadCallback {\n                    override fun onStart(requestId: String) {\n                        Log.d(TAG, &quot;onStart&quot;)\n                    }\n\n                    override fun onProgress(requestId: String, bytes: Long, totalBytes: Long) {\n\n                    }\n\n                    override fun onSuccess(requestId: String, resultData: Map&lt;*, *&gt;) {\n                        Toast.makeText(this@MainActivity, &quot;Upload successful&quot;, Toast.LENGTH_LONG).show()\n                        \n                    }\n\n                    override fun onError(requestId: String, error: ErrorInfo) {\n                        Log.d(TAG,error.description)\n                        button_upload_video.visibility = VISIBLE\n                        progress_bar.visibility = INVISIBLE\n                        Toast.makeText(this@MainActivity,&quot;Upload was not successful&quot;,Toast.LENGTH_LONG).show()\n                    }\n\n                    override fun onReschedule(requestId: String, error: ErrorInfo) {\n                        Log.d(TAG, &quot;onReschedule&quot;)\n                    }\n                }).dispatch()\n\n    }\n\n}\n<\/code><\/pre>\n<p>The above snippet contains three functions;<\/p>\n<ul>\n<li>\n<p><code>onCreate<\/code>: At start, the app calls this function on the creation of an activity, with the layout in the  <code>AndroidManifest.xml<\/code> file being the activity\u2019s default layout. On a click of the button, which contains a listener, the app checks if storage permissions are available. If the answer is yes, the app calls the function <code>openMediaChooser<\/code>. Otherwise, the app requests for those permissions and, after receiving them, calls <code>openMediaChooser<\/code>.<\/p>\n<p>For details on the <code>checkStoragePermission<\/code>, <code>requestPermission<\/code>, and <code>onPermissionsResult<\/code> functions, see their code in the <a href=\"https:\/\/github.com\/KingIdee\/cloudinary-video-to-gif\">GitHub repository<\/a>.<\/p>\n<\/li>\n<li>\n<p><code>openMediaChooser<\/code> : This function opens your phone\u2019s gallery to select a video, creating a unique request code in the variable <code>SELECT_VIDEO<\/code>. The <code>startActivityForResult<\/code> function denotes the requirement for a response from the video-selection process, with the response rendered in the <code>onActivityResult<\/code> function. If no video is selected, the app cancels the operation.<\/p>\n<\/li>\n<li>\n<p><code>onActivityResult<\/code> : On a video selection, the value of <code>resultCode<\/code> is <code>Activity.RESULT_OK<\/code>. Otherwise, the value becomes <code>Activity.RESULT_CANCELLED<\/code>. This function checks if the request code matches the one that was sent earlier and verifies if the result code reads <code>Activity.RESULT_OK<\/code>.<\/p>\n<\/li>\n<\/ul>\n<p>A successful video selection triggers an upload to Cloudinary through a dispatch of\n<code>UploadRequest<\/code>, which takes several methods. Here are four of them:<\/p>\n<ul>\n<li>\n<code>upload<\/code>, which contains the URI of the selected video.<\/li>\n<li>\n<code>unsigned<\/code>, which contains the preset name from the console.<\/li>\n<li>\n<code>option<\/code>, which specifies the resource type of the upload.<\/li>\n<li>\n<code>UploadCallback<\/code>, which tracks the upload progress.<\/li>\n<\/ul>\n<h2>Transforming the Video<\/h2>\n<p>On a successful upload, the app calls the <code>onSuccess<\/code> method, which contains the request ID (<code>requestId<\/code>) and other details of the upload. You can obtain the URL of the newly uploaded video by calling <code>resultData[&quot;url&quot;]<\/code>. To convert that video to a GIF, simply change the video\u2019s file extension to <code>.gif<\/code>, like this:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>    val publicId:String = resultData[&quot;public_id&quot;] as String\n    val gifUrl: String = MediaManager.get()\n            .url()\n            .resourceType(&quot;video&quot;)\n            .transformation(Transformation&lt;Transformation&lt;out Transformation&lt;*&gt;&gt;?&gt;().videoSampling(&quot;12&quot;)!!.width(&quot;174&quot;).height(&quot;232&quot;).effect(&quot;loop:2&quot;))\n    .generate(&quot;$publicId.gif&quot;)       \n<\/code><\/pre>\n<p>Cloudinary assigns to each uploaded video a unique ID, which you can access by calling <code>resultData[&quot;public_url&quot;]<\/code>. Hence, the resulting <code>gifUrl<\/code> value is your cloud-name-based URL (usually in the format of <code>res.cloudinary.com\/``&lt;``cloud name``&gt;``\/<\/code>, appended with the video\u2019s transformations; its resource type to be accessed; its cloud-stored, unique ID; and its output format (GIF with the file extension <code>.gif<\/code> in this case).<\/p>\n<p>The above code snippet details the transformations to the GIF file: shrinking the height and width to 232 and 174 pixels, respectively; setting the frame rate to 12 fps; and directing the GIF to loop twice. Without those transformations, the GIF file would be larger than the uploaded video, defeating the purpose of GIFs, whose file size should always be smaller than their video counterparts.<\/p>\n<p>For details on transforming videos with Cloudinary, see the <a href=\"https:\/\/cloudinary.com\/documentation\/animated_images#converting_an_existing_animated_image_to_animated_webp_or_png_format\">related documentation<\/a> here.<\/p>\n<h2>Downloading the GIF file<\/h2>\n<p>Now download the GIF file through the <code>PRDownloader<\/code> library.  Do the following:<\/p>\n<ol>\n<li>Add the <code>PRDownloader<\/code> dependency to the <code>build.gradle<\/code> file:<\/li>\n<\/ol>\n<pre class=\"js-syntax-highlighted\"><code>    implementation 'com.mindorks.android:prdownloader:0.2.0'\n<\/code><\/pre>\n<ol start=\"2\">\n<li>Initialize <code>PRDownloader<\/code> in the <code>AppController.kt<\/code> file:<\/li>\n<\/ol>\n<pre class=\"js-syntax-highlighted\"><code>    public class AppController : Application() {\n        override fun onCreate() {\n            super.onCreate()\n            \/\/ Initialize Cloudinary\n            MediaManager.init(this)\n            \/\/ Initialize the PRDownload library\n            PRDownloader.initialize(this)\n        }\n    }\n<\/code><\/pre>\n<ol start=\"3\">\n<li>Create a function called <code>downloadGIF<\/code> for handling downloads:<\/li>\n<\/ol>\n<pre class=\"js-syntax-highlighted\"><code>    private fun downloadGIF(url: String, name: String) {\n        val downloadId =\n                PRDownloader\n                        .download(url, getRootDirPath(), name).build()\n                        .setOnStartOrResumeListener(object : OnStartOrResumeListener {\n                            override fun onStartOrResume() {\n                                Log.d(TAG,&quot;download started&quot;)\n                            }\n                        })\n                        .setOnPauseListener(object : OnPauseListener {\n                            override fun onPause() {\n    \n                            }\n                        })\n                        .setOnCancelListener(object : OnCancelListener {\n                            override fun onCancel() {\n    \n                            }\n                        })\n                        .setOnProgressListener(object : OnProgressListener {\n                            override fun onProgress(progress: Progress) {\n    \n                            }\n                        })\n                        .start(object : OnDownloadListener {\n                            override fun onDownloadComplete() {\n                                progress_bar.visibility = INVISIBLE\n                                Toast.makeText(this@MainActivity,&quot;Download complete&quot;,Toast.LENGTH_LONG).show()\n                            }\n    \n                            override fun onError(error: Error) {\n                                Log.d(TAG,error.toString())\n                            }\n                        })\n    \n    }\n    \n    private fun getRootDirPath(): String {\n        return if (Environment.MEDIA_MOUNTED == Environment.getExternalStorageState()) {\n            val file = ContextCompat.getExternalFilesDirs(this@MainActivity,\n                    null)[0]\n            file.absolutePath\n        } else {\n            this@MainActivity.filesDir.absolutePath\n        }\n    }\n<\/code><\/pre>\n<p><code>PRDownload.download<\/code> takes three parameters: the URL of the to-be-downloaded file, the directory in which to place it, and the file name. Hence, after generation of <code>gifUrl<\/code>, the app calls <code>PRDownload.download<\/code> in the <code>onSuccess<\/code> function of <code>UploadCallback<\/code>, as reflected here:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>    override fun onSuccess(requestId: String, resultData: Map&lt;*, *&gt;) {\n        ...\n        downloadGIF(gifUrl,&quot;$publicId.gif&quot;)\n    }\n<\/code><\/pre>\n<p>Finally, the app places the downloaded file in the directory <code>Android\/data\/``&lt;``app package name``&gt;``\/files<\/code> in your phone.<\/p>\n<h2>Summing Up<\/h2>\n<p>Cloudinary offers numerous useful, intuitive APIs for manipulating and storing visual media. The demo app described in this article is just the tip of the iceberg. Do check out the details of <a href=\"https:\/\/cloudinary.com\/documentation\">Cloudinary\u2019s APIs and other capabilities<\/a>.  For the app\u2019s complete source code, see its <a href=\"https:\/\/github.com\/KingIdee\/cloudinary-video-to-gif\">GitHub repository<\/a>.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":21954,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[333,128,303,305],"class_list":["post-21953","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-android","tag-gif","tag-video","tag-video-api"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Converting Videos to GIFs in Kotlin-Based Android Apps<\/title>\n<meta name=\"description\" content=\"Learn how to build a Kotlin-based Android app that converts videos into animated GIFs.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Converting Videos to GIFs in Kotlin-Based Android Apps With Cloudinary\" \/>\n<meta property=\"og:description\" content=\"Learn how to build a Kotlin-based Android app that converts videos into animated GIFs.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2019-10-09T16:22:22+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-02-25T19:08:41+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649720696\/Web_Assets\/blog\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768.jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"1540\" \/>\n\t<meta property=\"og:image:height\" content=\"847\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Converting Videos to GIFs in Kotlin-Based Android Apps With Cloudinary\",\"datePublished\":\"2019-10-09T16:22:22+00:00\",\"dateModified\":\"2022-02-25T19:08:41+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary\"},\"wordCount\":10,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649720696\/Web_Assets\/blog\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768.jpg?_i=AA\",\"keywords\":[\"Android\",\"GIF\",\"Video\",\"Video API\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2019\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary\",\"url\":\"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary\",\"name\":\"Converting Videos to GIFs in Kotlin-Based Android Apps\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649720696\/Web_Assets\/blog\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768.jpg?_i=AA\",\"datePublished\":\"2019-10-09T16:22:22+00:00\",\"dateModified\":\"2022-02-25T19:08:41+00:00\",\"description\":\"Learn how to build a Kotlin-based Android app that converts videos into animated GIFs.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649720696\/Web_Assets\/blog\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649720696\/Web_Assets\/blog\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768.jpg?_i=AA\",\"width\":1540,\"height\":847},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Converting Videos to GIFs in Kotlin-Based Android Apps With Cloudinary\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Converting Videos to GIFs in Kotlin-Based Android Apps","description":"Learn how to build a Kotlin-based Android app that converts videos into animated GIFs.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary","og_locale":"en_US","og_type":"article","og_title":"Converting Videos to GIFs in Kotlin-Based Android Apps With Cloudinary","og_description":"Learn how to build a Kotlin-based Android app that converts videos into animated GIFs.","og_url":"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary","og_site_name":"Cloudinary Blog","article_published_time":"2019-10-09T16:22:22+00:00","article_modified_time":"2022-02-25T19:08:41+00:00","og_image":[{"width":1540,"height":847,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649720696\/Web_Assets\/blog\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768.jpg?_i=AA","type":"image\/jpeg"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary"},"author":{"name":"","@id":""},"headline":"Converting Videos to GIFs in Kotlin-Based Android Apps With Cloudinary","datePublished":"2019-10-09T16:22:22+00:00","dateModified":"2022-02-25T19:08:41+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary"},"wordCount":10,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649720696\/Web_Assets\/blog\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768.jpg?_i=AA","keywords":["Android","GIF","Video","Video API"],"inLanguage":"en-US","copyrightYear":"2019","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary","url":"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary","name":"Converting Videos to GIFs in Kotlin-Based Android Apps","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649720696\/Web_Assets\/blog\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768.jpg?_i=AA","datePublished":"2019-10-09T16:22:22+00:00","dateModified":"2022-02-25T19:08:41+00:00","description":"Learn how to build a Kotlin-based Android app that converts videos into animated GIFs.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649720696\/Web_Assets\/blog\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649720696\/Web_Assets\/blog\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768.jpg?_i=AA","width":1540,"height":847},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/converting_videos_to_gifs_in_kotlin_based_android_apps_with_cloudinary#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Converting Videos to GIFs in Kotlin-Based Android Apps With Cloudinary"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":""}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649720696\/Web_Assets\/blog\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768\/Video-to-GIF-Android-Kotlin-App-v1a_2195410768.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/21953","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=21953"}],"version-history":[{"count":2,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/21953\/revisions"}],"predecessor-version":[{"id":22846,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/21953\/revisions\/22846"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/21954"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=21953"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=21953"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=21953"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}