Download Image - RICOH THETA Client React Native Demo

Hi, everyone,

This guide shows you how to download an image on the user’s device on the theta-client SDK with React Native on your system with the help of an Android emulator.

Content

  • Prerequisites
  • Setup the React Native environment for the theta-client demo
  • Build the download image functionality
    • Install rn-fetch-blob as a dependency
    • Update AndroidManifest.xml file
    • Enhance the UI to download the image on a button press
  • Demo
  • Conclusion

Prerequisites

My Development Environment

I am using HP Omen that uses Windows 11 operating system… but the guide can be used for macOS and Linux as well.

Mobile platform: Android 13

Steps to setup the React Native environment for the theta-client

1. Clone the GitHub repository

On the GitHub page of theta-client, click on the “Code” dropdown button.

On the “Local” tab, click on the copy icon to copy the web URL.

Open the text editor of your choice, such as VS Code, then open the terminal, and type the following command:

git clone https://github.com/ricohapi/theta-client.git

This command clones the theta-client project on your system.

2. Go to the React Native demo

From the project root directory, go inside the React Native demo folder by following the path below:

theta-client > demos > demo-react-native

The file structure of the “demo-react-native” folder is like this:

3. Replace the API endpoint

For this demo, we do not have access to the 360-degree camera, so we cannot use the API endpoint of the camera. To get the list of images, we can use fake storage.

The API endpoint of fake storage provides a list of photos that can be used if the user does not have a camera. This API endpoint is great for testing new features.

  • To replace the API endpoint, go to MainMenu.tsx by following the path below:

demo-react-native > src > MainMenu.tsx

  • Find the endpoint variable in the code located inside the initTheta function.
