Android SDK React Native<> Kotlin Bridge

Sample Project for React native (Kotlin-React Bridge)

GitHub: Link | Please request access to Spyne Team
Branch Name: react_native_kotlin_bridge_android

SpyneModule.java

(android/app/src/main/java/com.your.package.name): This class defines the native module that interacts with the Spyne library. It's located in your app's package directory within the Android project.

SpynePackage.java

(android/app/src/main/java/com.your.package.name): This class defines a React package that registers the SpyneModule. It's located in your app's package directory within the Android project.

MainApplication.java

(android/app/src/main/java/com.your.package.name): This is the main application class where you initialize the Spyne library and register the SpynePackage. It's located in your app's package directory within the Android project.
We need to update the enterprise-specific API key in this class.

AndroidBridging.js (Your React Native project root directory)

This JavaScript file defines functions that interact with the SpyneModule methods. It's located in the root directory of your React Native project.

Add dependency in build.gradle (module)

// Spyne Sdk
implementation "com.github.Spyne-Tech:spyne-android-sdk:3.0.3"

In Project-level Build.gradle verify minSdkVersion, compileSdkVersion and targetSdkVersion should be
minSdkVersion = 26
compileSdkVersion = 34
targetSdkVersion = 34

Add this for jitpack Authorization in Project-level Build.gradle

allprojects {  
repositories {  
       google()  
       mavenCentral()  
       maven {  
           url "<https://jitpack.io">  
           credentials { username = project.properties['authToken'] }  
       }  
   }  
}

And ‘authToken’ value will be in gradle.properties

authToken=jp_g1e1t35osldunpv5a8u4dcjovr

File Structure:

project-root/
├── android/
│ ├── app/
│ │ └── src/
│ │ └── main/
│ │ └── java/
│ │ └── com/
│ │ └── yourapp
│ │ ├── SpyneModule.java // Java class
│ │ └── SpynePackage.java // Java class
│ │ └── MainApplication

├── androidBridging.ts // TypeScript file defining the bridge to call the SpyneModule methods


└── app.tsx // // Main React Native application file

App.tsx file:

import React, { useEffect } from 'react';  
import {  
 Platform,  
 View,  
 Text,  
 NativeModules,  
 NativeEventEmitter,  
 TouchableOpacity,  
 Button,  
 StyleSheet,  
} from 'react-native';  
import { start } from './androidBridging'; // Import the Android bridging module

const App = () => {  
   const handleStart = () => {  
       start('[[email protected]](mailto:[email protected])', 'uniqueTest123', 'prod_seY3vxhATCH', false);  
   };

   const handleStart360 = () => {  
       start('[[email protected]](mailto:[email protected])', 'uniqueTest123', 'prod_seY3vxhATCH', true);  
   };

   return (  
       <View style={styles.container}>  
         <Button title="Shoot Images" onPress={handleStart} style={styles.button} />  
         <View style={styles.spacer} />  
         <Button title="Shoot Video" onPress={handleStart360} style={styles.button} />  
       </View>  
     );  
   };  
const styles = StyleSheet.create({  
     container: { flex: 1, justifyContent: 'center',alignItems: 'center',},  
     button: { marginVertical: 10,},  
     spacer: { height: 20,},  
   });
export default App;

SpyneModule.java:

@ReactModule(name = SpyneModule.NAME)

@ReactModule(name = SpyneModule.NAME)
public class SpyneModule extends ReactContextBaseJavaModule implements Spyne.SkuListener {
   public static final String NAME = "SpyneModule";


   public SpyneModule(ReactApplicationContext reactContext) {
       super(reactContext);
   }


   @NonNull
   @Override
   public String getName() {
       return "Spyne";
   }


   @ReactMethod
   public void start(String userID, String uniqueId, String vehicleType, Boolean is360) {
       Activity activity = getCurrentActivity();
       if (activity != null) {
           start(activity, userID, uniqueId, vehicleType, is360);
       }
   }


   public void start(Context context, String userID, String uniqueId, String vehicleType, Boolean is360) {
       Activity activity = getCurrentActivity();
       if (activity != null) {
           Spyne.ShootBuilder builder = new Spyne.ShootBuilder(
                   context,
                   userID,
                   this,
                   8);


           builder.uniqueShootId(uniqueId)
                   .shootType(ShootType.SHOOT)
                   .classifier(Classifier.RESTRICTIVE)
                   .gyroMeter(Gyrometer.RESTRICTIVE)
                   .subcategoryId(vehicleType)
                   .syncProject(true);


           Spyne spyne = builder.build();


           if (is360) {
               spyne.startVideoThreeSixty(); // for video shoot flow
           } else {
               spyne.start(); // for catalog
           }
       }
   }


   @Override
   public void onShootCompleted(@NonNull String skuId, boolean isReshoot, boolean isThreeSixty) {
       // SKU ID will receive at the time of project completion
   }


   @Override
   public void onSkuCreated(@NonNull String skuId) {
       // SKU ID will receive at the time of creation of project
   }


   @Override
   public void onProgressChanges(@NonNull String s, double v) {
       // Handle progress changes
   }
}

SpynePackage.java:

public class SpynePackage implements ReactPackage {
   @NonNull
   @Override
   public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
       return Arrays.<NativeModule>asList(new SpyneModule(reactContext));
   }


   @NonNull
   @Override
   public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
       return Collections.emptyList();
   }
}

androidBridging.js :

import { NativeModules } from 'react-native';


const Spyne = NativeModules.Spyne
   ? NativeModules.Spyne
   : new Proxy(
       {},
       {
           get() {
               throw new Error('Spyne module not linked');
           },
       }
   );


export const start = (userID, uniqueId, vehicleType, is360) => {
   return Spyne.start(userID, uniqueId, vehicleType, is360);
}

MainApplication :

class MainApplication : Application(), ReactApplication {
   private val API_KEY = "Enter your Enterprise API Key"
   private val CATEGORY_ID = "cat_d8R14zUNE"


 override val reactNativeHost: ReactNativeHost =
     object : DefaultReactNativeHost(this) {
       override fun getPackages(): List<ReactPackage> =
           PackageList(this).packages.apply {
             // Packages that cannot be autolinked yet can be added manually here, for example:
             // add(MyReactNativePackage())
               add(SpynePackage())
           }


       override fun getJSMainModuleName(): String = "index"


       override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG


       override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
       override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
     }


 override val reactHost: ReactHost
   get() = getDefaultReactHost(applicationContext, reactNativeHost)


 override fun onCreate() {
   super.onCreate()
   SoLoader.init(this, false)
     Spyne.Companion.init(this, API_KEY, CATEGORY_ID)
   if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
     // If you opted-in for the New Architecture, we load the native entry point for this app.
     load()
   }
 }
}