diff --git a/mobile/examples/phi-3-vision/android/.gradle/8.0/checksums/checksums.lock b/mobile/examples/phi-3-vision/android/.gradle/8.0/checksums/checksums.lock
new file mode 100644
index 000000000..a38d9f87c
Binary files /dev/null and b/mobile/examples/phi-3-vision/android/.gradle/8.0/checksums/checksums.lock differ
diff --git a/mobile/examples/phi-3-vision/android/.gradle/8.0/checksums/md5-checksums.bin b/mobile/examples/phi-3-vision/android/.gradle/8.0/checksums/md5-checksums.bin
new file mode 100644
index 000000000..558d51a57
Binary files /dev/null and b/mobile/examples/phi-3-vision/android/.gradle/8.0/checksums/md5-checksums.bin differ
diff --git a/mobile/examples/phi-3-vision/android/.gradle/8.0/checksums/sha1-checksums.bin b/mobile/examples/phi-3-vision/android/.gradle/8.0/checksums/sha1-checksums.bin
new file mode 100644
index 000000000..bfab45525
Binary files /dev/null and b/mobile/examples/phi-3-vision/android/.gradle/8.0/checksums/sha1-checksums.bin differ
diff --git a/mobile/examples/phi-3-vision/android/.gradle/8.0/dependencies-accessors/dependencies-accessors.lock b/mobile/examples/phi-3-vision/android/.gradle/8.0/dependencies-accessors/dependencies-accessors.lock
new file mode 100644
index 000000000..a64d81212
Binary files /dev/null and b/mobile/examples/phi-3-vision/android/.gradle/8.0/dependencies-accessors/dependencies-accessors.lock differ
diff --git a/mobile/examples/phi-3-vision/android/.gradle/8.0/dependencies-accessors/gc.properties b/mobile/examples/phi-3-vision/android/.gradle/8.0/dependencies-accessors/gc.properties
new file mode 100644
index 000000000..e69de29bb
diff --git a/mobile/examples/phi-3-vision/android/.gradle/8.0/executionHistory/executionHistory.bin b/mobile/examples/phi-3-vision/android/.gradle/8.0/executionHistory/executionHistory.bin
new file mode 100644
index 000000000..febb3e0cc
Binary files /dev/null and b/mobile/examples/phi-3-vision/android/.gradle/8.0/executionHistory/executionHistory.bin differ
diff --git a/mobile/examples/phi-3-vision/android/.gradle/8.0/executionHistory/executionHistory.lock b/mobile/examples/phi-3-vision/android/.gradle/8.0/executionHistory/executionHistory.lock
new file mode 100644
index 000000000..354a7e884
Binary files /dev/null and b/mobile/examples/phi-3-vision/android/.gradle/8.0/executionHistory/executionHistory.lock differ
diff --git a/mobile/examples/phi-3-vision/android/.gradle/8.0/fileChanges/last-build.bin b/mobile/examples/phi-3-vision/android/.gradle/8.0/fileChanges/last-build.bin
new file mode 100644
index 000000000..f76dd238a
Binary files /dev/null and b/mobile/examples/phi-3-vision/android/.gradle/8.0/fileChanges/last-build.bin differ
diff --git a/mobile/examples/phi-3-vision/android/.gradle/8.0/fileHashes/fileHashes.bin b/mobile/examples/phi-3-vision/android/.gradle/8.0/fileHashes/fileHashes.bin
new file mode 100644
index 000000000..3de5c8074
Binary files /dev/null and b/mobile/examples/phi-3-vision/android/.gradle/8.0/fileHashes/fileHashes.bin differ
diff --git a/mobile/examples/phi-3-vision/android/.gradle/8.0/fileHashes/fileHashes.lock b/mobile/examples/phi-3-vision/android/.gradle/8.0/fileHashes/fileHashes.lock
new file mode 100644
index 000000000..38927b526
Binary files /dev/null and b/mobile/examples/phi-3-vision/android/.gradle/8.0/fileHashes/fileHashes.lock differ
diff --git a/mobile/examples/phi-3-vision/android/.gradle/8.0/fileHashes/resourceHashesCache.bin b/mobile/examples/phi-3-vision/android/.gradle/8.0/fileHashes/resourceHashesCache.bin
new file mode 100644
index 000000000..e4e1e9df5
Binary files /dev/null and b/mobile/examples/phi-3-vision/android/.gradle/8.0/fileHashes/resourceHashesCache.bin differ
diff --git a/mobile/examples/phi-3-vision/android/.gradle/8.0/gc.properties b/mobile/examples/phi-3-vision/android/.gradle/8.0/gc.properties
new file mode 100644
index 000000000..e69de29bb
diff --git a/mobile/examples/phi-3-vision/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/mobile/examples/phi-3-vision/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock
new file mode 100644
index 000000000..6f95a5dd1
Binary files /dev/null and b/mobile/examples/phi-3-vision/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ
diff --git a/mobile/examples/phi-3-vision/android/.gradle/buildOutputCleanup/cache.properties b/mobile/examples/phi-3-vision/android/.gradle/buildOutputCleanup/cache.properties
new file mode 100644
index 000000000..a7ad1f7f0
--- /dev/null
+++ b/mobile/examples/phi-3-vision/android/.gradle/buildOutputCleanup/cache.properties
@@ -0,0 +1,2 @@
+#Fri Jul 19 11:10:34 PDT 2024
+gradle.version=8.0
diff --git a/mobile/examples/phi-3-vision/android/.gradle/buildOutputCleanup/outputFiles.bin b/mobile/examples/phi-3-vision/android/.gradle/buildOutputCleanup/outputFiles.bin
new file mode 100644
index 000000000..eb51c52b1
Binary files /dev/null and b/mobile/examples/phi-3-vision/android/.gradle/buildOutputCleanup/outputFiles.bin differ
diff --git a/mobile/examples/phi-3-vision/android/.gradle/config.properties b/mobile/examples/phi-3-vision/android/.gradle/config.properties
new file mode 100644
index 000000000..0852258b7
--- /dev/null
+++ b/mobile/examples/phi-3-vision/android/.gradle/config.properties
@@ -0,0 +1,2 @@
+#Fri Jul 19 11:10:23 PDT 2024
+java.home=C\:\\Program Files\\Android\\Android Studio\\jbr
diff --git a/mobile/examples/phi-3-vision/android/.gradle/file-system.probe b/mobile/examples/phi-3-vision/android/.gradle/file-system.probe
new file mode 100644
index 000000000..cd68689a0
Binary files /dev/null and b/mobile/examples/phi-3-vision/android/.gradle/file-system.probe differ
diff --git a/mobile/examples/phi-3-vision/android/.gradle/vcs-1/gc.properties b/mobile/examples/phi-3-vision/android/.gradle/vcs-1/gc.properties
new file mode 100644
index 000000000..e69de29bb
diff --git a/mobile/examples/phi-3-vision/android/app/libs/onnxruntime-genai-android-0.4.1-dev-old.aar b/mobile/examples/phi-3-vision/android/app/libs/onnxruntime-genai-android-0.4.1-dev-old.aar
new file mode 100644
index 000000000..6c80a24e8
Binary files /dev/null and b/mobile/examples/phi-3-vision/android/app/libs/onnxruntime-genai-android-0.4.1-dev-old.aar differ
diff --git a/mobile/examples/phi-3-vision/android/app/libs/onnxruntime-genai-android-0.4.1-dev.aar b/mobile/examples/phi-3-vision/android/app/libs/onnxruntime-genai-android-0.4.1-dev.aar
new file mode 100644
index 000000000..f28ebfeed
Binary files /dev/null and b/mobile/examples/phi-3-vision/android/app/libs/onnxruntime-genai-android-0.4.1-dev.aar differ
diff --git a/mobile/examples/phi-3-vision/android/app/src/androidTest/java/ai/onnxruntime/genai/vision/demo/ExampleInstrumentedTest.java b/mobile/examples/phi-3-vision/android/app/src/androidTest/java/ai/onnxruntime/genai/vision/demo/ExampleInstrumentedTest.java
new file mode 100644
index 000000000..0e9868637
--- /dev/null
+++ b/mobile/examples/phi-3-vision/android/app/src/androidTest/java/ai/onnxruntime/genai/vision/demo/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package ai.onnxruntime.genai.vision.demo;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ assertEquals("ai.onnxruntime.genai.vision.demo", appContext.getPackageName());
+ }
+}
\ No newline at end of file
diff --git a/mobile/examples/phi-3-vision/android/app/src/main/java/ai/onnxruntime/genai/vision/demo/GenAIImage.java b/mobile/examples/phi-3-vision/android/app/src/main/java/ai/onnxruntime/genai/vision/demo/GenAIImage.java
new file mode 100644
index 000000000..0a6f5d7c8
--- /dev/null
+++ b/mobile/examples/phi-3-vision/android/app/src/main/java/ai/onnxruntime/genai/vision/demo/GenAIImage.java
@@ -0,0 +1,60 @@
+package ai.onnxruntime.genai.vision.demo;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import ai.onnxruntime.genai.GenAIException;
+import ai.onnxruntime.genai.Images;
+
+public class GenAIImage {
+ Images images = null;
+ Bitmap bitmap = null;
+
+ GenAIImage(Context context, Uri uri, final int maxWidth, final int maxHeight) throws IOException, GenAIException {
+ Bitmap bmp = decodeUri(context, uri, maxWidth, maxHeight);
+ String filename = context.getFilesDir() + "/multimodalinput.png";
+ FileOutputStream out = new FileOutputStream(filename);
+ bmp.compress(Bitmap.CompressFormat.PNG, 100, out); // bmp is your Bitmap instance
+ // PNG is a lossless format, the compression factor (100) is ignored
+ images = new Images(filename);
+ images = new Images(filename);
+ bitmap = BitmapFactory.decodeFile(filename);
+ }
+
+ GenAIImage(Context context, Uri uri) throws IOException, GenAIException {
+ this(context, uri, 100000, 100000);
+ }
+
+ public Images getImages() {
+ return images;
+ }
+
+ public Bitmap getBitmap() { return bitmap; }
+
+ private static Bitmap decodeUri(Context c, Uri uri, final int maxWidth, final int maxHeight)
+ throws FileNotFoundException {
+ BitmapFactory.Options o = new BitmapFactory.Options();
+ o.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(c.getContentResolver().openInputStream(uri), null, o);
+
+ int width_tmp = o.outWidth
+ , height_tmp = o.outHeight;
+ int scale = 1;
+
+ while(width_tmp / 2 > maxWidth || height_tmp / 2 > maxHeight) {
+ width_tmp /= 2;
+ height_tmp /= 2;
+ scale *= 2;
+ }
+
+ BitmapFactory.Options o2 = new BitmapFactory.Options();
+ o2.inSampleSize = scale;
+ return BitmapFactory.decodeStream(c.getContentResolver().openInputStream(uri), null, o2);
+ }
+}
diff --git a/mobile/examples/phi-3-vision/android/app/src/main/java/ai/onnxruntime/genai/vision/demo/MainActivity.java b/mobile/examples/phi-3-vision/android/app/src/main/java/ai/onnxruntime/genai/vision/demo/MainActivity.java
new file mode 100644
index 000000000..c71b35072
--- /dev/null
+++ b/mobile/examples/phi-3-vision/android/app/src/main/java/ai/onnxruntime/genai/vision/demo/MainActivity.java
@@ -0,0 +1,354 @@
+package ai.onnxruntime.genai.vision.demo;
+
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.PickVisualMediaRequest;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.util.Pair;
+import android.view.View;
+import android.view.WindowManager;
+import android.webkit.MimeTypeMap;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.function.Consumer;
+
+import ai.onnxruntime.genai.GenAIException;
+import ai.onnxruntime.genai.Generator;
+import ai.onnxruntime.genai.GeneratorParams;
+import ai.onnxruntime.genai.Images;
+import ai.onnxruntime.genai.Model;
+import ai.onnxruntime.genai.MultiModalProcessor;
+import ai.onnxruntime.genai.NamedTensors;
+import ai.onnxruntime.genai.Sequences;
+import ai.onnxruntime.genai.SimpleGenAI;
+import ai.onnxruntime.genai.Tokenizer;
+import ai.onnxruntime.genai.TokenizerStream;
+import ai.onnxruntime.genai.vision.demo.databinding.ActivityMainBinding;
+
+public class MainActivity extends AppCompatActivity implements Consumer {
+
+ private ActivityMainBinding binding;
+ private EditText userMsgEdt;
+ private Model model;
+ //private Tokenizer tokenizer;
+ private MultiModalProcessor multiModalProcessor;
+ private ImageButton sendMsgIB;
+ private ImageButton selectPhotoIB;
+ private TextView generatedTV;
+ private TextView promptTV;
+ private TextView progressText;
+ private static final String TAG = "genai.demo.MainActivity";
+
+ private final int PICK_IMAGE_FILE = 2;
+ private GenAIImage inputImage = null;
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode,
+ Intent resultData) {
+ if (requestCode == PICK_IMAGE_FILE) {
+ if (resultCode == RESULT_OK) {
+ // The result data contains a URI for the document or directory that
+ // the user selected.
+ inputImage = null;
+ if (resultData != null && resultData.getData() != null) {
+ Uri uri = resultData.getData();
+ try {
+ inputImage = new GenAIImage(this, uri);
+ if (inputImage.getBitmap() != null) {
+ runOnUiThread(() -> {
+ selectPhotoIB.setImageBitmap(inputImage.getBitmap());
+ });
+ }
+ } catch (IOException | GenAIException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+ super.onActivityResult(requestCode, resultCode, resultData);
+ }
+ private static boolean fileExists(Context context, String fileName) {
+ File file = new File(context.getFilesDir(), fileName);
+ return file.exists();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ binding = ActivityMainBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+
+ sendMsgIB = findViewById(R.id.idIBSend);
+ selectPhotoIB = findViewById(R.id.idIBPhoto);
+ userMsgEdt = findViewById(R.id.idEdtMessage);
+ generatedTV = findViewById(R.id.sample_text);
+ promptTV = findViewById(R.id.user_text);
+ progressText = findViewById(R.id.idProgressStatus);
+
+ // Trigger the download operation when the application is created
+ try {
+ downloadModels(
+ getApplicationContext());
+ } catch (GenAIException e) {
+ throw new RuntimeException(e);
+ }
+
+ Consumer tokenListener = this;
+
+ //enable scrolling and resizing of text boxes
+ generatedTV.setMovementMethod(new ScrollingMovementMethod());
+ getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
+
+ selectPhotoIB.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+// final int androidVersion = Build.VERSION.SDK_INT;
+// if (androidVersion >= Build.VERSION_CODES.TIRAMISU) {
+// ActivityResultLauncher pickMedia =
+// registerForActivityResult(new ActivityResultContracts.PickVisualMedia(), uri -> {
+// // Callback is invoked after the user selects a media item or closes the
+// // photo picker.
+// if (uri != null) {
+// Log.d("PhotoPicker", "Selected URI: " + uri);
+// } else {
+// Log.d("PhotoPicker", "No media selected");
+// }
+// });
+// pickMedia.launch(new PickVisualMediaRequest.Builder()
+// .setMediaType(ActivityResultContracts.PickVisualMedia.ImageOnly.INSTANCE)
+// .build());
+// } else {
+ Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
+ chooseFile.addCategory(Intent.CATEGORY_OPENABLE);
+ chooseFile.setType("image/*");
+ startActivityForResult(
+ Intent.createChooser(chooseFile, "Choose an image"),
+ PICK_IMAGE_FILE
+ );
+ return;
+// }
+ }});
+
+ // adding on click listener for send message button.
+ sendMsgIB.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (model == null) {
+ // if the edit text is empty display a toast message.
+ Toast.makeText(MainActivity.this, "Model not loaded yet, please wait...", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ // Checking if the message entered
+ // by user is empty or not.
+ if (userMsgEdt.getText().toString().isEmpty()) {
+ // if the edit text is empty display a toast message.
+ Toast.makeText(MainActivity.this, "Please enter your message..", Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ String promptQuestion = "<|user|>\n";
+ if (inputImage != null) {
+ promptQuestion += "<|image_1|>\n";
+ }
+ promptQuestion += userMsgEdt.getText().toString() + "You are a helpful AI assistant. Answer in two paragraphs or less<|end|>\n<|assistant|>\n";
+ final String promptQuestion_formatted = promptQuestion;
+
+ Log.i("GenAI: prompt question", promptQuestion_formatted);
+ setVisibility();
+
+ // Disable send button while responding to prompt.
+ sendMsgIB.setEnabled(false);
+
+ promptTV.setText(userMsgEdt.getText().toString());
+ // Clear Edit Text or prompt question.
+ userMsgEdt.setText("");
+ if (inputImage != null) {
+ generatedTV.setText("[analyzing image...]\n");
+ }
+ else {
+ generatedTV.setText("");
+ }
+
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ TokenizerStream stream = multiModalProcessor.createStream();
+
+ GeneratorParams generatorParams = model.createGeneratorParams();
+ //generatorParams.setSearchOption("length_penalty", 1000);
+ //generatorParams.setSearchOption("max_length", 500);
+
+ Images images = null;
+ if (inputImage != null) {
+ images = inputImage.getImages();
+ }
+
+
+ NamedTensors inputTensors = multiModalProcessor.processImages(promptQuestion_formatted, images);
+ generatorParams.setInput(inputTensors);
+
+ Generator generator = new Generator(model, generatorParams);
+
+ while (!generator.isDone()) {
+ generator.computeLogits();
+ generator.generateNextToken();
+
+ int token = generator.getLastTokenInSequence(0);
+
+ tokenListener.accept(stream.decode(token));
+ }
+
+ generator.close();
+ }
+ catch (GenAIException e) {
+ throw new RuntimeException(e);
+ }
+
+ runOnUiThread(() -> {
+ sendMsgIB.setEnabled(true);
+ });
+ }
+ }).start();
+ }
+ });
+ }
+
+ @Override
+ protected void onDestroy() {
+ multiModalProcessor.close();
+ multiModalProcessor = null;
+ model.close();
+ model = null;
+ super.onDestroy();
+ }
+
+
+ private void downloadModels(Context context) throws GenAIException {
+
+ final String baseUrl = "https://huggingface.co/microsoft/Phi-3-vision-128k-instruct-onnx-cpu/resolve/main/cpu-int4-rtn-block-32-acc-level-4/";
+ List files = Arrays.asList(
+ "genai_config.json",
+ "phi-3-v-128k-instruct-text-embedding.onnx",
+ "phi-3-v-128k-instruct-text-embedding.onnx.data",
+ "phi-3-v-128k-instruct-text.onnx",
+ "phi-3-v-128k-instruct-text.onnx.data",
+ "phi-3-v-128k-instruct-vision.onnx",
+ "phi-3-v-128k-instruct-vision.onnx.data",
+ "processor_config.json",
+ "special_tokens_map.json",
+ "tokenizer.json",
+ "tokenizer_config.json");
+
+
+ List> urlFilePairs = new ArrayList<>();
+ for (String file : files) {
+ if (/*file.endsWith(".data") ||*/ !fileExists(context, file)) {
+ urlFilePairs.add(new Pair<>(
+ baseUrl + file,// + "?download=true",
+ file));
+ }
+ }
+ if (urlFilePairs.isEmpty()) {
+ // Display a message using Toast
+ Toast.makeText(this, "All files already exist. Skipping download.", Toast.LENGTH_SHORT).show();
+ Log.d(TAG, "All files already exist. Skipping download.");
+ model = new Model(getFilesDir().getPath());
+ multiModalProcessor = new MultiModalProcessor(model);
+ return;
+ }
+
+ progressText.setText("Downloading...");
+ progressText.setVisibility(View.VISIBLE);
+
+ Toast.makeText(this,
+ "Downloading model for the app... Model Size greater than 2GB, please allow a few minutes to download.",
+ Toast.LENGTH_SHORT).show();
+
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ executor.execute(() -> {
+ ModelDownloader.downloadModel(context, urlFilePairs, new ModelDownloader.DownloadCallback() {
+ @Override
+ public void onProgress(long lastBytesRead, long bytesRead, long bytesTotal) {
+ long lastPctDone = 100 * lastBytesRead / bytesTotal;
+ long pctDone = 100 * bytesRead / bytesTotal;
+ if (pctDone > lastPctDone) {
+ Log.d(TAG, "Downloading files: " + pctDone + "%");
+ //if (pctDone / 10 > lastPctDone / 10) {
+ runOnUiThread(() -> {
+ progressText.setText("Downloading: " + pctDone + "%");
+ //Toast.makeText(context, "Downloading: " + pctDone + "%", Toast.LENGTH_SHORT).show();
+ });
+ //}
+ }
+ }
+ @Override
+ public void onDownloadComplete() {
+ Log.d(TAG, "All downloads completed.");
+
+ // Last download completed, create SimpleGenAI
+ try {
+ model = new Model(getFilesDir().getPath());
+ multiModalProcessor = new MultiModalProcessor(model);
+ runOnUiThread(() -> {
+ Toast.makeText(context, "All downloads completed", Toast.LENGTH_SHORT).show();
+ progressText.setVisibility(View.INVISIBLE);
+ });
+ } catch (GenAIException e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
+
+ }
+ });
+ });
+ executor.shutdown();
+ }
+
+ @Override
+ public void accept(String token) {
+ runOnUiThread(() -> {
+ // Update and aggregate the generated text and write to text box.
+ CharSequence generated = generatedTV.getText();
+ generatedTV.setText(generated + token);
+ generatedTV.invalidate();
+ final int scrollAmount = generatedTV.getLayout().getLineTop(generatedTV.getLineCount()) - generatedTV.getHeight();
+ generatedTV.scrollTo(0, Math.max(scrollAmount, 0));
+ });
+ }
+
+ public void setVisibility() {
+ TextView view = (TextView) findViewById(R.id.user_text);
+ view.setVisibility(View.VISIBLE);
+ TextView botView = (TextView) findViewById(R.id.sample_text);
+ botView.setVisibility(View.VISIBLE);
+ }
+}
diff --git a/mobile/examples/phi-3-vision/android/app/src/main/java/ai/onnxruntime/genai/vision/demo/ModelDownloader.java b/mobile/examples/phi-3-vision/android/app/src/main/java/ai/onnxruntime/genai/vision/demo/ModelDownloader.java
new file mode 100644
index 000000000..3d544a69d
--- /dev/null
+++ b/mobile/examples/phi-3-vision/android/app/src/main/java/ai/onnxruntime/genai/vision/demo/ModelDownloader.java
@@ -0,0 +1,104 @@
+package ai.onnxruntime.genai.vision.demo;
+
+import static androidx.constraintlayout.helper.widget.MotionEffect.TAG;
+
+import android.content.Context;
+import android.util.Log;
+import android.util.Pair;
+import android.widget.Toast;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import ai.onnxruntime.genai.GenAIException;
+
+public class ModelDownloader {
+ interface DownloadCallback {
+ void onProgress(long lastBytesRead, long bytesRead, long bytesTotal);
+ void onDownloadComplete() throws GenAIException;
+ }
+
+ public static void downloadModel(Context context, List> urlFilePairs, DownloadCallback callback) {
+ try {
+
+ List connections = new ArrayList<>();
+ long totalDownloadBytes = 0;
+ for (int i = 0; i < urlFilePairs.size(); i++) {
+ String url = urlFilePairs.get(i).first;
+ URL modelUrl = new URL(url);
+ HttpURLConnection connection = (HttpURLConnection) modelUrl.openConnection();
+ connections.add(connection);
+ long totalFileSize = connection.getHeaderFieldLong("Content-Length",-1);
+ totalDownloadBytes += totalFileSize;
+ }
+
+ long totalBytesRead = 0;
+ for (int i = 0; i < urlFilePairs.size(); i++) {
+ String fileName = urlFilePairs.get(i).second;
+ HttpURLConnection connection = connections.get(i);
+
+ File file = new File(context.getFilesDir(), fileName);
+ File tempFile = new File(context.getFilesDir(), fileName + ".tmp");
+ Log.d(TAG, "Downloading file: " + fileName);
+ connection.connect();
+
+ // Check if response code is OK
+ if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
+ InputStream inputStream = connection.getInputStream();
+ FileOutputStream outputStream = new FileOutputStream(tempFile);
+
+ long begin = System.currentTimeMillis();
+
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, bytesRead);
+ if (callback != null) {
+ callback.onProgress(totalBytesRead, totalBytesRead + bytesRead, totalDownloadBytes);
+ }
+ totalBytesRead += bytesRead;
+ }
+
+ outputStream.flush();
+ outputStream.close();
+ inputStream.close();
+ connection.disconnect();
+
+ long duration = System.currentTimeMillis() - begin;
+
+ // File downloaded successfully
+ if (tempFile.renameTo(file)) {
+ if (duration > 0) {
+ Log.d(TAG, "File downloaded successfully: " + fileName + "(" + totalBytesRead + " bytes, " + (totalBytesRead / duration) + "KBps)");
+ } else {
+ Log.d(TAG, "File downloaded successfully: " + fileName + "(" + totalBytesRead + " bytes, " + (duration / 1000.0) + "s)");
+ }
+ } else {
+ Log.e(TAG, "Failed to rename temp file to original file");
+ }
+ } else {
+ Log.e(TAG, "Failed to download model. HTTP response code: " + connection.getResponseCode());
+ }
+ }
+ if (callback != null) {
+ callback.onDownloadComplete();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ Log.e(TAG, "Exception occurred during model download: " + e.getMessage());
+ } catch (GenAIException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/mobile/examples/phi-3-vision/android/app/src/test/java/ai/onnxruntime/genai/vision/demo/README.md b/mobile/examples/phi-3-vision/android/app/src/test/java/ai/onnxruntime/genai/vision/demo/README.md
new file mode 100644
index 000000000..2d75f43a3
--- /dev/null
+++ b/mobile/examples/phi-3-vision/android/app/src/test/java/ai/onnxruntime/genai/vision/demo/README.md
@@ -0,0 +1,5 @@
+**Note**:
+
+Note that we are not implementing any unit tests here as there's no simple unit tests that can be done without having a model locally.
+
+Debugging/Testing should be done via running the app on the simulator or actual android device.
\ No newline at end of file
diff --git a/mobile/examples/phi-3-vision/android/local.properties b/mobile/examples/phi-3-vision/android/local.properties
new file mode 100644
index 000000000..50b82dc35
--- /dev/null
+++ b/mobile/examples/phi-3-vision/android/local.properties
@@ -0,0 +1,8 @@
+## This file must *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+#
+# Location of the SDK. This is only used by Gradle.
+# For customization when using a Version Control System, please read the
+# header note.
+#Fri Jul 26 10:25:51 PDT 2024
+sdk.dir=C\:\\Users\\t-boskovicf\\AppData\\Local\\Android\\Sdk
diff --git a/mobile/examples/phi-3/android/README.md b/mobile/examples/phi-3/android/README.md
index 129a6d961..e6145bb81 100644
--- a/mobile/examples/phi-3/android/README.md
+++ b/mobile/examples/phi-3/android/README.md
@@ -27,7 +27,7 @@ Clone this repository to get the sample application.
The current set up supports downloading Phi-3-mini model directly from Huggingface repo to the android device folder. However, it takes time since the model data is >2.5G.
-You can also follow this link to download **Phi-3-mini**: https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx/tree/main/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4
+You can also download [**Phi-3-mini**](https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx/tree/main/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4)
and manually copy to the android device file directory following the below instructions:
#### Steps for manual copying models to android device directory:
@@ -40,7 +40,7 @@ From Android Studio:
- Open Device Explorer in Android Studio
- Navigate to `/data/data/ai.onnxruntime.genai.demo/files`
- adjust as needed if the value returned by getFilesDir() differs for your emulator or device
- - copy the whole [phi-3](https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx/tree/main/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4) model folder to the `files` directory
+ - copy the whole [Phi-3](https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx/tree/main/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4) model folder to the `files` directory
### Step 3: Connect Android Device and Run the app
Connect your Android Device to your computer or select the Android Emulator in Android Studio Device manager.
diff --git a/mobile/examples/phi-3/android/app/build.gradle.kts b/mobile/examples/phi-3/android/app/build.gradle.kts
index e8d3478a5..10e53b580 100644
--- a/mobile/examples/phi-3/android/app/build.gradle.kts
+++ b/mobile/examples/phi-3/android/app/build.gradle.kts
@@ -17,7 +17,7 @@ android {
ndk {
//noinspection ChromeOsAbiSupport
- abiFilters += listOf("arm64-v8a")
+ abiFilters += listOf("arm64-v8a", "x86_64")
}
}
diff --git a/mobile/examples/phi-3/android/app/src/main/java/ai/onnxruntime/genai/demo/MainActivity.java b/mobile/examples/phi-3/android/app/src/main/java/ai/onnxruntime/genai/demo/MainActivity.java
index 49bb93e7f..c43722149 100644
--- a/mobile/examples/phi-3/android/app/src/main/java/ai/onnxruntime/genai/demo/MainActivity.java
+++ b/mobile/examples/phi-3/android/app/src/main/java/ai/onnxruntime/genai/demo/MainActivity.java
@@ -40,6 +40,7 @@ public class MainActivity extends AppCompatActivity implements Consumer
private ImageButton sendMsgIB;
private TextView generatedTV;
private TextView promptTV;
+ private TextView progressText;
private static final String TAG = "genai.demo.MainActivity";
private static boolean fileExists(Context context, String fileName) {
@@ -92,7 +93,7 @@ public void onClick(View v) {
}
String promptQuestion = userMsgEdt.getText().toString();
- String promptQuestion_formatted = "You are a helpful AI assistant. Answer in one paragraph or less<|end|><|user|>"+promptQuestion+"<|end|>\n";
+ String promptQuestion_formatted = "You are a helpful AI assistant. Answer in two paragraphs or less<|end|><|user|>"+promptQuestion+"<|end|>\n";
Log.i("GenAI: prompt question", promptQuestion_formatted);
setVisibility();
@@ -107,12 +108,15 @@ public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
- try {
+ try (Outstream stream = new stream;
+ Outsteam generatorParams;
+ Outstream generator;
+ ) {
TokenizerStream stream = tokenizer.createStream();
GeneratorParams generatorParams = model.createGeneratorParams();
//generatorParams.setSearchOption("length_penalty", 1000);
- generatorParams.setSearchOption("max_length", 500);
+ //generatorParams.setSearchOption("max_length", 500);
Sequences encodedPrompt = tokenizer.encode(promptQuestion_formatted);
generatorParams.setInput(encodedPrompt);
@@ -128,7 +132,9 @@ public void run() {
tokenListener.accept(stream.decode(token));
}
- //generator.close();
+ generator.close();
+ generatorParams.close();
+
}
catch (GenAIException e) {
throw new RuntimeException(e);
@@ -153,89 +159,81 @@ protected void onDestroy() {
}
private void downloadModels(Context context) throws GenAIException {
- List> urlFilePairs = Arrays.asList(
- new Pair<>(
- "https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx/resolve/main/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/added_tokens.json?download=true",
- "added_tokens.json"),
- new Pair<>(
- "https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx/resolve/main/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/config.json?download=true",
- "config.json"),
- new Pair<>(
- "https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx/resolve/main/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/configuration_phi3.py?download=true",
- "configuration_phi3.py"),
- new Pair<>(
- "https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx/resolve/main/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/genai_config.json?download=true",
- "genai_config.json"),
- new Pair<>(
- "https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx/resolve/main/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/phi3-mini-4k-instruct-cpu-int4-rtn-block-32-acc-level-4.onnx?download=true",
- "phi3-mini-4k-instruct-cpu-int4-rtn-block-32-acc-level-4.onnx"),
- new Pair<>(
- "https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx/resolve/main/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/phi3-mini-4k-instruct-cpu-int4-rtn-block-32-acc-level-4.onnx.data?download=true",
- "phi3-mini-4k-instruct-cpu-int4-rtn-block-32-acc-level-4.onnx.data"),
- new Pair<>(
- "https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx/resolve/main/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/special_tokens_map.json?download=true",
- "special_tokens_map.json"),
- new Pair<>(
- "https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx/resolve/main/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/tokenizer.json?download=true",
- "tokenizer.json"),
- new Pair<>(
- "https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx/resolve/main/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/tokenizer.model?download=true",
- "tokenizer.model"),
- new Pair<>(
- "https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx/resolve/main/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/tokenizer_config.json?download=true",
- "tokenizer_config.json"));
+
+ final String baseUrl = "https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx/resolve/main/cpu_and_mobile/cpu-int4-rtn-block-32-acc-level-4/";
+ List files = Arrays.asList(
+ "added_tokens.json",
+ "config.json",
+ "configuration_phi3.py",
+ "genai_config.json",
+ "phi3-mini-4k-instruct-cpu-int4-rtn-block-32-acc-level-4.onnx",
+ "phi3-mini-4k-instruct-cpu-int4-rtn-block-32-acc-level-4.onnx.data",
+ "special_tokens_map.json",
+ "tokenizer.json",
+ "tokenizer.model",
+ "tokenizer_config.json");
+
+ List> urlFilePairs = new ArrayList<>();
+ for (String file : files) {
+ if (/*file.endsWith(".data") ||*/ !fileExists(context, file)) {
+ urlFilePairs.add(new Pair<>(
+ baseUrl + file,// + "?download=true",
+ file));
+ }
+ }
+ if (urlFilePairs.isEmpty()) {
+ // Display a message using Toast
+ Toast.makeText(this, "All files already exist. Skipping download.", Toast.LENGTH_SHORT).show();
+ Log.d(TAG, "All files already exist. Skipping download.");
+ model = new Model(getFilesDir().getPath());
+ tokenizer = model.createTokenizer();
+ return;
+ }
+
+ progressText.setText("Downloading...");
+ progressText.setVisibility(View.VISIBLE);
+
Toast.makeText(this,
"Downloading model for the app... Model Size greater than 2GB, please allow a few minutes to download.",
Toast.LENGTH_SHORT).show();
ExecutorService executor = Executors.newSingleThreadExecutor();
- for (int i = 0; i < urlFilePairs.size(); i++) {
- final int index = i;
- String url = urlFilePairs.get(index).first;
- String fileName = urlFilePairs.get(index).second;
- if (fileExists(context, fileName)) {
- // Display a message using Toast
- Toast.makeText(this, "File already exists. Skipping Download.", Toast.LENGTH_SHORT).show();
-
- Log.d(TAG, "File " + fileName + " already exists. Skipping download.");
- // note: since we always download the files lists together for once,
- // so assuming if one filename exists, then the download model step has already
- // be
- // done.
- if (index == urlFilePairs.size() - 1) {
- model = new Model(getFilesDir().getPath());
- tokenizer = model.createTokenizer();
- break;
- }
- continue;
- }
- executor.execute(() -> {
- ModelDownloader.downloadModel(context, url, fileName, new ModelDownloader.DownloadCallback() {
- private long pctDone = 0;
- @Override
- public void onDownloadProgress(long bytesDone, long bytesTotal) {
- if (bytesTotal > 0) {
- long newPctDone = bytesDone * 100 / bytesTotal;
- if (newPctDone > pctDone) {
- pctDone = newPctDone;
- Log.d(TAG, "Download" + fileName + ": " + pctDone
- + "% of " + (bytesTotal/1024) + " KB");
- }
- }
+ executor.execute(() -> {
+ ModelDownloader.downloadModel(context, urlFilePairs, new ModelDownloader.DownloadCallback() {
+ @Override
+ public void onProgress(long lastBytesRead, long bytesRead, long bytesTotal) {
+ long lastPctDone = 100 * lastBytesRead / bytesTotal;
+ long pctDone = 100 * bytesRead / bytesTotal;
+ if (pctDone > lastPctDone) {
+ Log.d(TAG, "Downloading files: " + pctDone + "%");
+ //if (pctDone / 10 > lastPctDone / 10) {
+ runOnUiThread(() -> {
+ progressText.setText("Downloading: " + pctDone + "%");
+ //Toast.makeText(context, "Downloading: " + pctDone + "%", Toast.LENGTH_SHORT).show();
+ });
+ //}
}
- @Override
- public void onDownloadComplete() throws GenAIException {
- Log.d(TAG, "Download complete for " + fileName);
- if (index == urlFilePairs.size() - 1) {
- // Last download completed, create GenAIWrapper
- model = new Model(getFilesDir().getPath());
- tokenizer = model.createTokenizer();
- Log.d(TAG, "All downloads completed");
- }
+ }
+ @Override
+ public void onDownloadComplete() {
+ Log.d(TAG, "All downloads completed.");
+
+ // Last download completed, create SimpleGenAI
+ try {
+ model = new Model(getFilesDir().getPath());
+ tokenizer = model.createTokenizer();
+ runOnUiThread(() -> {
+ Toast.makeText(context, "All downloads completed", Toast.LENGTH_SHORT).show();
+ progressText.setVisibility(View.INVISIBLE);
+ });
+ } catch (GenAIException e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
}
- });
+
+ }
});
- }
+ });
executor.shutdown();
}
diff --git a/mobile/examples/phi-3/android/app/src/main/java/ai/onnxruntime/genai/demo/ModelDownloader.java b/mobile/examples/phi-3/android/app/src/main/java/ai/onnxruntime/genai/demo/ModelDownloader.java
index c41f7eb57..120560eb6 100644
--- a/mobile/examples/phi-3/android/app/src/main/java/ai/onnxruntime/genai/demo/ModelDownloader.java
+++ b/mobile/examples/phi-3/android/app/src/main/java/ai/onnxruntime/genai/demo/ModelDownloader.java
@@ -4,6 +4,9 @@
import android.content.Context;
import android.util.Log;
+import android.util.Pair;
+import android.widget.Toast;
+
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
@@ -14,61 +17,88 @@
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
import ai.onnxruntime.genai.GenAIException;
public class ModelDownloader {
interface DownloadCallback {
- void onDownloadProgress(long doneBytes, long totalBytes);
+ void onProgress(long lastBytesRead, long bytesRead, long bytesTotal);
void onDownloadComplete() throws GenAIException;
}
- public static void downloadModel(Context context, String url, String fileName, DownloadCallback callback) {
+ public static void downloadModel(Context context, List> urlFilePairs, DownloadCallback callback) {
try {
- File file = new File(context.getFilesDir(), fileName);
- File tempFile = new File(context.getFilesDir(), fileName + ".tmp");
- URL modelUrl = new URL(url);
- HttpURLConnection connection = (HttpURLConnection) modelUrl.openConnection();
- connection.connect();
-
- // Check if response code is OK
- if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
- InputStream inputStream = connection.getInputStream();
- FileOutputStream outputStream = new FileOutputStream(tempFile);
- long totalFileSize = connection.getHeaderFieldLong("Content-Length", -1);
-
- byte[] buffer = new byte[4096];
- long totalBytesRead = 0;
- int bytesRead;
- while ((bytesRead = inputStream.read(buffer)) != -1) {
- outputStream.write(buffer, 0, bytesRead);
- if (callback != null) {
+
+ List connections = new ArrayList<>();
+ long totalDownloadBytes = 0;
+ for (int i = 0; i < urlFilePairs.size(); i++) {
+ String url = urlFilePairs.get(i).first;
+ URL modelUrl = new URL(url);
+ HttpURLConnection connection = (HttpURLConnection) modelUrl.openConnection();
+ connections.add(connection);
+ long totalFileSize = connection.getHeaderFieldLong("Content-Length",-1);
+ totalDownloadBytes += totalFileSize;
+ }
+
+ long totalBytesRead = 0;
+ for (int i = 0; i < urlFilePairs.size(); i++) {
+ String fileName = urlFilePairs.get(i).second;
+ HttpURLConnection connection = connections.get(i);
+
+ File file = new File(context.getFilesDir(), fileName);
+ File tempFile = new File(context.getFilesDir(), fileName + ".tmp");
+ Log.d(TAG, "Downloading file: " + fileName);
+ connection.connect();
+
+ // Check if response code is OK
+ if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
+ InputStream inputStream = connection.getInputStream();
+ FileOutputStream outputStream = new FileOutputStream(tempFile);
+
+ long begin = System.currentTimeMillis();
+
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, bytesRead);
+ if (callback != null) {
+ callback.onProgress(totalBytesRead, totalBytesRead + bytesRead, totalDownloadBytes);
+ }
totalBytesRead += bytesRead;
- callback.onDownloadProgress(totalBytesRead, totalFileSize);
}
- }
- outputStream.flush();
- outputStream.close();
- inputStream.close();
+ outputStream.flush();
+ outputStream.close();
+ inputStream.close();
+ connection.disconnect();
+
+ long duration = System.currentTimeMillis() - begin;
- // File downloaded successfully
- if (tempFile.renameTo(file)) {
- Log.d(TAG, "File downloaded successfully");
- if (callback != null) {
- callback.onDownloadComplete();
+ // File downloaded successfully
+ if (tempFile.renameTo(file)) {
+ if (duration > 0) {
+ Log.d(TAG, "File downloaded successfully: " + fileName + "(" + totalBytesRead + " bytes, " + (totalBytesRead / duration) + "KBps)");
+ } else {
+ Log.d(TAG, "File downloaded successfully: " + fileName + "(" + totalBytesRead + " bytes, " + (duration / 1000.0) + "s)");
+ }
+ } else {
+ Log.e(TAG, "Failed to rename temp file to original file");
}
} else {
- Log.e(TAG, "Failed to rename temp file to original file");
+ Log.e(TAG, "Failed to download model. HTTP response code: " + connection.getResponseCode());
}
- } else {
- Log.e(TAG, "Failed to download model. HTTP response code: " + connection.getResponseCode());
+ }
+ if (callback != null) {
+ callback.onDownloadComplete();
}
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "Exception occurred during model download: " + e.getMessage());
} catch (GenAIException e) {
- throw new RuntimeException(e);
+ throw new RuntimeException(e);
}
}
}
\ No newline at end of file
diff --git a/mobile/examples/phi-3/android/app/src/main/jniLibs/arm64-v8a/libonnxruntime-genai.so b/mobile/examples/phi-3/android/app/src/main/jniLibs/arm64-v8a/libonnxruntime-genai.so
new file mode 100644
index 000000000..f5e6e6e05
Binary files /dev/null and b/mobile/examples/phi-3/android/app/src/main/jniLibs/arm64-v8a/libonnxruntime-genai.so differ
diff --git a/mobile/examples/phi-3/android/app/src/main/jniLibs/arm64-v8a/libonnxruntime.so b/mobile/examples/phi-3/android/app/src/main/jniLibs/arm64-v8a/libonnxruntime.so
new file mode 100644
index 000000000..c14976892
Binary files /dev/null and b/mobile/examples/phi-3/android/app/src/main/jniLibs/arm64-v8a/libonnxruntime.so differ
diff --git a/mobile/examples/phi-3/android/app/src/main/jniLibs/arm64-v8a/libonnxruntime4j_jni.so b/mobile/examples/phi-3/android/app/src/main/jniLibs/arm64-v8a/libonnxruntime4j_jni.so
new file mode 100644
index 000000000..f75540a19
Binary files /dev/null and b/mobile/examples/phi-3/android/app/src/main/jniLibs/arm64-v8a/libonnxruntime4j_jni.so differ
diff --git a/mobile/examples/phi-3/android/app/src/main/jniLibs/x86_64/libonnxruntime-genai.so b/mobile/examples/phi-3/android/app/src/main/jniLibs/x86_64/libonnxruntime-genai.so
new file mode 100644
index 000000000..f986355ab
Binary files /dev/null and b/mobile/examples/phi-3/android/app/src/main/jniLibs/x86_64/libonnxruntime-genai.so differ
diff --git a/mobile/examples/phi-3/android/app/src/main/jniLibs/x86_64/libonnxruntime.so b/mobile/examples/phi-3/android/app/src/main/jniLibs/x86_64/libonnxruntime.so
new file mode 100644
index 000000000..c14976892
Binary files /dev/null and b/mobile/examples/phi-3/android/app/src/main/jniLibs/x86_64/libonnxruntime.so differ
diff --git a/mobile/examples/phi-3/android/app/src/main/jniLibs/x86_64/libonnxruntime4j_jni.so b/mobile/examples/phi-3/android/app/src/main/jniLibs/x86_64/libonnxruntime4j_jni.so
new file mode 100644
index 000000000..f75540a19
Binary files /dev/null and b/mobile/examples/phi-3/android/app/src/main/jniLibs/x86_64/libonnxruntime4j_jni.so differ
diff --git a/mobile/examples/phi-3/android/app/src/main/res/drawable/rounded_corner2.xml b/mobile/examples/phi-3/android/app/src/main/res/drawable/rounded_corner2.xml
index de9870ee9..913738d1d 100644
--- a/mobile/examples/phi-3/android/app/src/main/res/drawable/rounded_corner2.xml
+++ b/mobile/examples/phi-3/android/app/src/main/res/drawable/rounded_corner2.xml
@@ -1,7 +1,7 @@
-
+
@color/blue_700
- @color/black
- - @color/teal_200
- - @color/teal_200
+ - @color/purple_200
+ - @color/purple_200
- @color/black
- ?attr/colorPrimaryVariant
diff --git a/mobile/examples/phi-3/android/app/src/main/res/values/colors.xml b/mobile/examples/phi-3/android/app/src/main/res/values/colors.xml
index 8a660986c..31b1630c0 100644
--- a/mobile/examples/phi-3/android/app/src/main/res/values/colors.xml
+++ b/mobile/examples/phi-3/android/app/src/main/res/values/colors.xml
@@ -3,8 +3,8 @@
#CC03A9F4
#CC03A9F4
#CC03A9F4
- #FF03DAC5
- #FF018786
+ #a26cf3
+ #7626ef
#FF000000
#FFFFFFFF
\ No newline at end of file
diff --git a/mobile/examples/phi-3/android/app/src/main/res/values/themes.xml b/mobile/examples/phi-3/android/app/src/main/res/values/themes.xml
index 53ebc75d6..5e976ac5e 100644
--- a/mobile/examples/phi-3/android/app/src/main/res/values/themes.xml
+++ b/mobile/examples/phi-3/android/app/src/main/res/values/themes.xml
@@ -6,8 +6,8 @@
- @color/blue_700
- @color/white
- - @color/teal_200
- - @color/teal_700
+ - @color/purple_200
+ - @color/purple_700
- @color/black
- ?attr/colorPrimaryVariant