const MainMenu = ({navigation}) => {
  const goTake = () => {
    navigation.navigate('take');
  };
  const goList = () => {
    navigation.navigate('list');
  };

  const initTheta = async () => {
    const endpoint = 'http://192.168.1.1';
    const config = {
      // clientMode: { // Client mode authentication settings
      //   username: 'THETAXX12345678',
      //   password: '12345678',
      // }
    };
    await initialize(endpoint, config);
    console.log('initialized.');
  };

https://fake-theta.vercel.app

const MainMenu = ({navigation}) => {
  const goTake = () => {
    navigation.navigate('take');
  };
  const goList = () => {
    navigation.navigate('list');
  };

  const initTheta = async () => {
    // const endpoint = 'http://192.168.1.1';
    const endpoint = 'https://fake-theta.vercel.app';
    const config = {
      // clientMode: { // Client mode authentication settings
      //   username: 'THETAXX12345678',
      //   password: '12345678',
      // }
    };
    await initialize(endpoint, config);
    console.log('initialized.');
  };

4. Install all dependencies

Ensure that you are on this path:

theta-client\demos\demo-react-native

Run the command below to install all the dependencies

yarn install

5. Open an Android emulator

We need a virtual Android device that is connected to our React Native app to see any changes we make to the code. For this, you can use any Android emulator, but in this guide, we are using “Android Studio”.

1. Open Android Studio

2. Go to Virtual Device Manager

Click on the “More Actions” dropdown button and click “Virtual Device Manager”.

3. Open a virtual device

Click on the “Play” icon to open any one of your virtual devices. If you do not have any virtual devices created, you can create one from here.

4. The emulator launches

Here, we have launched a Pixel 7a device.

But before actually running the project with yarn run android, we have a few things to do. It is explained in the next section.

Build the download image functionality

Now, we will work on building the functionality to download an image on a button press. Let’s start with the first step.

  1. Install rn-fetch-blob as a dependency

Open the terminal of your text editor (in my case, VS Code) and type the following command:

yarn add rn-fetch-blob

This will install the package in our project.

The dependencies on the package.json file will look like this

"dependencies": {
    "@react-navigation/native": "^6.1.0",
    "@react-navigation/native-stack": "^6.9.5",
    "marzipano": "0.10.2",
    "react": "18.2.0",
    "react-native": "0.71.14",
    "react-native-safe-area-context": "^4.4.1",
    "react-native-screens": "^3.18.2",
    "react-native-webview": "^13.6.2",
    "rn-fetch-blob": "^0.12.0",
    "theta-client-react-native": "1.9.1"
  },

You can see that the newly installed rn-fetch-blob package is present.

"rn-fetch-blob": "^0.12.0"
  1. Update AndroidManifest.xml file

Starting with Android 11 and regardless of the app’s target SDK version, you do not need to request permission for WRITE_EXTERNAL_STORAGE explicitly from the user, so there will be no user interaction required.

If you request permission for WRITE_EXTERNAL_STORAGE from the user, it will always result in ‘never_ask_again’ and you will not be able to download any image. So, you need to skip the process of requesting the permission.

However, to ensure that we still have the access to WRITE_EXTERNAL_STORAGE on lower Android versions prior to Android 11, we need to do a few things on the AndroidManifest.xml file.

  1. Go to the AndroidManifest.xml file present here: demo-react-native > android > app > src > main > AndroidManifest.xml
  2. Paste the following command above the <application> tag:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

For Android, we are going to use Android Download Manager, which is the default download manager for Android. It notifies the user when the download is finished.

  1. To use it, add the following code inside the <intent-filter> tag:
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>

The final AndroidManifest.xml should look like this:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.demoreactnative">

    <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
      android:name=".MainApplication"
      android:label="@string/app_name"
      android:icon="@mipmap/ic_launcher"
      android:roundIcon="@mipmap/ic_launcher_round"
      android:allowBackup="false"
      android:theme="@style/AppTheme">
      <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
        android:launchMode="singleTask"
        android:windowSoftInputMode="adjustResize"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
            <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
        </intent-filter>
      </activity>
    </application>
</manifest>

3. Run the app

Whenever we make changes to the AndroidManifest.xml file, we have to build the project.

Type the command below on the terminal to run the app:

yarn android

This command will execute react-native run-android

Note:

Sometimes when you try to build your project, you may encounter this error:

Solution:

  1. Create a local.properties file inside the android folder. Path: demo-react-native > android > local.properties

  1. Paste your Android SDK path on the local.properties file and save it. It will be something like this:

sdk.dir=C:\Users\UserName\AppData\Local\Android\sdk

  1. Run yarn run android again
  2. The project should run

4. Create a function to download a single image

We need to create a function that can download an image on the press of a button. Here’s how we can do it:

  1. Go to demo-react-native > src > ListPhotos.tsx
  2. Copy and paste the code below. We will understand what this code does after this.
import RNFetchBlob from 'rn-fetch-blob';

const downloadImage = async (imageFileName: string, imageUrl: string) => {
  const {config, fs} = RNFetchBlob;
  const PictureDir = fs.dirs.PictureDir;

  const date = new Date();
  const fileName = `${imageFileName}_${Math.floor(
    date.getTime() + date.getSeconds() / 2,
  )}.jpg`;

  const options = {
    fileCache: true,
    addAndroidDownloads: {
      useDownloadManager: true,
      notification: true,
      path: `${PictureDir}/${fileName}`,
      description: 'Downloading image.',
    },
  };

  config(options)
    .fetch('GET', imageUrl)
    .progress((received, total) => {
      const percentage = Math.round((received / total) * 100);
    })
    .then(res => {
      Alert.alert('Download Success', 'Image downloaded successfully.');
    })
    .catch(err => {
      Alert.alert('Download Failed', 'File download failed.');
    });
};

Breakdown of the code

In the above code, we have imported the rn-fetch-blob module using ES6 import statement.

import RNFetchBlob from 'rn-fetch-blob'

Next, we have declared a function that takes two parameters in the form of strings:

  • imageFileName: Name of the image
  • imageUrl: URL of the image

We have destructured the RNFetchBlob object and taken out config and fs properties. We will use config to send a few additional information for downloading the image. We will use fs to access the file system of the user’s device.

const {config, fs} = RNFetchBlob;

We are going to store the downloaded image inside the Pictures directory on the user’s virtual device. For that, we have chosen the PictureDir from the file access API of RNFetchBlob and assigned it to the PictureDir variable.

const PictureDir = fs.dirs.PictureDir;

Next, we are utilizing the Date objects of JavaScript to give a unique file name to the downloaded image.

 const date = new Date();
  const fileName = `${imageFileName}_${Math.floor(
    date.getTime() + date.getSeconds() / 2,
  )}.jpg`;

Before sending the GET request for downloading the image, we have to send a few additional information to the config method of RNFetchBlob. This information include:

  • fileCache: We want the downloaded image to be cached hence, the value is set to true.
  • addAndroidDownloads: This is optional information and is only specified for the Android operating system.
    • useDownloadManager: We are going to use the native Android Download Manager, so the value is true.
    • notification: When image download succeeds or fails, the user will be notified.
    • path: The destination path where the image will be downloaded and stored.
    • description: The description of the downloaded image
const options = {
    fileCache: true,
    addAndroidDownloads: {
      useDownloadManager: true,
      notification: true,
      path: `${PictureDir}/${fileName}`,
      description: 'Downloading image.',
    },
  };

Finally, we can perform a GET request with JavaScript Fetch API by initializing the config method for the download request.

config(options)
    .fetch('GET', imageUrl)
    .progress((received, total) => {
      const percentage = Math.round((received / total) * 100);
    })
    .then(res => {
      Alert.alert('Download Success', 'Image downloaded successfully.');
    })
    .catch(err => {
      Alert.alert('Download Failed', 'Image download failed.');
    });
};

In the above code, we have also attached a progress method on our GET request. This optional method will inform us how much downloaded data it has received from the server. We can use this to show a progress bar or an indicator to show the progress of the download operation.

In case of success or failure, an alert pop-up will appear on the user’s device with the particular title and description as specified in the alert method.

5. Create a “Download” button for images

To actually perform the download operation, we have to call the downloadImage function but where should we call it?

We will create a simple Button component and attach it to each image. When the user presses the button, the particular image attached with the button will be downloaded.

To create a button:

  1. Ensure that you are on this path: demo-react-native > src > ListPhotos.tsx
  2. Import Button component from React Native
import { Button } from 'react-native';
  1. Go to the items variable
  2. Modify the items variable to include the Button component.
const items = files.map(item => (
    <TouchableOpacity
      style={styles.fileItemBase}
      key={item.name}
      onPress={() => onSelect(item)}>
      <Image style={styles.thumbnail} source={{uri: item.thumbnailUrl}} />
      <View
        style={{
          width: Dimensions.get('window').width - 108,
        }}>
        <View style={styles.largeSpacer} />
        <View
          style={{
            flexDirection: 'row',
            alignItems: 'center',
            justifyContent: 'space-between',
          }}>
          <Text style={styles.fileName}>{item.name}</Text>
          <Button
            title="Download"
            onPress={() => downloadImage(item.name, item.fileUrl)}
          />
        </View>
        <View style={styles.largeSpacer} />
      </View>
    </TouchableOpacity>
  ));

In the above code, we have created the Button inside a map function, which maps over the files variable and displays each image in a single column based layout.

We have attached an onPress event on the button that will call the downloadImage function and pass the name of the item and the URL of the file as the two arguments.

  1. Output

Demo

We have built the functionality of downloading and storing the image on the user’s device. Now it is time for some action.

Here are the steps we will perform:

  1. When the react native demo runs, the home screen on the Android virtual device will be like this:

  1. Click on “List Photos” button

  1. A screen will open displaying a list of images. Click on the “Download” button of any image to download it. In this case, let’s download this image: “R0010006.JPG”

  1. An alert will appear on the screen when the image is successfully downloaded.

  1. The Android Download Manager will notify the completion of the image download operation through a notification too.

  1. Let’s see the storage to check if the newly downloaded image is present. Open the “Files” app on the virtual device.

  1. The “Downloads” screen will open. Press on the Hamburger menu icon to open the sidebar.

  1. Press the “Images” option

  1. You will see that the “Pictures” directory has been created. Press on the “Pictures” button to open it.

  1. On the “Pictures” screen, the newly downloaded image is present!

  1. You can press the image to open it

  1. The image will also be present on the “Google Photos” app

Conclusion

In this guide, we saw how to set up the React Native environment for the theta-client demo and perform the functionality to download any image to the “Pictures” directory on a user’s device.

Feel free to leave a comment in case of any doubts about this.

1 Like