Front-End Development VideoJS and React: A Perfect Match for Modern Video Players React Optimization 101: Tips and Tricks Integrating Cloudinary with Astro Building an Image Upload Feature with JavaScript Mastering Image Alignment: Centering Images with HTML & CSS Adding Video to Your React Native App with react-native-video HTML Image Slider: Do It Yourself and 1-Step Image Gallery Widget How to Effectively Manage Digital Assets in a PHP Image Gallery Introducing Angular Image Editor: Your New Editing Too Mastering Javascript Image Annotation Mastering JavaScript Image Popup Python Video Player: 3 Free Options and a Quick Tutorial Image Recognition Machine Learning: Use Cases and Common Algorithms HTML/CSS: How to Center Images Vertically and Horizontally How to Create an Image Map Understand CSS Background Position with 4 Simple Examples Java for Image Processing: 4 Libraries You Should Know Python Video Processing: 6 Useful Libraries and a Quick Tutorial Blur Image CSS: Two Ways to Blur Images for Gorgeous Effects Designing a Video Flipping App for Android Build an App for Embedding Video Watermarks on Android Devices Change Image on Hover with HTML and CSS How to Align Images with CSS Full Page Background Image with CSS: Tutorial and 5 Automation Tips Using CSS to Scale Page Elements and a Better Way to Scale Your Images CSS Background Image: Quick Tutorial and 3 Automation Tips Featured Image: Best Practices to Feature Images on Your Website Image Gallery Websites: Tips and Tricks for a Stunning Image Gallery 6 Ways to Stretch a Background Image with CSS Auto Cropping for Images and Video: Features & Best Practices FLAC vs. WAV: 4 Key Differences and How to Choose Converting Audio to Video: A Practical Guide FLAC vs. AIFF: 5 Key Differences and How to Choose FLAC vs. MQA: 5 Key Differences and How to Choose Converting WAV Files To OGG The Ultimate Guide On Converting OGG Files To WAV Sound Choices: FLAC vs. MP3 AAC vs MP3 – The Future of Audio Files All about AIFF and how it compares to WAV and MP3 Integrating Cloudinary with Netlify Integrating Cloudinary with Svelte and SvelteKit Integrating Cloudinary with Nuxt Integrating Cloudinary with Gatsby File Upload as a Service: How It Works and 5 Leading Solutions Native Mobile App Development Creative Uses for CSS Inner Border and 3 Ways to Set a Border Integrating Cloudinary with Next.js Front-End Development: The Complete Guide

Designing a Video Flipping App for Android

video flipping app

In a world where the average viewer spends 17 hours a week watching digital videos, the ability to manipulate video content is more important than ever. One way is flipping videos. This technique allows developers to enhance video content’s visual appeal and user experience.

Flipping videos, both horizontally and vertically, can transform how content is perceived, making it a powerful tool for developers and creatives. This article aims to guide you in creating an Android flip video app using Cloudinary, a cloud-based service that provides solutions for image and video management.

Step-by-Step Guide to Flip Video on Android

For this tutorial, we will start by creating an Android script, which requires you to install Android Studio. You can download the latest version from the Android Studio website if you don’t have it installed.

We’ll also use Cloudinary to power our app and host our videos. Go ahead and sign up for a free account if you haven’t already done so.

Once you’ve signed up, go to the Cloudinary website and log in. You will be greeted by the Cloudinary Media Library. Now click on the Programmable Media button in the top left corner of your screen and go to the Dashboard tab.

video flipping app

Now copy your Cloud Name, API Key, and API Secret to your clipboard, as we need them later. With this, we’re now ready and can create our Android Project.

video flipping app

Setting Up Our Android Studio Project

Now open Android Studio and start by selecting an Empty Activity project.

video flipping app

It is important that you select the Empty Views Activity instead of the Basic Views Activity. The former supports both Java and Kotlin development, whereas the latter only supports Kotlin development. This is an important step since we will use Java for this project.

Click on Next to navigate to the next window.

Next, rename your project as appropriate. Here, we have named our project FlipVideo. Select the directory to save your project and select Java as the project language. Finally, click on Finish to finish setting up your project.

video flipping app

It will take some time to build your Gradle and download the requirements, but your project should open once completed.

video flipping app

Once finished, head over to the build.gradle file and add in your imports. Here, we will be importing Picasso, an image downloading and caching library for Android, as well as the Cloudinary Android SDK:

