diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp index e205bd6..5cbb2dd 100644 --- a/app/src/main/cpp/main.cpp +++ b/app/src/main/cpp/main.cpp @@ -16,6 +16,7 @@ FaceApp* g_Application = nullptr; AAssetManager* g_assetManager = nullptr; jobject g_callback = nullptr; +jobject g_callbackAnimationFinished = nullptr; void handle_cmd(android_app *pApp, int32_t cmd) { switch (cmd) { @@ -149,22 +150,7 @@ Java_com_hmwl_face_1sdk_FaceActivity_SetCppInitArg(JNIEnv *env, jobject thiz, js } memcpy(g_InitArgString, nativeString, len); } -extern "C" -JNIEXPORT void JNICALL -Java_com_hmwl_face_1sdk_FaceActivity_ChangeStateCpp(JNIEnv *env, jobject thiz, jstring json) { - // TODO: implement ChangeStateCpp() - const char *nativeString = env->GetStringUTFChars(json, nullptr); - jsize len = env->GetStringUTFLength(json); - if(len > ArgLen) - { - aout << "ArgLen to long:" << len << std::endl; - return; - } - if(g_Application->isInited()) - { - g_Application->changeMotion(nativeString); - } -} + extern "C" JNIEXPORT void JNICALL Java_com_hmwl_face_1sdk_FaceActivity_StopRunning(JNIEnv *env, jobject thiz) { @@ -241,6 +227,63 @@ void CppCallback(const string& motion_type) } +void CppAnimationFinishedCallback() +{ + JNIEnv* env = nullptr; + bool needDetach = false; + + // 获取当前线程的 JNIEnv + JavaVM* vm = g_jvm; // 你需要提前保存 JavaVM* 到全局变量 g_jvm + int status = vm->GetEnv((void**)&env, JNI_VERSION_1_6); + if (status == JNI_EDETACHED) { + // 当前线程未 attach,需 attach + if (vm->AttachCurrentThread(&env, nullptr) != 0) { + return; + } + needDetach = true; + } else if (status != JNI_OK) { + return; + } + + if (g_callbackAnimationFinished == nullptr) { + if (needDetach) vm->DetachCurrentThread(); + return; + } + + // 1. 获取 CallbackInterface 的 class + jclass callbackClass = env->GetObjectClass(g_callbackAnimationFinished); + if (callbackClass == nullptr) { + if (needDetach) vm->DetachCurrentThread(); + return; + } + + // 2. 获取 OnLoadActionFinished 方法 ID(注意方法名大小写必须一致!) + jmethodID methodId = env->GetMethodID(callbackClass, "OnAnimationFinished", "()V"); + if (methodId == nullptr) { + env->ExceptionDescribe(); // 打印异常 + env->ExceptionClear(); + if (needDetach) vm->DetachCurrentThread(); + return; + } + + // 4. 调用 Java 回调 + env->CallVoidMethod(g_callbackAnimationFinished, methodId); + + // 5. 检查是否抛出异常 + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + + env->DeleteLocalRef(callbackClass); + + // 7. 如果是临时 attach 的线程,要 detach + if (needDetach) { + vm->DetachCurrentThread(); + } +} + + extern "C" JNIEXPORT jstring JNICALL @@ -268,4 +311,31 @@ Java_com_hmwl_face_1sdk_FaceActivity_PreReadAction(JNIEnv *env, jobject thiz, js std::string ret = g_Application->preReadyMotion(nativeString, CppCallback); return env->NewStringUTF(ret.c_str()); +} + + +extern "C" +JNIEXPORT void JNICALL +Java_com_hmwl_face_1sdk_FaceActivity_ChangeMotionCpp(JNIEnv *env, jobject thiz, jstring json, + jobject callback, jboolean loop) { + // TODO: implement ChangeMotionCpp() + const char *nativeString = env->GetStringUTFChars(json, nullptr); + jsize len = env->GetStringUTFLength(json); + if(len > ArgLen) + { + aout << "ArgLen to long:" << len << std::endl; + return; + } + + // 保存新的回调 + if (callback != nullptr) { + g_callbackAnimationFinished = env->NewGlobalRef(callback); + } else { + g_callbackAnimationFinished = nullptr; + } + + if(g_Application->isInited()) + { + g_Application->changeMotion(nativeString, CppAnimationFinishedCallback, loop); + } } \ No newline at end of file diff --git a/app/src/main/java/com/hmwl/face_sdk/FaceActivity.java b/app/src/main/java/com/hmwl/face_sdk/FaceActivity.java index 399db81..7d77f60 100644 --- a/app/src/main/java/com/hmwl/face_sdk/FaceActivity.java +++ b/app/src/main/java/com/hmwl/face_sdk/FaceActivity.java @@ -50,6 +50,21 @@ public class FaceActivity extends GameActivity implements FaceLandmarkerHelper.L } } + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + + if (requestCode == REQUEST_CAMERA_PERMISSION) { + // 检查权限是否被授予 + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // 用户授予了权限,启动相机 + startCamera(); + } else { + requestCameraPermission(); + } + } + } + private void startCamera() { ListenableFuture cameraProviderFuture = ProcessCameraProvider.getInstance(this); @@ -276,15 +291,19 @@ public class FaceActivity extends GameActivity implements FaceLandmarkerHelper.L void OnLoadActionFinished(String result); } + public interface AnimationFinishedCallbackInterface { + void OnAnimationFinished(); + } + protected native String PreReadAction(String motion, CallbackInterface callback); protected String PreLoadAction(String json, CallbackInterface callback){ return PreReadAction(json, callback); } - protected native void ChangeStateCpp(String json); - protected void ChangeMotion(String json){ + protected native void ChangeMotionCpp(String json, AnimationFinishedCallbackInterface callback, boolean loop); + protected void ChangeMotion(String json, AnimationFinishedCallbackInterface callback, boolean loop){ Log.i("FaceActivity", "ChangeState:" + json); - ChangeStateCpp(json); + ChangeMotionCpp(json, callback, loop); } // 添加这个缺失的方法 diff --git a/example/src/main/java/com/inewme/uvmirror/MakeupActivity.kt b/example/src/main/java/com/inewme/uvmirror/MakeupActivity.kt index 4ca279c..9ff3292 100644 --- a/example/src/main/java/com/inewme/uvmirror/MakeupActivity.kt +++ b/example/src/main/java/com/inewme/uvmirror/MakeupActivity.kt @@ -4,8 +4,11 @@ package com.inewme.uvmirror import android.content.Context import android.os.Bundle import android.util.Log +import android.view.KeyEvent +import android.view.KeyEvent.KEYCODE_BACK import android.view.MotionEvent import android.widget.Toast +import androidx.activity.OnBackPressedCallback import com.hmwl.face_sdk.FaceActivity import com.hmwl.face_sdk.InitArg import com.hmwl.face_sdk.Motion @@ -38,12 +41,31 @@ class MakeupActivity : FaceActivity() override fun OnLoadActionFinished(motion_type: String) { //加载完成后,切断到对应的动画 Log.i("info","加载完成后,切换到对应的动画") - ChangeMotion(motion_type) + ChangeMotion(motion_type, object : AnimationFinishedCallbackInterface{ + override fun OnAnimationFinished(){ + Log.i("info","动画播放完成,收到回调") + } + }, false) } }) Toast.makeText(this, "开始加载动画: $result", Toast.LENGTH_SHORT).show() } + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + if (keyCode == KEYCODE_BACK) { + Log.d("MakeupActivity", "Back key pressed") + handleBackPress() + return true // 消费事件,防止默认行为(如直接 finish) + } + return super.onKeyDown(keyCode, event) + } + + private fun handleBackPress() { + Stop() + finish() + } + + private fun getMotionByIndex(index: Int): Motion { pngFilesByFolder?.let { folders -> val motion = Motion().apply { @@ -98,7 +120,11 @@ class MakeupActivity : FaceActivity() else { //手动切换动画 - ChangeMotion(nextMotionType); + ChangeMotion(nextMotionType, object: AnimationFinishedCallbackInterface{ + override fun OnAnimationFinished(){ + Log.i("info","动画播放完成的回调") + } + }, false); nextMotionLoadFinished = false; curIndex++; Toast.makeText(this, "切换到下一个动画 $nextMotionType", Toast.LENGTH_SHORT).show() diff --git a/vulkan/FaceApp.cpp b/vulkan/FaceApp.cpp index 2a72b9c..8ec69fd 100644 --- a/vulkan/FaceApp.cpp +++ b/vulkan/FaceApp.cpp @@ -1322,7 +1322,19 @@ void FaceApp::drawFrame(long long frameTime) _curTexIndex++; if (_curTexIndex >= _curMotion_png_size) { - _curTexIndex = 0; + if(_animationFinishedCallback != nullptr) + { + _animationFinishedCallback(); + } + + if (_animationLoop) + { + _curTexIndex = 0; + } + else + { + _curTexIndex = _curMotion_png_size - 1; + } } game_time = game_time - actionTime; } @@ -1357,7 +1369,7 @@ string FaceApp::preReadyMotion(const std::string& json, Callback callback) return "failure"; } -void FaceApp::changeMotion(const string motion_type) +void FaceApp::changeMotion(const string motion_type, AnimationFinishedCallback callback, bool loop) { if (_curMotion_type == motion_type) { @@ -1387,6 +1399,8 @@ void FaceApp::changeMotion(const string motion_type) _curMotion_type = motion_type; _curMotion_png_size = _nextMotion.png_names.size(); _motionState = ready; + _animationFinishedCallback = callback; + _animationLoop = loop; // for (int i = 0; i < _curMotion.png_names.size(); ++i) diff --git a/vulkan/FaceApp.h b/vulkan/FaceApp.h index 3b5397b..ba87ceb 100644 --- a/vulkan/FaceApp.h +++ b/vulkan/FaceApp.h @@ -35,6 +35,7 @@ struct PushConstants { }; using Callback = std::function; +using AnimationFinishedCallback = std::function; class FaceApp :public Application { @@ -139,8 +140,10 @@ private: public: Callback _callback; + AnimationFinishedCallback _animationFinishedCallback; + bool _animationLoop = true; string preReadyMotion(const std::string& json, Callback callback); - void changeMotion(const string motion_type); + void changeMotion(const string motion_type, AnimationFinishedCallback callback, bool loop); //void changeMotion(Motion& motion); InitArg _initArg; //Motion _curMotion; diff --git a/vulkan/main.cpp b/vulkan/main.cpp index 831e6dd..3deda09 100644 --- a/vulkan/main.cpp +++ b/vulkan/main.cpp @@ -41,11 +41,18 @@ std::wstring ReadFileWithChinesePath(const std::wstring& filePath) { FaceApp* g_app; + +void CppAnimationFinishedCallback() +{ + std::cout << "call CppAnimationFinishedCallback\n"; +} + void CppCallback(const string& motion_type) { - g_app->changeMotion(motion_type.c_str()); + g_app->changeMotion(motion_type.c_str(), CppAnimationFinishedCallback, true); } + int main() { //std::string path = "chinese.txt";