From 4f97b269c1b6289018b6048a8668761cd6e42a9e Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Thu, 14 Nov 2024 12:47:15 -0800 Subject: [PATCH 1/4] Committing Directory Structure for generated applications --- .../webview-ml-shell/app/src/main/assets/gradio_gen/README | 1 + .../android/webview-ml-shell/app/src/main/assets/js/README | 1 + .../android/webview-ml-shell/app/src/main/assets/models/README | 1 + 3 files changed, 3 insertions(+) create mode 100644 src/templates/android/webview-ml-shell/app/src/main/assets/gradio_gen/README create mode 100644 src/templates/android/webview-ml-shell/app/src/main/assets/js/README create mode 100644 src/templates/android/webview-ml-shell/app/src/main/assets/models/README diff --git a/src/templates/android/webview-ml-shell/app/src/main/assets/gradio_gen/README b/src/templates/android/webview-ml-shell/app/src/main/assets/gradio_gen/README new file mode 100644 index 0000000..31363f2 --- /dev/null +++ b/src/templates/android/webview-ml-shell/app/src/main/assets/gradio_gen/README @@ -0,0 +1 @@ +Generated HTML from Gradio Applications goes here diff --git a/src/templates/android/webview-ml-shell/app/src/main/assets/js/README b/src/templates/android/webview-ml-shell/app/src/main/assets/js/README new file mode 100644 index 0000000..4eb7510 --- /dev/null +++ b/src/templates/android/webview-ml-shell/app/src/main/assets/js/README @@ -0,0 +1 @@ +DayPack Local Javscript goes here diff --git a/src/templates/android/webview-ml-shell/app/src/main/assets/models/README b/src/templates/android/webview-ml-shell/app/src/main/assets/models/README new file mode 100644 index 0000000..8f8a5aa --- /dev/null +++ b/src/templates/android/webview-ml-shell/app/src/main/assets/models/README @@ -0,0 +1 @@ +Model Files Go Here From b46d9087fd64bd6ba508af969e1eefaca5b90804 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Sat, 16 Nov 2024 18:43:02 -0800 Subject: [PATCH 2/4] Starting to scaffold out WebView ML shell --- .../webview-ml-shell/app/build.gradle.kts | 3 ++ .../app/src/main/cpp/CMakeLists.txt | 15 +++++++ .../app/src/main/cpp/lite_rt_model.cpp | 43 +++++++++++++++++++ .../app/src/main/cpp/lite_rt_model.h | 16 +++++++ .../app/src/main/cpp/model_manager.cpp | 0 .../app/src/main/cpp/model_manager.h | 0 .../app/src/main/cpp/native-lib.cpp | 27 +++++++++++- .../ai/baseweight/daypack/MainActivity.kt | 28 +++++++++--- .../ai/baseweight/daypack/ModelManager.kt | 14 ++++++ .../gradle/libs.versions.toml | 2 + 10 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 src/templates/android/webview-ml-shell/app/src/main/cpp/lite_rt_model.cpp create mode 100644 src/templates/android/webview-ml-shell/app/src/main/cpp/lite_rt_model.h create mode 100644 src/templates/android/webview-ml-shell/app/src/main/cpp/model_manager.cpp create mode 100644 src/templates/android/webview-ml-shell/app/src/main/cpp/model_manager.h create mode 100644 src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/ModelManager.kt diff --git a/src/templates/android/webview-ml-shell/app/build.gradle.kts b/src/templates/android/webview-ml-shell/app/build.gradle.kts index 4dc48ca..341b6e9 100644 --- a/src/templates/android/webview-ml-shell/app/build.gradle.kts +++ b/src/templates/android/webview-ml-shell/app/build.gradle.kts @@ -41,16 +41,19 @@ android { } buildFeatures { viewBinding = true + prefab = true } } dependencies { implementation("androidx.webkit:webkit:1.9.0") + implementation("com.google.android.gms:play-services-tflite-java:16.2.0-beta02") implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) implementation(libs.material) implementation(libs.androidx.constraintlayout) + implementation(libs.play.services.gcm) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/src/templates/android/webview-ml-shell/app/src/main/cpp/CMakeLists.txt b/src/templates/android/webview-ml-shell/app/src/main/cpp/CMakeLists.txt index 237390f..1792154 100644 --- a/src/templates/android/webview-ml-shell/app/src/main/cpp/CMakeLists.txt +++ b/src/templates/android/webview-ml-shell/app/src/main/cpp/CMakeLists.txt @@ -11,6 +11,21 @@ cmake_minimum_required(VERSION 3.22.1) # build script scope). project("daypack") + +find_package(tensorflowlite_jni_gms_client REQUIRED CONFIG) + +target_link_libraries(tflite-jni # your JNI lib target + tensorflowlite_jni_gms_client::tensorflowlite_jni_gms_client + android # other deps for your target + log) + +# Also add -DTFLITE_IN_GMSCORE -DTFLITE_WITH_STABLE_ABI +# to the C/C++ compiler flags. + +add_compile_definitions(TFLITE_IN_GMSCORE) +add_compile_definitions(TFLITE_WITH_STABLE_ABI) + + # Creates and names a library, sets it as either STATIC # or SHARED, and provides the relative paths to its source code. # You can define multiple libraries, and CMake builds them for you. diff --git a/src/templates/android/webview-ml-shell/app/src/main/cpp/lite_rt_model.cpp b/src/templates/android/webview-ml-shell/app/src/main/cpp/lite_rt_model.cpp new file mode 100644 index 0000000..ef3df30 --- /dev/null +++ b/src/templates/android/webview-ml-shell/app/src/main/cpp/lite_rt_model.cpp @@ -0,0 +1,43 @@ +// TFLite C API +#include "tensorflow/lite/c/c_api.h" +#include + +#define PRE_TAG "Model Handler Code" +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, PRE_TAG, __VA_ARGS__) + +LiteRTModel::LiteRTModel(const char* modelPath) { + model = TfLiteModelCreateFromFile(fullPath.c_str()); + mOptions = TfLiteInterpreterOptionsCreate(); + + if(getDevice() == MLStats::Device::NNAPI) + { + TfLiteNnapiDelegateOptions options = TfLiteNnapiDelegateOptionsDefault(); + delegate = TfLiteNnapiDelegateCreate(&options); + TfLiteInterpreterOptionsAddDelegate(mOptions, delegate); + } + else if(getDevice() == MLStats::Device::GPU) + { + TfLiteGpuDelegateOptionsV2 options = TfLiteGpuDelegateOptionsV2Default(); + delegate = TfLiteGpuDelegateV2Create(&options); + TfLiteInterpreterOptionsAddDelegate(mOptions, delegate); + } +} + + +LiteRTModel::~LiteRTModel() { + TfLiteInterpreterDelete(interpreter); + TfLiteInterpreterOptionsDelete(mOptions); + if(delegate != nullptr) + { + if(getDevice() == MLStats::Device::NNAPI) + { + TfLiteNnapiDelegateDelete(delegate); + } + else + { + TfLiteGpuDelegateV2Delete(delegate); + } + } + + TfLiteModelDelete(model); +} diff --git a/src/templates/android/webview-ml-shell/app/src/main/cpp/lite_rt_model.h b/src/templates/android/webview-ml-shell/app/src/main/cpp/lite_rt_model.h new file mode 100644 index 0000000..c9ff770 --- /dev/null +++ b/src/templates/android/webview-ml-shell/app/src/main/cpp/lite_rt_model.h @@ -0,0 +1,16 @@ +#include "tensorflow/lite/c/c_api.h" +#include + +class LiteRTModel { + + public: + LiteRTModel(std::string modelPath); + ~LiteRTModel(); + private: + // Not thrilled about using raw pointers here, however LiteRT controls + // the lifecycle here, and I don't want to use the STL and have them blow up + TfLiteModel * model; + TfLiteInterpreterOptions * mOptions; + TfLiteInterpreter* interpreter; + TfLiteDelegate* delegate = nullptr; +} \ No newline at end of file diff --git a/src/templates/android/webview-ml-shell/app/src/main/cpp/model_manager.cpp b/src/templates/android/webview-ml-shell/app/src/main/cpp/model_manager.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/templates/android/webview-ml-shell/app/src/main/cpp/model_manager.h b/src/templates/android/webview-ml-shell/app/src/main/cpp/model_manager.h new file mode 100644 index 0000000..e69de29 diff --git a/src/templates/android/webview-ml-shell/app/src/main/cpp/native-lib.cpp b/src/templates/android/webview-ml-shell/app/src/main/cpp/native-lib.cpp index c14fd4a..ae4b889 100644 --- a/src/templates/android/webview-ml-shell/app/src/main/cpp/native-lib.cpp +++ b/src/templates/android/webview-ml-shell/app/src/main/cpp/native-lib.cpp @@ -1,11 +1,36 @@ #include #include +// Bring in + extern "C" JNIEXPORT jstring JNICALL Java_ai_baseweight_daypack_MainActivity_stringFromJNI( JNIEnv* env, - jobject /* this */) { + jobject /* this */, + js) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); } +extern "C" JNIEXPORT jboolean JNICALL +Java_ai_baseweight_daypack_MainActivity_isModelLoaded( + JNIEnv* env, + jobject /* this */, + jstring modelId) { + // TODO: Implement model loading check + return false; +} + +extern "C" JNIEXPORT void JNICALL +Java_ai_baseweight_daypack_MainActivity_callModel( + JNIEnv* env, + jobject /* this */, + jobjectArray inputNames, + jobjectArray inputShapes, + jobjectArray inputBuffers, + jobjectArray outputNames, + jobjectArray outputShapes, + jobjectArray outputBuffers) { + // TODO: Implement model inference +} + diff --git a/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/MainActivity.kt b/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/MainActivity.kt index 6091053..0dff34f 100644 --- a/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/MainActivity.kt +++ b/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/MainActivity.kt @@ -4,6 +4,8 @@ import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import ai.baseweight.daypack.databinding.ActivityMainBinding import android.webkit.JavascriptInterface +import com.google.android.gms.tasks.Task +import com.google.android.gms.tflite.java.TfLiteNative import org.json.JSONArray import org.json.JSONObject @@ -14,12 +16,13 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + val tfLiteInitializeTask: Task = TfLiteNative.initialize(this) + binding = ActivityMainBinding.inflate(layoutInflater) binding.webview.settings.javaScriptEnabled = true binding.webview.addJavascriptInterface(WebAppInterface(this), "AndroidMLExec") setContentView(binding.root); binding.webview.loadUrl(resources.getString(R.string.launch_url)) - } /** @@ -28,19 +31,26 @@ class MainActivity : AppCompatActivity() { */ external fun stringFromJNI(): String + external fun isModelLoaded(modelId: String) : Boolean + external fun callModel( + inputNames: Array, + inputShapes: Array, + inputBuffers: Array, + outputNames: Array, + outputShapes: Array, + outputBuffers: Array + ) + companion object { // Used to load the 'daypack' library on application startup. init { + System.loadLibrary("tflite-jni") System.loadLibrary("daypack") } } // Add this class class WebAppInterface(private val context: MainActivity) { - @JavascriptInterface - fun stringFromJNI(): String { - return context.stringFromJNI() - } @JavascriptInterface fun getInstalledModels(callback: String) { @@ -50,6 +60,14 @@ class MainActivity : AppCompatActivity() { context.binding.webview.evaluateJavascript("$callback($jsonModels)", null) } + @JavascriptInterface + fun getLoadedModels(callback: String) { + // TODO: Implement native model listing + val models = arrayOf() + val jsonModels = JSONArray(models) + context.binding.webview.evaluateJavascript("$callback($jsonModels)", null) + } + @JavascriptInterface fun isModelInstalled(modelId: String, callback: String) { // TODO: Implement model existence check diff --git a/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/ModelManager.kt b/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/ModelManager.kt new file mode 100644 index 0000000..7f410dd --- /dev/null +++ b/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/ModelManager.kt @@ -0,0 +1,14 @@ +package ai.baseweight.daypack + +class Model() { + private val name: String + private val path: String + private val type: String + private val inputs: List + private val outputs: List +} + +class ModelManager() { + private val models = mutableMapOf() + +} \ No newline at end of file diff --git a/src/templates/android/webview-ml-shell/gradle/libs.versions.toml b/src/templates/android/webview-ml-shell/gradle/libs.versions.toml index 523e331..a08d41c 100644 --- a/src/templates/android/webview-ml-shell/gradle/libs.versions.toml +++ b/src/templates/android/webview-ml-shell/gradle/libs.versions.toml @@ -9,6 +9,7 @@ appcompat = "1.7.0" material = "1.12.0" constraintlayout = "2.1.4" webkit = "1.9.0" +playServicesGcm = "17.0.0" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -19,6 +20,7 @@ androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version material = { group = "com.google.android.material", name = "material", version.ref = "material" } androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } androidx-webkit = { group = "androidx.webkit", name="webkit", version.ref="webkit"} +play-services-gcm = { group = "com.google.android.gms", name = "play-services-gcm", version.ref = "playServicesGcm" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } From 3860dd0c3b505bd0230f55f1de160429f6ea07b0 Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Sun, 17 Nov 2024 18:11:25 -0800 Subject: [PATCH 3/4] No longer using TFLite, working on model downloader --- .../webview-ml-shell/app/build.gradle.kts | 6 +- .../app/src/main/assets/models/models.json | 3 + .../app/src/main/cpp/CMakeLists.txt | 18 +--- .../app/src/main/cpp/native-lib.cpp | 68 ++++++++++++-- .../ai/baseweight/daypack/MainActivity.kt | 29 +++--- .../ai/baseweight/daypack/ModelManager.kt | 92 +++++++++++++++++-- .../ai/baseweight/daypack/ModelMetadata.kt | 9 ++ .../gradle/libs.versions.toml | 4 + 8 files changed, 179 insertions(+), 50 deletions(-) create mode 100644 src/templates/android/webview-ml-shell/app/src/main/assets/models/models.json create mode 100644 src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/ModelMetadata.kt diff --git a/src/templates/android/webview-ml-shell/app/build.gradle.kts b/src/templates/android/webview-ml-shell/app/build.gradle.kts index 341b6e9..97233b1 100644 --- a/src/templates/android/webview-ml-shell/app/build.gradle.kts +++ b/src/templates/android/webview-ml-shell/app/build.gradle.kts @@ -46,14 +46,14 @@ android { } dependencies { - - implementation("androidx.webkit:webkit:1.9.0") - implementation("com.google.android.gms:play-services-tflite-java:16.2.0-beta02") + implementation(libs.google.gson) + implementation(libs.androidx.webkit) implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) implementation(libs.material) implementation(libs.androidx.constraintlayout) implementation(libs.play.services.gcm) + implementation(libs.firebase.crashlytics.buildtools) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/src/templates/android/webview-ml-shell/app/src/main/assets/models/models.json b/src/templates/android/webview-ml-shell/app/src/main/assets/models/models.json new file mode 100644 index 0000000..c44dc44 --- /dev/null +++ b/src/templates/android/webview-ml-shell/app/src/main/assets/models/models.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/src/templates/android/webview-ml-shell/app/src/main/cpp/CMakeLists.txt b/src/templates/android/webview-ml-shell/app/src/main/cpp/CMakeLists.txt index 1792154..97e1cd8 100644 --- a/src/templates/android/webview-ml-shell/app/src/main/cpp/CMakeLists.txt +++ b/src/templates/android/webview-ml-shell/app/src/main/cpp/CMakeLists.txt @@ -11,21 +11,6 @@ cmake_minimum_required(VERSION 3.22.1) # build script scope). project("daypack") - -find_package(tensorflowlite_jni_gms_client REQUIRED CONFIG) - -target_link_libraries(tflite-jni # your JNI lib target - tensorflowlite_jni_gms_client::tensorflowlite_jni_gms_client - android # other deps for your target - log) - -# Also add -DTFLITE_IN_GMSCORE -DTFLITE_WITH_STABLE_ABI -# to the C/C++ compiler flags. - -add_compile_definitions(TFLITE_IN_GMSCORE) -add_compile_definitions(TFLITE_WITH_STABLE_ABI) - - # Creates and names a library, sets it as either STATIC # or SHARED, and provides the relative paths to its source code. # You can define multiple libraries, and CMake builds them for you. @@ -41,7 +26,8 @@ add_compile_definitions(TFLITE_WITH_STABLE_ABI) # used in the AndroidManifest.xml file. add_library(${CMAKE_PROJECT_NAME} SHARED # List C/C++ source files with relative paths to this CMakeLists.txt. - native-lib.cpp) + native-lib.cpp + model_manager.cpp) # Specifies libraries CMake should link to your target library. You # can link libraries from various origins, such as libraries defined in this diff --git a/src/templates/android/webview-ml-shell/app/src/main/cpp/native-lib.cpp b/src/templates/android/webview-ml-shell/app/src/main/cpp/native-lib.cpp index ae4b889..d2517d4 100644 --- a/src/templates/android/webview-ml-shell/app/src/main/cpp/native-lib.cpp +++ b/src/templates/android/webview-ml-shell/app/src/main/cpp/native-lib.cpp @@ -1,17 +1,29 @@ #include #include -// Bring in -extern "C" JNIEXPORT jstring JNICALL -Java_ai_baseweight_daypack_MainActivity_stringFromJNI( + +extern "C" JNIEXPORT jboolean JNICALL +Java_ai_baseweight_daypack_MainActivity_loadModel( JNIEnv* env, jobject /* this */, - js) { - std::string hello = "Hello from C++"; - return env->NewStringUTF(hello.c_str()); + jstring modelId, + jstring modelPath) { + // Convert Java strings to C++ strings + const char* id_chars = env->GetStringUTFChars(modelId, nullptr); + const char* path_chars = env->GetStringUTFChars(modelPath, nullptr); + std::string id(id_chars); + std::string path(path_chars); + + // Release the string resources + env->ReleaseStringUTFChars(modelId, id_chars); + env->ReleaseStringUTFChars(modelPath, path_chars); + + // TODO: Implement actual model loading logic + return false; } + extern "C" JNIEXPORT jboolean JNICALL Java_ai_baseweight_daypack_MainActivity_isModelLoaded( JNIEnv* env, @@ -21,16 +33,56 @@ Java_ai_baseweight_daypack_MainActivity_isModelLoaded( return false; } + + extern "C" JNIEXPORT void JNICALL Java_ai_baseweight_daypack_MainActivity_callModel( JNIEnv* env, jobject /* this */, + jstring modelId, jobjectArray inputNames, jobjectArray inputShapes, jobjectArray inputBuffers, + jint numInputs, jobjectArray outputNames, jobjectArray outputShapes, - jobjectArray outputBuffers) { - // TODO: Implement model inference + jobjectArray outputBuffers, + jint numOutputs) { + // Convert Java arrays to C++ vectors + std::vector input_names; + std::vector output_names; + std::vector input_buffers; + std::vector output_buffers; + + // Extract input names + for (int i = 0; i < numInputs; i++) { + jstring str = (jstring)env->GetObjectArrayElement(inputNames, i); + const char* chars = env->GetStringUTFChars(str, nullptr); + input_names.push_back(std::string(chars)); + env->ReleaseStringUTFChars(str, chars); + env->DeleteLocalRef(str); + } + + // Extract output names + for (int i = 0; i < numOutputs; i++) { + jstring str = (jstring)env->GetObjectArrayElement(outputNames, i); + const char* chars = env->GetStringUTFChars(str, nullptr); + output_names.push_back(std::string(chars)); + env->ReleaseStringUTFChars(str, chars); + env->DeleteLocalRef(str); + } + + // Extract input buffers + for (int i = 0; i < numInputs; i++) { + jfloatArray buffer = (jfloatArray)env->GetObjectArrayElement(inputBuffers, i); + input_buffers.push_back(buffer); + } + + // Extract output buffers + for (int i = 0; i < numOutputs; i++) { + jfloatArray buffer = (jfloatArray)env->GetObjectArrayElement(outputBuffers, i); + output_buffers.push_back(buffer); + } + } diff --git a/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/MainActivity.kt b/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/MainActivity.kt index 0dff34f..ba08b8e 100644 --- a/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/MainActivity.kt +++ b/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/MainActivity.kt @@ -4,19 +4,19 @@ import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import ai.baseweight.daypack.databinding.ActivityMainBinding import android.webkit.JavascriptInterface -import com.google.android.gms.tasks.Task -import com.google.android.gms.tflite.java.TfLiteNative import org.json.JSONArray import org.json.JSONObject +import kotlinx.coroutines.* + class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding + val modelManager = ModelManager(this) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - - val tfLiteInitializeTask: Task = TfLiteNative.initialize(this) + startDownloadThread() binding = ActivityMainBinding.inflate(layoutInflater) binding.webview.settings.javaScriptEnabled = true @@ -25,26 +25,32 @@ class MainActivity : AppCompatActivity() { binding.webview.loadUrl(resources.getString(R.string.launch_url)) } + fun startDownloadThread() = runBlocking { + val job = withContext(Dispatchers.IO) { + modelManager.downloadModels() + } + } + /** * A native method that is implemented by the 'daypack' native library, * which is packaged with this application. */ - external fun stringFromJNI(): String - + external fun loadModel(modelId: String, path: String) : Boolean external fun isModelLoaded(modelId: String) : Boolean external fun callModel( inputNames: Array, inputShapes: Array, inputBuffers: Array, + numInputs: Int, outputNames: Array, outputShapes: Array, - outputBuffers: Array + outputBuffers: Array, + numOutputs: Int ) companion object { // Used to load the 'daypack' library on application startup. init { - System.loadLibrary("tflite-jni") System.loadLibrary("daypack") } } @@ -54,16 +60,13 @@ class MainActivity : AppCompatActivity() { @JavascriptInterface fun getInstalledModels(callback: String) { - // TODO: Implement native model listing - val models = arrayOf() - val jsonModels = JSONArray(models) + val jsonModels = context.modelManager.getInstalledModels() context.binding.webview.evaluateJavascript("$callback($jsonModels)", null) } @JavascriptInterface fun getLoadedModels(callback: String) { - // TODO: Implement native model listing - val models = arrayOf() + val jsonModels = JSONArray(models) context.binding.webview.evaluateJavascript("$callback($jsonModels)", null) } diff --git a/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/ModelManager.kt b/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/ModelManager.kt index 7f410dd..369c2b4 100644 --- a/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/ModelManager.kt +++ b/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/ModelManager.kt @@ -1,14 +1,86 @@ package ai.baseweight.daypack -class Model() { - private val name: String - private val path: String - private val type: String - private val inputs: List - private val outputs: List -} - -class ModelManager() { - private val models = mutableMapOf() +import android.content.Context +import android.net.Uri +import com.google.gson.Gson + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.File +import java.io.FileOutputStream +import java.io.InputStreamReader +import java.net.URL + +class ModelManager(val context: Context) { + private val models = mutableMapOf() + + init { + try { + val inputStream = context.assets.open("models/models.json") + val reader = InputStreamReader(inputStream) + val modelMetadataList = Gson().fromJson(reader, Array::class.java) + reader.close() + inputStream.close() + modelMetadataList.forEach { + models[it.name] = it + } + } + catch (e: Exception) { + e.printStackTrace() + } + } + + fun getInstalledModels(): String? { + val metadataList = mutableListOf() + models.forEach { + if(it.value?.isInstalled == true) { + metadataList.add(it.value!!) + } + } + return Gson().toJson(metadataList) + } + + + + suspend fun downloadModels() + { + models.forEach { + fetchAndSaveModel(context, Uri.parse(it.value?.path)) + it.value?.isInstalled = true + } + } + + + + suspend fun fetchAndSaveModel(context: Context, modelUri: Uri) { + withContext(Dispatchers.IO) { + try { + val url = URL(modelUri.toString()) + val connection = url.openConnection() + val inputStream = connection.getInputStream() + + // Save the model to the app's external storage + val fileName = modelUri.lastPathSegment ?: "model.pte" + val file = File(context.getExternalFilesDir(null), fileName) + val outputStream = FileOutputStream(file) + + val buffer = ByteArray(4096) + var bytesRead: Int + + while (inputStream.read(buffer).also { bytesRead = it } != -1) { + outputStream.write(buffer, 0, bytesRead) + } + + inputStream.close() + outputStream.close() + + // Model saved successfully + } catch (e: Exception) { + // Handle exceptions (e.g., network errors, file I/O errors) + e.printStackTrace() + } + } + } + } \ No newline at end of file diff --git a/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/ModelMetadata.kt b/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/ModelMetadata.kt new file mode 100644 index 0000000..8a4835f --- /dev/null +++ b/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/ModelMetadata.kt @@ -0,0 +1,9 @@ +package ai.baseweight.daypack + +class ModelMetadata(val name: String, val path: String, + val type: String, val inputs: List, + val outputs: List) +{ + var isLoaded: Boolean = false + var isInstalled: Boolean = false +} diff --git a/src/templates/android/webview-ml-shell/gradle/libs.versions.toml b/src/templates/android/webview-ml-shell/gradle/libs.versions.toml index a08d41c..1dd268a 100644 --- a/src/templates/android/webview-ml-shell/gradle/libs.versions.toml +++ b/src/templates/android/webview-ml-shell/gradle/libs.versions.toml @@ -10,8 +10,11 @@ material = "1.12.0" constraintlayout = "2.1.4" webkit = "1.9.0" playServicesGcm = "17.0.0" +firebaseCrashlyticsBuildtools = "3.0.2" +gson = "2.10" [libraries] +google-gson = {group="com.google.code.gson", name="gson", version.ref="gson"} androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } @@ -21,6 +24,7 @@ material = { group = "com.google.android.material", name = "material", version.r androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } androidx-webkit = { group = "androidx.webkit", name="webkit", version.ref="webkit"} play-services-gcm = { group = "com.google.android.gms", name = "play-services-gcm", version.ref = "playServicesGcm" } +firebase-crashlytics-buildtools = { group = "com.google.firebase", name = "firebase-crashlytics-buildtools", version.ref = "firebaseCrashlyticsBuildtools" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } From ce5eb830340bb6483b653a61d180af0fc01a3ddc Mon Sep 17 00:00:00 2001 From: Joe Bowser Date: Sun, 17 Nov 2024 18:11:54 -0800 Subject: [PATCH 4/4] Deleting code we're not using --- .../app/src/main/cpp/lite_rt_model.cpp | 43 ------------------- .../app/src/main/cpp/lite_rt_model.h | 16 ------- 2 files changed, 59 deletions(-) delete mode 100644 src/templates/android/webview-ml-shell/app/src/main/cpp/lite_rt_model.cpp delete mode 100644 src/templates/android/webview-ml-shell/app/src/main/cpp/lite_rt_model.h diff --git a/src/templates/android/webview-ml-shell/app/src/main/cpp/lite_rt_model.cpp b/src/templates/android/webview-ml-shell/app/src/main/cpp/lite_rt_model.cpp deleted file mode 100644 index ef3df30..0000000 --- a/src/templates/android/webview-ml-shell/app/src/main/cpp/lite_rt_model.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// TFLite C API -#include "tensorflow/lite/c/c_api.h" -#include - -#define PRE_TAG "Model Handler Code" -#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, PRE_TAG, __VA_ARGS__) - -LiteRTModel::LiteRTModel(const char* modelPath) { - model = TfLiteModelCreateFromFile(fullPath.c_str()); - mOptions = TfLiteInterpreterOptionsCreate(); - - if(getDevice() == MLStats::Device::NNAPI) - { - TfLiteNnapiDelegateOptions options = TfLiteNnapiDelegateOptionsDefault(); - delegate = TfLiteNnapiDelegateCreate(&options); - TfLiteInterpreterOptionsAddDelegate(mOptions, delegate); - } - else if(getDevice() == MLStats::Device::GPU) - { - TfLiteGpuDelegateOptionsV2 options = TfLiteGpuDelegateOptionsV2Default(); - delegate = TfLiteGpuDelegateV2Create(&options); - TfLiteInterpreterOptionsAddDelegate(mOptions, delegate); - } -} - - -LiteRTModel::~LiteRTModel() { - TfLiteInterpreterDelete(interpreter); - TfLiteInterpreterOptionsDelete(mOptions); - if(delegate != nullptr) - { - if(getDevice() == MLStats::Device::NNAPI) - { - TfLiteNnapiDelegateDelete(delegate); - } - else - { - TfLiteGpuDelegateV2Delete(delegate); - } - } - - TfLiteModelDelete(model); -} diff --git a/src/templates/android/webview-ml-shell/app/src/main/cpp/lite_rt_model.h b/src/templates/android/webview-ml-shell/app/src/main/cpp/lite_rt_model.h deleted file mode 100644 index c9ff770..0000000 --- a/src/templates/android/webview-ml-shell/app/src/main/cpp/lite_rt_model.h +++ /dev/null @@ -1,16 +0,0 @@ -#include "tensorflow/lite/c/c_api.h" -#include - -class LiteRTModel { - - public: - LiteRTModel(std::string modelPath); - ~LiteRTModel(); - private: - // Not thrilled about using raw pointers here, however LiteRT controls - // the lifecycle here, and I don't want to use the STL and have them blow up - TfLiteModel * model; - TfLiteInterpreterOptions * mOptions; - TfLiteInterpreter* interpreter; - TfLiteDelegate* delegate = nullptr; -} \ No newline at end of file