Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/templates/android/webview-ml-shell/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Generated HTML from Gradio Applications goes here
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DayPack Local Javscript goes here
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Model Files Go Here
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[

]
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -1,11 +1,88 @@
#include <jni.h>
#include <string>

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<std::string> input_names;
std::vector<std::string> output_names;
std::vector<jfloatArray> input_buffers;
std::vector<jfloatArray> 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);
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
inputShapes: Array<IntArray>,
inputBuffers: Array<ByteArray>,
numInputs: Int,
outputNames: Array<String>,
outputShapes: Array<IntArray>,
outputBuffers: Array<ByteArray>,
numOutputs: Int
)

companion object {
// Used to load the 'daypack' library on application startup.
Expand All @@ -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<String>()
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)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, ModelMetadata>()

init {
try {
val inputStream = context.assets.open("models/models.json")
val reader = InputStreamReader(inputStream)
val modelMetadataList = Gson().fromJson(reader, Array<ModelMetadata>::class.java)
reader.close()
inputStream.close()
modelMetadataList.forEach {
models[it.name] = it
}
}
catch (e: Exception) {
e.printStackTrace()
}
}

fun getInstalledModels(): String? {
val metadataList = mutableListOf<ModelMetadata>()
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()
}
}
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ai.baseweight.daypack

class ModelMetadata(val name: String, val path: String,
val type: String, val inputs: List<String>,
val outputs: List<String>)
{
var isLoaded: Boolean = false
var isInstalled: Boolean = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand All @@ -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" }
Expand Down