implementation("com.squareup.picasso:picasso:2.8")
implementation("com.cloudinary:cloudinary-android:2.5.0")

Next, we will add some app permissions that will allow our app to access local files on a user’s Android device. To do this, head over to the AndroidManifest.xml file and add in permissions for reading image and video files along with accessing the internet:

<!-- Read Storage -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

<!-- Access Internet -->
<uses-permission android:name="android.permission.INTERNET" />

Creating A UI For Our App

Now that we’ve completed setting up our project, we can create an appealing UI for our Android app.

To create a UI, go to activity_main.xml, which is located in the res/layout folder. This will open up your main app screen.

video flipping app

Here, we will start by adding a button, which will call our video transformations. Next, using the search bar, we will search for and add a VideoView as well as a few radio buttons. The VideoView will be used to select our video, whereas the radio buttons will toggle between vertical or horizontal flips.

Finally, open the XML code for the UI and redefine your UI objects according to your needs. Here, we have changed the radio buttons’ ids and text.

video flipping app

Here is what our app interface looks like:

video flipping app

Coding Our Android Flip Video App

Now that our UI is complete, we can start writing the main logic for our app. We will begin by navigating to MainActivity.java in the com.example.cloudinary folder of our project. Here, we will start our program by creating several new environment variables for the program:

public class MainActivity extends AppCompatActivity {

   private Uri videoPath;
   Map config = new HashMap();

   private VideoView Video;
   private Button button;
   private RadioButton Horizontal;
   private RadioButton Vertical;

Here, the VideoView variable displays our selected video, whereas videoPath stores the video’s path. The button variable refers to our Flip button, which triggers the flip transformations. The radio buttons are used to add choices for vertical and horizontal transformations, and finally, the HashMap() will be used to configure our Cloudinary API.

Now that we’ve set up our variables let’s go to the onCreate() function. We will start by retrieving our objects from the XML UI document.

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);

   Video = findViewById(R.id.videoView);
   button = findViewById(R.id.button);
   Horizontal = findViewById(R.id.Horizontal);
   Vertical = findViewById(R.id.Vertical);
   Horizontal.toggle();

In OnCreate(), we also call the Horizontal.toggle() function to set the default transformation to Horizontal.

Next, we configure our Cloudinary API using our config hashmap:

...
       config.put("cloud_name", "...");
       config.put("api_key","...");
       config.put("api_secret","...");
//        config.put("secure", true);
       MediaManager.init(this, config);
...

Finally, we will add an onclick listener function to both our button and VideoView. The view’s onclick function will call another function called requestPermission(), which we will define later.

In the button’s on-click function, we will start by retrieving the video file’s last segment and naming it as vidID. Next, we use MediaManger to upload our video to the cloud, with its public ID defined as the vidID. Finally, we create a new Handler, which creates a Cloudinary transformation based on which radio button is selected and creates a new video URL. It then calls an intent to display the video in a new browser tab after a delay of 15 seconds to ensure a successful upload. Here is what our complete OnCreate() function looks like:

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       Video = findViewById(R.id.videoView);
       button = findViewById(R.id.button);
       Horizontal = findViewById(R.id.Horizontal);
       Vertical = findViewById(R.id.Vertical);
       Horizontal.toggle();

       config.put("cloud_name", "...");
       config.put("api_key","...");
       config.put("api_secret","...");
//        config.put("secure", true);
       MediaManager.init(this, config);

       Video.setOnClickListener(v -> {
           requestPermission();
       });

       button.setOnClickListener(v -> {

           Log.d("Selection", "Selected Button");

           String vidID = videoPath.getLastPathSegment();

           MediaManager.get().upload(videoPath).option("public_id", vidID).option("resource_type", "video").dispatch();
           new Handler(Looper.getMainLooper()).postDelayed(() -> {
               String angle = "";

               if (Horizontal.isChecked())
               {
                   angle = "hflip";
               }
               else
               {
                   angle = "vflip";
               }

               String url = MediaManager.get().url().transformation(new Transformation()
                       .angle(angle)).resourceType("video").generate(vidID);

               Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
               startActivity(myIntent);
//
               Log.d("URL", url);
           }, 15000); // Delay in milliseconds


       });

   }

Now that we’ve completed our OnCreate function, we will need to define our requestPermission() function. The function works as its name suggests. It triggers an Android System request to the user, requesting permission to access user files and internet usage. If permission is granted, the function calls in a select() function, which we will define next. This function’s code is as follows:

