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..97233b1 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(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/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 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 237390f..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 @@ -26,7 +26,8 @@ project("daypack") # 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/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..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,11 +1,88 @@ #include #include -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 */, + 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, + 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 */) { - std::string hello = "Hello from C++"; - return env->NewStringUTF(hello.c_str()); + jobject /* this */, + jstring modelId, + jobjectArray inputNames, + jobjectArray inputShapes, + jobjectArray inputBuffers, + jint numInputs, + jobjectArray outputNames, + jobjectArray outputShapes, + 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 6091053..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 @@ -7,26 +7,46 @@ import android.webkit.JavascriptInterface 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) + startDownloadThread() 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)) - + } + + 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, + numOutputs: Int + ) companion object { // Used to load the 'daypack' library on application startup. @@ -37,15 +57,16 @@ class MainActivity : AppCompatActivity() { // Add this class class WebAppInterface(private val context: MainActivity) { - @JavascriptInterface - fun stringFromJNI(): String { - return context.stringFromJNI() - } @JavascriptInterface fun getInstalledModels(callback: String) { - // TODO: Implement native model listing - val models = arrayOf() + val jsonModels = context.modelManager.getInstalledModels() + context.binding.webview.evaluateJavascript("$callback($jsonModels)", null) + } + + @JavascriptInterface + fun getLoadedModels(callback: String) { + 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 new file mode 100644 index 0000000..369c2b4 --- /dev/null +++ b/src/templates/android/webview-ml-shell/app/src/main/java/ai/baseweight/daypack/ModelManager.kt @@ -0,0 +1,86 @@ +package ai.baseweight.daypack + +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 523e331..1dd268a 100644 --- a/src/templates/android/webview-ml-shell/gradle/libs.versions.toml +++ b/src/templates/android/webview-ml-shell/gradle/libs.versions.toml @@ -9,8 +9,12 @@ appcompat = "1.7.0" 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" } @@ -19,6 +23,8 @@ 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" } +firebase-crashlytics-buildtools = { group = "com.google.firebase", name = "firebase-crashlytics-buildtools", version.ref = "firebaseCrashlyticsBuildtools" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" }