Releae Version 0.7.1

+ Click here to [download](https://meta.alicdn.com/data/mnn/mnn_chat_0_7_1.apk)
+ add new models:
  + [MiniCPM-V-4](https://huggingface.co/openbmb/MiniCPM-V-4): GPT-4V Level MLLM for Single Image, Multi Image and Video on Your Phone
  + [WebSailor-3B](https://huggingface.co/Alibaba-NLP/WebSailor-3B): a complete post-training methodology designed to teach LLM agents sophisticated reasoning for complex web navigation and information-seeking tasks.
  + [Lingshu-7B](https://huggingface.co/lingshu-medical-mllm/Lingshu-7B):Multimodal Large Language Models for Medical Domain
+ bugfix:
 + Crash when choose images.
This commit is contained in:
若遗 2025-08-21 16:20:05 +08:00
parent a73434ba29
commit ef2e617b90
14 changed files with 458 additions and 43 deletions

1
.gitignore vendored
View File

@ -376,3 +376,4 @@ datasets/*
# qnn 3rdParty
source/backend/qnn/3rdParty/include
apps/Android/MnnLlmChat/release_outputs

View File

@ -61,6 +61,15 @@ This is our full multimodal language model (LLM) Android app
```
# Releases
## Version 0.7.1
+ Click here to [download](https://meta.alicdn.com/data/mnn/mnn_chat_0_7_1.apk)
+ add new models:
+ [MiniCPM-V-4](https://huggingface.co/openbmb/MiniCPM-V-4): GPT-4V Level MLLM for Single Image, Multi Image and Video on Your Phone
+ [WebSailor-3B](https://huggingface.co/Alibaba-NLP/WebSailor-3B): a complete post-training methodology designed to teach LLM agents sophisticated reasoning for complex web navigation and information-seeking tasks.
+ [Lingshu-7B](https://huggingface.co/lingshu-medical-mllm/Lingshu-7B):Multimodal Large Language Models for Medical Domain
+ bugfix:
+ Crash when choose images.
## Version 0.7.0
+ Click here to [download](https://meta.alicdn.com/data/mnn/mnn_chat_0_7_0.apk)
+ add new models: gpt-oss-20b

View File

@ -54,6 +54,15 @@
# Releases
## 版本 0.7.1
+ [点击此处下载](https://meta.alicdn.com/data/mnn/mnn_chat_0_7_1.apk)
+ 新增模型:
+ [MiniCPM-V-4](https://huggingface.co/openbmb/MiniCPM-V-4):可在手机上运行的、达到 GPT-4V 水准的多模态大语言模型,支持单图、多图和视频理解
+ [WebSailor-3B](https://huggingface.co/Alibaba-NLP/WebSailor-3B):一种完整的后训练方法论,旨在教会大语言模型代理在复杂网页导航和信息检索任务中进行高级推理
+ [Lingshu-7B](https://huggingface.co/lingshu-medical-mllm/Lingshu-7B):面向医疗领域的多模态大语言模型
+ 问题修复:
+ 选择图片时崩溃的问题
## Version 0.7.0
+ Click here to [download](https://meta.alicdn.com/data/mnn/mnn_chat_0_7_0.apk)
+ 增加新模型: gpt-oss-20b

View File

@ -59,8 +59,8 @@ android {
applicationId "com.alibaba.mnnllm.android"
minSdk 26
targetSdk 35
versionCode 700
versionName "0.7.0"
versionCode 701
versionName "0.7.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {

View File

@ -8,6 +8,7 @@
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

View File

@ -1,5 +1,5 @@
{
"version": "4",
"version": "5",
"tagTranslations": {
"Vision": "图像理解",
"Video": "视频理解",
@ -92,23 +92,6 @@
"vendor": "MiniCPM",
"file_size": 2834668967
},
{
"modelName": "gemma-3-270m-it-MNN",
"tags": [
],
"categories": [
"recommended",
"qwen"
],
"sources": {
"HuggingFace": "taobao-mnn/gemma-3-270m-it-MNN",
"ModelScope": "MNN/gemma-3-270m-it-MNN",
"Modelers": "MNN/gemma-3-270m-it-MNN"
},
"size_gb": 0.27,
"vendor": "Gemma",
"file_size": 308727585
},
{
"modelName": "MiniCPM4-0.5B-MNN",
"tags": [
@ -348,8 +331,7 @@
],
"sources": {
"HuggingFace": "taobao-mnn/ERNIE-4.5-0.3B-PT-MNN",
"ModelScope": "MNN/ERNIE-4.5-0.3B-PT-MNN",
"Modelers": "MNN/ERNIE-4.5-0.3B-PT-MNN"
"ModelScope": "MNN/ERNIE-4.5-0.3B-PT-MNN"
},
"size_gb": 0.3,
"vendor": "ERNIE",

View File

@ -423,14 +423,7 @@ class ChatActivity : AppCompatActivity() {
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
this.chatInputModule!!.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
private fun handleNewSession() {
if (!isGenerating) {
@ -457,6 +450,20 @@ class ChatActivity : AppCompatActivity() {
super.onActivityResult(requestCode, resultCode, data)
this.chatInputModule!!.handleResult(requestCode, resultCode, data)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// Forward permission results to the attachment picker module
this.chatInputModule?.let { inputModule ->
if (inputModule is ChatInputComponent) {
inputModule.attachmentPickerModule?.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
}
private suspend fun handleSendMessage(userData: ChatDataItem): HashMap<String, Any> {
return chatPresenter.sendMessage(userData)

View File

@ -2,15 +2,19 @@
// Copyright (c) 2024 Alibaba Group Holding Limited All rights reserved.
package com.alibaba.mnnllm.android.chat.input
import android.Manifest
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.provider.MediaStore
import android.util.Log
import android.view.View
import android.widget.ImageView
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import com.alibaba.mnnllm.android.R
import com.alibaba.mnnllm.android.chat.ChatActivity
@ -85,11 +89,11 @@ class AttachmentPickerModule(private val activity: ChatActivity) {
intent.addCategory(Intent.CATEGORY_OPENABLE)
try {
activity.startActivityForResult(
Intent.createChooser(intent, "Select a WAV file"),
Intent.createChooser(intent, activity.getString(R.string.select_wav_file)),
REQUEST_CODE_SELECT_WAV
)
} catch (ex: ActivityNotFoundException) {
Toast.makeText(this.activity, "Please install a File Manager.", Toast.LENGTH_SHORT)
Toast.makeText(this.activity, R.string.file_manager_required, Toast.LENGTH_SHORT)
.show()
}
}
@ -106,13 +110,33 @@ class AttachmentPickerModule(private val activity: ChatActivity) {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.setType("image/*")
activity.startActivityForResult(
Intent.createChooser(intent, "Select Picture"),
Intent.createChooser(intent, activity.getString(R.string.select_picture)),
REQUEST_CODE_SELECT_IMAGE,
null
)
}
private fun takePhoto() {
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted, request it
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA)) {
// Show rationale to user
Toast.makeText(activity, R.string.camera_permission_rationale, Toast.LENGTH_LONG).show()
}
ActivityCompat.requestPermissions(
activity,
arrayOf(Manifest.permission.CAMERA),
REQUEST_CODE_CAMERA_PERMISSION
)
return
}
// Permission is granted, proceed with camera
startCameraIntent()
}
private fun startCameraIntent() {
val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
photoFile = File(
FileUtils.generateDestPhotoFilePath(
@ -127,7 +151,15 @@ class AttachmentPickerModule(private val activity: ChatActivity) {
photoFile!!
)
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileProviderUri)
activity.startActivityForResult(cameraIntent, REQUEST_CODE_CAPTURE_IMAGE)
try {
activity.startActivityForResult(cameraIntent, REQUEST_CODE_CAPTURE_IMAGE)
} catch (e: SecurityException) {
Log.e(TAG, "Camera permission denied", e)
Toast.makeText(activity, R.string.camera_permission_denied, Toast.LENGTH_SHORT).show()
} catch (e: ActivityNotFoundException) {
Log.e(TAG, "No camera app found", e)
Toast.makeText(activity, R.string.no_camera_app_found, Toast.LENGTH_SHORT).show()
}
}
fun setOnImagePickCallback(callback: ImagePickCallback?) {
@ -135,14 +167,14 @@ class AttachmentPickerModule(private val activity: ChatActivity) {
}
fun canHandleResult(requestCode: Int): Boolean {
return requestCode >= REQUEST_CODE_SELECT_WAV && requestCode <= REQUEST_CODE_CAPTURE_IMAGE
return requestCode >= REQUEST_CODE_SELECT_WAV && requestCode <= REQUEST_CODE_CAPTURE_IMAGE ||
requestCode == REQUEST_CODE_CAMERA_PERMISSION
}
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_CODE_CAPTURE_IMAGE) {
if (resultCode == Activity.RESULT_OK) {
if (imageUri != null) {
showImagePreview()
val imagePath = imageUri?.path
Log.d("ImagePath", "Image saved to: $imagePath")
showImagePreview()
@ -181,12 +213,24 @@ class AttachmentPickerModule(private val activity: ChatActivity) {
showAudioPreview(Uri.fromFile(destFile))
} catch (e: IOException) {
Log.e(TAG, "get audio file failed", e)
Toast.makeText(this.activity, "get audio file failed", Toast.LENGTH_SHORT)
Toast.makeText(this.activity, R.string.audio_file_failed, Toast.LENGTH_SHORT)
.show()
}
}
}
}
fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
if (requestCode == REQUEST_CODE_CAMERA_PERMISSION) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Camera permission granted, proceed with camera
startCameraIntent()
} else {
// Camera permission denied
Toast.makeText(activity, R.string.camera_permission_denied, Toast.LENGTH_SHORT).show()
}
}
}
private fun showAudioPreview(audioUri: Uri) {
attachmentPreview.setImageResource(R.drawable.ic_audio_attachment)
@ -256,6 +300,7 @@ class AttachmentPickerModule(private val activity: ChatActivity) {
companion object {
const val TAG: String = "ImagePickerModule"
var REQUEST_CODE_CAPTURE_IMAGE: Int = 100
const val REQUEST_CODE_CAMERA_PERMISSION: Int = 101
var REQUEST_CODE_SELECT_IMAGE: Int = 99
var REQUEST_CODE_SELECT_WAV: Int = 98

View File

@ -42,7 +42,7 @@ class ChatInputComponent(
private lateinit var editUserMessage: EditText
private var buttonSend: ImageView = binding.btnSend
private lateinit var imageMore: ImageView
private var attachmentPickerModule: AttachmentPickerModule? = null
var attachmentPickerModule: AttachmentPickerModule? = null
private lateinit var voiceRecordingModule: VoiceRecordingModule
private var currentUserMessage: ChatDataItem? = null
private var buttonSwitchVoice: View? = null
@ -335,6 +335,9 @@ class ChatInputComponent(
} else {
voiceRecordingModule.handlePermissionDenied()
}
} else if (attachmentPickerModule != null &&
requestCode == AttachmentPickerModule.REQUEST_CODE_CAMERA_PERMISSION) {
attachmentPickerModule!!.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
</selector>

View File

@ -54,6 +54,13 @@
<string name="diffusion_generate_progress">图片生成进度: %1$s%%</string>
<string name="no_history">没有历史记录,请在模型列表页面选择模型聊天</string>
<string name="recording_permission_denied">请允许录音权限</string>
<string name="camera_permission_denied">需要相机权限才能拍照</string>
<string name="camera_permission_rationale">需要相机权限来为聊天拍照</string>
<string name="no_camera_app_found">此设备上没有找到相机应用</string>
<string name="file_manager_required">请安装文件管理器</string>
<string name="audio_file_failed">获取音频文件失败</string>
<string name="select_picture">选择图片</string>
<string name="select_wav_file">选择 WAV 文件</string>
<string name="release_to_send">松开发送,上滑取消</string>
<string name="release_to_cancel">松开取消</string>
<string name="r1_thinking_message">思考中…</string>

View File

@ -55,6 +55,13 @@
<string name="diffusion_generate_progress">Image Generate progress: %1$s%%</string>
<string name="no_history">No history, please select model in the model list and chat</string>
<string name="recording_permission_denied">Please allow permission to record audio</string>
<string name="camera_permission_denied">Camera permission is required to take photos</string>
<string name="camera_permission_rationale">Camera access is needed to take photos for your chat</string>
<string name="no_camera_app_found">No camera app found on this device</string>
<string name="file_manager_required">Please install a File Manager</string>
<string name="audio_file_failed">Failed to get audio file</string>
<string name="select_picture">Select Picture</string>
<string name="select_wav_file">Select a WAV file</string>
<string name="release_to_send">Release to Send, Slide up to cancel</string>
<string name="release_to_cancel">Release to cancel</string>
<string name="scroll_to_bottom">Scroll to bottom</string>

View File

@ -0,0 +1,348 @@
#!/bin/bash
# Model Test Script for MnnLlmChat
# This script pushes model files and test binaries to the phone and runs a test
#
# Usage:
# ./scripts/push_model_test.sh # Normal verbose output
# VERBOSE=false ./scripts/push_model_test.sh # Quiet mode, less output
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
PROJECT_NAME="MnnLlmChat"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
ROOT_PROJECT_DIR="$(dirname "$(dirname "$(dirname "$PROJECT_DIR")")")"
# Control verbosity (set to false to reduce output)
VERBOSE=${VERBOSE:-true}
# Paths
MODELSCOPE_CACHE_DIR="$HOME/.cache/modelscope"
MNN_MODELS_DIR="$MODELSCOPE_CACHE_DIR/hub/models/MNN"
BUILD_64_DIR="$ROOT_PROJECT_DIR/project/android/build_64"
PHONE_TEST_DIR="/data/local/tmp/test_model"
# Functions
log_info() {
if [[ "$VERBOSE" == "true" ]]; then
echo -e "${BLUE}[INFO]${NC} $1"
fi
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
check_requirements() {
log_info "Checking requirements..."
# Check if we're in the right directory
if [[ ! -f "app/build.gradle" ]]; then
log_error "This script must be run from the project root directory"
exit 1
fi
# Check if ADB is available
if ! command -v adb &> /dev/null; then
log_error "ADB is not installed or not in PATH"
exit 1
fi
# Check if modelscope cache directory exists
if [[ ! -d "$MODELSCOPE_CACHE_DIR" ]]; then
log_error "ModelScope cache directory not found: $MODELSCOPE_CACHE_DIR"
exit 1
fi
# Check if models directory exists (either MNN subdirectory or main models directory)
if [[ ! -d "$MNN_MODELS_DIR" ]] && [[ ! -d "$MODELSCOPE_CACHE_DIR/hub/models" ]]; then
log_error "Neither MNN models directory nor main models directory found"
log_info "Available directories in $MODELSCOPE_CACHE_DIR:"
ls -la "$MODELSCOPE_CACHE_DIR" 2>/dev/null || true
exit 1
fi
# Check if build_64 directory exists
if [[ ! -d "$BUILD_64_DIR" ]]; then
log_error "Build 64 directory not found: $BUILD_64_DIR"
exit 1
fi
log_success "Requirements check completed"
}
check_device_connection() {
log_info "Checking device connection..."
# Check if any device is connected
if ! adb devices | grep -q "device$"; then
log_error "No Android device connected. Please connect a device and enable USB debugging."
exit 1
fi
# Get device info
DEVICE_ID=$(adb devices | grep "device$" | head -n1 | cut -f1)
log_info "Connected device: $DEVICE_ID"
# Check if device is rooted (needed for /data/local/tmp access)
if ! adb shell "su -c 'ls /data/local/tmp'" &> /dev/null; then
log_warning "Device may not be rooted. Some operations might fail."
fi
log_success "Device connection verified"
}
setup_phone_directory() {
log_info "Setting up phone directory..."
# Create test directory on phone
adb shell "mkdir -p $PHONE_TEST_DIR"
adb shell "chmod 755 $PHONE_TEST_DIR"
log_success "Phone directory setup completed"
}
select_and_push_model() {
log_info "Selecting model from models directory..."
# First try MNN subdirectory, then fall back to main models directory
if [[ -d "$MNN_MODELS_DIR" ]]; then
MODELS_DIR="$MNN_MODELS_DIR"
log_info "Using MNN models directory: $MODELS_DIR"
else
MODELS_DIR="$MODELSCOPE_CACHE_DIR/hub/models"
log_info "MNN subdirectory not found, using main models directory: $MODELS_DIR"
fi
# Debug: show what's actually in the models directory
if [[ "$VERBOSE" == "true" ]]; then
log_info "Contents of $MODELS_DIR:"
ls -la "$MODELS_DIR" 2>/dev/null || true
echo "----------------------------------------"
fi
# List available models
log_info "Available models in $MODELS_DIR:"
echo "----------------------------------------"
# Get list of model directories (excluding . and .. and the parent directory)
MODEL_DIRS=()
# Get top-level directories, excluding . and .. and the parent directory itself
while IFS= read -r -d '' dir; do
if [[ "$dir" != "$MODELS_DIR" ]]; then
MODEL_DIRS+=("$dir")
fi
done < <(find "$MODELS_DIR" -maxdepth 1 -type d ! -name "." ! -name ".." -print0 | sort -z)
# Debug: show what we found
if [[ "$VERBOSE" == "true" ]]; then
log_info "Found ${#MODEL_DIRS[@]} model directories:"
for dir in "${MODEL_DIRS[@]}"; do
log_info " Found: $dir"
done
fi
if [[ ${#MODEL_DIRS[@]} -eq 0 ]]; then
log_error "No model directories found in $MODELS_DIR. Please check your ModelScope cache structure."
return 1
fi
# Display models with numbers
for i in "${!MODEL_DIRS[@]}"; do
model_name=$(basename "${MODEL_DIRS[$i]}")
echo "$((i+1)). $model_name"
done
echo "----------------------------------------"
# Get user selection
while true; do
read -p "Select a model (1-${#MODEL_DIRS[@]}): " selection
if [[ "$selection" =~ ^[0-9]+$ ]] && [[ "$selection" -ge 1 ]] && [[ "$selection" -le "${#MODEL_DIRS[@]}" ]]; then
SELECTED_MODEL_DIR="${MODEL_DIRS[$((selection-1))]}"
break
else
echo "Please enter a valid number between 1 and ${#MODEL_DIRS[@]}"
fi
done
SELECTED_MODEL_NAME=$(basename "$SELECTED_MODEL_DIR")
log_info "Selected model: $SELECTED_MODEL_NAME"
# Push the selected model files
log_info "Pushing all files for $SELECTED_MODEL_NAME..."
# Push all files from the selected directory
MODEL_FILES=$(find "$SELECTED_MODEL_DIR" -type f 2>/dev/null)
if [[ -z "$MODEL_FILES" ]]; then
log_warning "No files found in $SELECTED_MODEL_NAME directory"
return 1
fi
# Push each file
for file in $MODEL_FILES; do
filename=$(basename "$file")
log_info "Pushing $filename..."
adb push "$file" "$PHONE_TEST_DIR/"
done
log_success "Model files for $SELECTED_MODEL_NAME pushed to phone"
# Show what files were pushed
if [[ "$VERBOSE" == "true" ]]; then
log_info "Files pushed for $SELECTED_MODEL_NAME:"
adb shell "ls -la $PHONE_TEST_DIR/*" 2>/dev/null || true
fi
}
push_test_binaries() {
log_info "Pushing test binaries from build_64 directory..."
# Check for llm_demo binary
if [[ -f "$BUILD_64_DIR/llm_demo" ]]; then
log_info "Pushing llm_demo binary..."
adb push "$BUILD_64_DIR/llm_demo" "$PHONE_TEST_DIR/"
adb shell "chmod +x $PHONE_TEST_DIR/llm_demo"
else
log_warning "llm_demo binary not found in $BUILD_64_DIR"
fi
# Push .so files
SO_FILES=$(find "$BUILD_64_DIR" -name "*.so" 2>/dev/null)
if [[ -n "$SO_FILES" ]]; then
log_info "Pushing .so files..."
for file in $SO_FILES; do
filename=$(basename "$file")
log_info "Pushing $filename..."
adb push "$file" "$PHONE_TEST_DIR/"
done
else
log_warning "No .so files found in $BUILD_64_DIR"
fi
# Push other important files
for file in "config.json" "tokenizer.json" "tokenizer_config.json" "special_tokens_map.json"; do
if [[ -f "$BUILD_64_DIR/$file" ]]; then
log_info "Pushing $file..."
adb push "$BUILD_64_DIR/$file" "$PHONE_TEST_DIR/"
fi
done
# Push libMNN.so from the correct location
if [[ -f "$BUILD_64_DIR/libMNN.so" ]]; then
log_info "Found libMNN.so in build directory"
else
log_error "libMNN.so not found at $BUILD_64_DIR/libMNN.so. Please ensure it's built."
return 1
fi
log_success "Test binaries pushed to phone"
}
run_test() {
log_info "Running test on phone..."
# Create prompt file
adb shell "echo 'hello' > $PHONE_TEST_DIR/prompt"
# Find config file
CONFIG_FILE=""
for config in "config.json" "model_config.json" "config.ini"; do
if adb shell "test -f $PHONE_TEST_DIR/$config" 2>/dev/null; then
CONFIG_FILE="$config"
break
fi
done
# Set LD_LIBRARY_PATH to include current directory for .so files
if [[ -z "$CONFIG_FILE" ]]; then
log_warning "No config file found, trying to run without config..."
adb shell "cd $PHONE_TEST_DIR && export LD_LIBRARY_PATH=./ && ./llm_demo prompt"
else
log_info "Using config file: $CONFIG_FILE"
adb shell "cd $PHONE_TEST_DIR && export LD_LIBRARY_PATH=./ && ./llm_demo $CONFIG_FILE prompt"
fi
log_success "Test execution completed"
}
list_phone_files() {
if [[ "$VERBOSE" == "true" ]]; then
log_info "Listing files on phone..."
adb shell "ls -la $PHONE_TEST_DIR"
fi
}
verify_library_dependencies() {
log_info "Verifying library dependencies..."
# Check if libMNN.so is present
if adb shell "test -f $PHONE_TEST_DIR/libMNN.so" 2>/dev/null; then
log_success "libMNN.so found on phone"
else
log_error "libMNN.so not found on phone - this will cause execution to fail"
return 1
fi
# Check if llm_demo is executable
if adb shell "test -x $PHONE_TEST_DIR/llm_demo" 2>/dev/null; then
log_success "llm_demo is executable on phone"
else
log_error "llm_demo is not executable on phone"
return 1
fi
log_success "Library dependencies verified"
}
cleanup() {
log_info "Cleaning up..."
# Optionally remove test directory from phone
read -p "Do you want to remove the test directory from phone? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
adb shell "rm -rf $PHONE_TEST_DIR"
log_success "Phone test directory removed"
fi
}
main() {
log_info "Starting Model Test Script for $PROJECT_NAME"
check_requirements
check_device_connection
setup_phone_directory
select_and_push_model
push_test_binaries
list_phone_files
verify_library_dependencies
run_test
log_success "Model test script completed successfully"
cleanup
}
# Handle script interruption
trap 'log_error "Script interrupted"; exit 1' INT TERM
# Run main function
main "$@"

View File

@ -144,7 +144,7 @@ build_googleplay_release() {
./gradlew bundleGoogleplayRelease
# Copy AAB to output directory
AAB_PATH="$BUILD_DIR/outputs/bundle/googleplay/release/app-googleplay-release.aab"
AAB_PATH="$BUILD_DIR/outputs/bundle/googleplayRelease/app-googleplay-release.aab"
if [[ -f "$AAB_PATH" ]]; then
cp "$AAB_PATH" "$GOOGLE_PLAY_DIR/"
log_success "Google Play release AAB built: $GOOGLE_PLAY_DIR/app-googleplay-release.aab"