private void requestPermission() {
   if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_MEDIA_VIDEO)
                   == PackageManager.PERMISSION_GRANTED) {
       select();

   } else {
       // Request for permissions
       ActivityCompat.requestPermissions(MainActivity.this, new String[]{
               Manifest.permission.READ_MEDIA_VIDEO,
               Manifest.permission.READ_EXTERNAL_STORAGE
       }, VID_REQ);
   }
}

Now let’s define the selectImage() function.

We start by creating a new intent inside the function. Here, we use the intent to launch an activity, which we will define next. We also use .setType("video/*") to filter our files based on videos. Finally, we use the .launch() method to launch our activity. Here is our complete select() function:

private void select() {
   Intent intent=new Intent();
   intent.setType("video/*");
   Log.d("Selection", "Filtering Video");
   intent.setAction(Intent.ACTION_GET_CONTENT);
   someActivityResultLauncher.launch(intent);
}

Finally, we define our activity as follows:

ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
       new ActivityResultContracts.StartActivityForResult(),
       new ActivityResultCallback<ActivityResult>() {
           @Override
           public void onActivityResult(ActivityResult result) {
               if (result.getResultCode() == Activity.RESULT_OK) {
                   // There are no request codes
                   Intent data = result.getData();
                   Log.d("Selection", "Selecting Video");
                   videoPath = data.getData();
                   // Use 'selectedVideoUri' with your VideoView
                   Video.setVideoURI(videoPath);
                   Video.start();
                   Log.d("Selection", String.valueOf(videoPath));
               }
           }
       });

Here, the activity is simply used to define our video path. It simply uses the result of the user’s selection to get the video’s data. It then uses the data to populate the videoPath variable with the actual path of the video.

With this, our code is now complete. Here is what our final MainActivity.java file looks like:

package com.example.flipvideo;

import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.VideoView;

import com.cloudinary.Transformation;
import com.cloudinary.android.MediaManager;

import java.util.HashMap;
import java.util.Map;

public class MainActivity extends AppCompatActivity {

   private static int VID_REQ=1;
   private Uri videoPath;
   Map config = new HashMap();

   private VideoView Video;
   private Button button;
   private RadioButton Horizontal;
   private RadioButton Vertical;


   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       Video = findViewById(R.id.videoView);
       button = findViewById(R.id.button);
       Horizontal = findViewById(R.id.Horizontal);
       Vertical = findViewById(R.id.Vertical);
       Horizontal.toggle();

       config.put("cloud_name", "...");
       config.put("api_key","...");
       config.put("api_secret","...");
//        config.put("secure", true);
       MediaManager.init(this, config);

       Video.setOnClickListener(v -> {
           requestPermission();
       });

       button.setOnClickListener(v -> {

           Log.d("Selection", "Selected Button");

           String vidID = videoPath.getLastPathSegment();

           MediaManager.get().upload(videoPath).option("public_id", vidID).option("resource_type", "video").dispatch();
           new Handler(Looper.getMainLooper()).postDelayed(() -> {
               String angle = "";

               if (Horizontal.isChecked())
               {
                   angle = "hflip";
               }
               else
               {
                   angle = "vflip";
               }

               String url = MediaManager.get().url().transformation(new Transformation()
                       .angle(angle)).resourceType("video").generate(vidID);

               Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
               startActivity(myIntent);
//
               Log.d("URL", url);
           }, 15000); // Delay in milliseconds


       });

   }

   private void requestPermission() {
       if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_MEDIA_VIDEO)
                       == PackageManager.PERMISSION_GRANTED) {
           select();

       } else {
           // Request for permissions
           ActivityCompat.requestPermissions(MainActivity.this, new String[]{
                   Manifest.permission.READ_MEDIA_VIDEO,
                   Manifest.permission.READ_EXTERNAL_STORAGE
           }, VID_REQ);
       }
   }

   
   // Select the video from the gallery
   private void select() {
       Intent intent=new Intent();
       intent.setType("video/*");
       Log.d("Selection", "Filtering Video");
       intent.setAction(Intent.ACTION_GET_CONTENT);
       someActivityResultLauncher.launch(intent);
   }

   // You can do the assignment inside onAttach or onCreate, i.e, before the activity is displayed
   ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
           new ActivityResultContracts.StartActivityForResult(),
           new ActivityResultCallback<ActivityResult>() {
               @Override
               public void onActivityResult(ActivityResult result) {
                   if (result.getResultCode() == Activity.RESULT_OK) {
                       // There are no request codes
                       Intent data = result.getData();
                       Log.d("Selection", "Selecting Video");
                       videoPath = data.getData();
                       // Use 'selectedVideoUri' with your VideoView
                       Video.setVideoURI(videoPath);
                       Video.start();
                       Log.d("Selection", String.valueOf(videoPath));
                   }
               }
           });
}

Testing Our Android Video Flip App

Now, all we need to do is run our app. To do this, simply press Shift+F10 on your keyboard or click the Play button at the top-right corner of your screen. This will launch your mobile emulator and run your Android flip video app.

video flipping app

Here is what our app looks like:

video flipping app

Next, click on the VideoView and select the video you want to transform. For this guide, we will be choosing dog from the Cloudinary demo cloud:

video flipping app

Finally, select a radio button to choose your transformation, then click the Flip button. This will launch our result video in a new browser tab.

video flipping app

Wrapping Up

The ability to flip videos opens up a myriad of possibilities for content presentation. It allows developers to create unique and engaging visual experiences, enhancing the overall appeal of their applications.

Cloudinary is a comprehensive cloud-based service that provides solutions for image and video management. It offers a wide array of functionalities that go beyond just flipping videos. With Cloudinary, you can perform a variety of transformations on your media files. This includes resizing, cropping, rotating, adjusting brightness, contrast, saturation, and much more. It also allows for more advanced transformations, such as adding filters, effects, or watermarks to your images and videos.

Additionally, Cloudinary supports automatic optimization and responsive delivery. This means it can automatically adjust the quality and format of your media files to deliver the best possible version based on the end user’s device, browser, and location. So, sign up for a Cloudinary account to create your own Android flip video app and discover how Cloudinary can elevate your development process.

More from Cloudinary:

How to Apply Gravity Transformations to Images

Adding Watermarks, Credits, Badges, and Text Overlays to Images

QUICK TIPS
Colby Fayock
Cloudinary Logo Colby Fayock

In my experience, here are tips that can help you better design and optimize a video flipping app for Android:

  1. Optimize video loading and playback
    Use efficient video loading techniques to ensure smooth playback in your app. Consider implementing video preloading or using a loading indicator while the video is being fetched or processed. This helps in providing a seamless user experience, especially when working with larger video files.
  2. Implement error handling and user feedback
    Integrate robust error handling mechanisms to manage potential issues like network failures, permission denials, or Cloudinary API errors. Provide clear feedback to users when something goes wrong, such as displaying a toast message or dialog with options to retry or cancel the operation.
  3. Cache video data for better performance
    Utilize caching strategies to store frequently accessed video data locally on the device. This reduces repeated network calls, speeds up video loading, and enhances the overall performance of your app. Libraries like Picasso already support caching, but you can also implement custom caching for video content.
  4. Leverage Android’s WorkManager for background processing
    Use Android’s WorkManager or other background processing libraries to handle video uploads and transformations in the background. This ensures that the app remains responsive, and users can continue using other features while video processing tasks are being completed.
  5. Support various video formats and resolutions
    Ensure your app supports multiple video formats and resolutions to cater to different user needs. Implement format detection and conversion if necessary, so users can upload and process videos regardless of their original format.
  6. Enhance UI/UX with animations and transitions
    Improve user experience by adding subtle animations and transitions when videos are being flipped or processed. For instance, you can animate the flipping button or the video view itself to indicate the ongoing transformation, making the app feel more interactive and polished.
  7. Ensure app security with Cloudinary’s API
    Secure your API interactions by using signed URLs or tokens for video uploads and transformations. This prevents unauthorized access and ensures that only legitimate requests are processed, protecting your app and user data.
  8. Optimize video transformation delay
    Consider reducing the delay for video transformation results. Instead of using a fixed delay, you can implement a polling mechanism or WebSocket to check the transformation status and retrieve the URL as soon as the processing is complete. This provides a more responsive experience.
  9. Test on multiple devices and network conditions
    Ensure your app performs well across various devices, screen sizes, and network conditions. Test on different Android versions and network types (Wi-Fi, 4G, 3G) to optimize video processing times, loading speeds, and UI responsiveness.
  10. Monitor app performance and user behavior
    Integrate analytics tools like Firebase Analytics or Google Analytics to monitor app performance and user behavior. Track metrics such as video processing times, user engagement, and error rates. Use this data to optimize the app and improve user satisfaction continually.
Last updated: Aug 24, 2024