diff --git a/cmake/OpenCVDetectAndroidSDK.cmake b/cmake/OpenCVDetectAndroidSDK.cmake
index 0e0240ca86..b125561d4a 100644
--- a/cmake/OpenCVDetectAndroidSDK.cmake
+++ b/cmake/OpenCVDetectAndroidSDK.cmake
@@ -264,13 +264,23 @@ macro(add_android_project target path)
ocv_list_filterout(android_proj_jni_files "\\\\.svn")
if(android_proj_jni_files AND EXISTS ${path}/jni/Android.mk AND NOT DEFINED JNI_LIB_NAME)
+ # find local module name in Android.mk file to build native lib
file(STRINGS "${path}/jni/Android.mk" JNI_LIB_NAME REGEX "LOCAL_MODULE[ ]*:=[ ]*.*" )
string(REGEX REPLACE "LOCAL_MODULE[ ]*:=[ ]*([a-zA-Z_][a-zA-Z_0-9]*)[ ]*" "\\1" JNI_LIB_NAME "${JNI_LIB_NAME}")
+ # find using of native app glue to determine native activity
+ file(STRINGS "${path}/jni/Android.mk" NATIVE_APP_GLUE REGEX ".*(call import-module,android/native_app_glue)" )
+
if(JNI_LIB_NAME)
ocv_include_modules_recurse(${android_proj_NATIVE_DEPS})
ocv_include_directories("${path}/jni")
+ if (NATIVE_APP_GLUE)
+ include_directories(${ANDROID_NDK}/sources/android/native_app_glue)
+ list(APPEND android_proj_jni_files ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
+ set(android_proj_NATIVE_DEPS ${android_proj_NATIVE_DEPS} android)
+ endif()
+
add_library(${JNI_LIB_NAME} MODULE ${android_proj_jni_files})
target_link_libraries(${JNI_LIB_NAME} ${OPENCV_LINKER_LIBS} ${android_proj_NATIVE_DEPS})
diff --git a/samples/android/CMakeLists.txt b/samples/android/CMakeLists.txt
index a0794cb972..9d7b0cbf0f 100644
--- a/samples/android/CMakeLists.txt
+++ b/samples/android/CMakeLists.txt
@@ -11,6 +11,10 @@ add_subdirectory(face-detection)
add_subdirectory(image-manipulations)
add_subdirectory(color-blob-detection)
+if (ANDROID_NATIVE_API_LEVEL GREATER 8)
+ add_subdirectory(native-activity)
+endif()
+
add_subdirectory(tutorial-1-camerapreview)
add_subdirectory(tutorial-2-mixedprocessing)
add_subdirectory(tutorial-3-cameracontrol)
diff --git a/samples/android/native-activity/AndroidManifest.xml b/samples/android/native-activity/AndroidManifest.xml
new file mode 100644
index 0000000000..369bc75027
--- /dev/null
+++ b/samples/android/native-activity/AndroidManifest.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/android/native-activity/jni/Android.mk b/samples/android/native-activity/jni/Android.mk
new file mode 100644
index 0000000000..720d38b40d
--- /dev/null
+++ b/samples/android/native-activity/jni/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+include ../../sdk/native/jni/OpenCV.mk
+
+LOCAL_MODULE := native_activity
+LOCAL_SRC_FILES := native.cpp
+LOCAL_LDLIBS := -lm -llog -landroid
+LOCAL_STATIC_LIBRARIES := android_native_app_glue
+
+include $(BUILD_SHARED_LIBRARY)
+
+$(call import-module,android/native_app_glue)
diff --git a/samples/android/native-activity/jni/Application.mk b/samples/android/native-activity/jni/Application.mk
new file mode 100644
index 0000000000..a89e12df19
--- /dev/null
+++ b/samples/android/native-activity/jni/Application.mk
@@ -0,0 +1,2 @@
+APP_ABI := armeabi-v7a
+APP_PLATFORM := android-9
diff --git a/samples/android/native-activity/jni/native.cpp b/samples/android/native-activity/jni/native.cpp
new file mode 100644
index 0000000000..38dda06038
--- /dev/null
+++ b/samples/android/native-activity/jni/native.cpp
@@ -0,0 +1,221 @@
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#define LOG_TAG "OCV:libnative_activity"
+#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
+#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
+
+struct Engine
+{
+ android_app* app;
+ cv::Ptr capture;
+};
+
+cv::Size calc_optimal_camera_resolution(const char* supported, int width, int height)
+{
+ int frame_width = 0;
+ int frame_height = 0;
+
+ size_t prev_idx = 0;
+ size_t idx = 0;
+ float min_diff = FLT_MAX;
+
+ do
+ {
+ int tmp_width;
+ int tmp_height;
+
+ prev_idx = idx;
+ while ((supported[idx] != '\0') && (supported[idx] != ','))
+ idx++;
+
+ sscanf(&supported[prev_idx], "%dx%d", &tmp_width, &tmp_height);
+
+ int w_diff = width - tmp_width;
+ int h_diff = height - tmp_height;
+ if ((h_diff >= 0) && (w_diff >= 0))
+ {
+ if ((h_diff <= min_diff) && (tmp_height <= 720))
+ {
+ frame_width = tmp_width;
+ frame_height = tmp_height;
+ min_diff = h_diff;
+ }
+ }
+
+ idx++; // to skip coma symbol
+
+ } while(supported[idx-1] != '\0');
+
+ return cv::Size(frame_width, frame_height);
+}
+
+static void engine_draw_frame(Engine* engine, const cv::Mat& frame)
+{
+ if (engine->app->window == NULL)
+ return; // No window.
+
+ ANativeWindow_Buffer buffer;
+ if (ANativeWindow_lock(engine->app->window, &buffer, NULL) < 0)
+ {
+ LOGW("Unable to lock window buffer");
+ return;
+ }
+
+ void* pixels = buffer.bits;
+
+ int left_indent = (buffer.width-frame.cols)/2;
+ int top_indent = (buffer.height-frame.rows)/2;
+
+ for (int yy = top_indent; yy < std::min(frame.rows+top_indent, buffer.height); yy++)
+ {
+ unsigned char* line = (unsigned char*)pixels;
+ memcpy(line+left_indent*4*sizeof(unsigned char), frame.ptr(yy),
+ std::min(frame.cols, buffer.width)*4*sizeof(unsigned char));
+ // go to next line
+ pixels = (int32_t*)pixels + buffer.stride;
+ }
+ ANativeWindow_unlockAndPost(engine->app->window);
+}
+
+static void engine_handle_cmd(android_app* app, int32_t cmd)
+{
+ Engine* engine = (Engine*)app->userData;
+ switch (cmd)
+ {
+ case APP_CMD_INIT_WINDOW:
+ if (app->window != NULL)
+ {
+ LOGI("APP_CMD_INIT_WINDOW");
+
+ engine->capture = new cv::VideoCapture(0);
+
+ union {double prop; const char* name;} u;
+ u.prop = engine->capture->get(CV_CAP_PROP_SUPPORTED_PREVIEW_SIZES_STRING);
+
+ int view_width = ANativeWindow_getWidth(app->window);
+ int view_height = ANativeWindow_getHeight(app->window);
+
+ cv::Size camera_resolution;
+ if (u.name)
+ camera_resolution = calc_optimal_camera_resolution(u.name, 640, 480);
+ else
+ {
+ LOGE("Cannot get supported camera camera_resolutions");
+ camera_resolution = cv::Size(ANativeWindow_getWidth(app->window),
+ ANativeWindow_getHeight(app->window));
+ }
+
+ if ((camera_resolution.width != 0) && (camera_resolution.height != 0))
+ {
+ engine->capture->set(CV_CAP_PROP_FRAME_WIDTH, camera_resolution.width);
+ engine->capture->set(CV_CAP_PROP_FRAME_HEIGHT, camera_resolution.height);
+ }
+
+ float scale = std::min((float)view_width/camera_resolution.width,
+ (float)view_height/camera_resolution.height);
+
+ if (ANativeWindow_setBuffersGeometry(app->window, (int)(view_width/scale),
+ int(view_height/scale), WINDOW_FORMAT_RGBA_8888) < 0)
+ {
+ LOGE("Cannot set pixel format!");
+ return;
+ }
+
+ LOGI("Camera initialized at resoution %dx%d", camera_resolution.width, camera_resolution.height);
+ }
+ break;
+ case APP_CMD_TERM_WINDOW:
+ LOGI("APP_CMD_TERM_WINDOW");
+
+ engine->capture->release();
+ break;
+ }
+}
+
+void android_main(android_app* app)
+{
+ Engine engine;
+
+ // Make sure glue isn't stripped.
+ app_dummy();
+
+ memset(&engine, 0, sizeof(engine));
+ app->userData = &engine;
+ app->onAppCmd = engine_handle_cmd;
+ engine.app = app;
+
+ float fps = 0;
+ cv::Mat drawing_frame;
+ std::queue time_queue;
+
+ // loop waiting for stuff to do.
+ while (1)
+ {
+ // Read all pending events.
+ int ident;
+ int events;
+ android_poll_source* source;
+
+ // Process system events
+ while ((ident=ALooper_pollAll(0, NULL, &events, (void**)&source)) >= 0)
+ {
+ // Process this event.
+ if (source != NULL)
+ {
+ source->process(app, source);
+ }
+
+ // Check if we are exiting.
+ if (app->destroyRequested != 0)
+ {
+ LOGI("Engine thread destroy requested!");
+ return;
+ }
+ }
+
+ int64 then;
+ int64 now = cv::getTickCount();
+ time_queue.push(now);
+
+ // Capture frame from camera and draw it
+ if (!engine.capture.empty())
+ {
+ if (engine.capture->grab())
+ engine.capture->retrieve(drawing_frame, CV_CAP_ANDROID_COLOR_FRAME_RGBA);
+
+ char buffer[256];
+ sprintf(buffer, "Display performance: %dx%d @ %.3f", drawing_frame.cols, drawing_frame.rows, fps);
+ cv::putText(drawing_frame, std::string(buffer), cv::Point(8,64),
+ cv::FONT_HERSHEY_COMPLEX_SMALL, 1, cv::Scalar(0,255,0,255));
+ engine_draw_frame(&engine, drawing_frame);
+ }
+
+ if (time_queue.size() >= 2)
+ then = time_queue.front();
+ else
+ then = 0;
+
+ if (time_queue.size() >= 25)
+ time_queue.pop();
+
+ fps = time_queue.size() * (float)cv::getTickFrequency() / (now-then);
+ }
+}
diff --git a/samples/android/native-activity/res/drawable/icon.png b/samples/android/native-activity/res/drawable/icon.png
new file mode 100644
index 0000000000..630454927b
Binary files /dev/null and b/samples/android/native-activity/res/drawable/icon.png differ
diff --git a/samples/android/native-activity/res/values/strings.xml b/samples/android/native-activity/res/values/strings.xml
new file mode 100644
index 0000000000..888534efe0
--- /dev/null
+++ b/samples/android/native-activity/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ OCV Native Activity
+
diff --git a/samples/android/native-activity/src/org/opencv/samples/NativeActivity/CvNativeActivity.java b/samples/android/native-activity/src/org/opencv/samples/NativeActivity/CvNativeActivity.java
new file mode 100644
index 0000000000..d8e437533e
--- /dev/null
+++ b/samples/android/native-activity/src/org/opencv/samples/NativeActivity/CvNativeActivity.java
@@ -0,0 +1,44 @@
+package org.opencv.samples.NativeActivity;
+
+import org.opencv.android.BaseLoaderCallback;
+import org.opencv.android.LoaderCallbackInterface;
+import org.opencv.android.OpenCVLoader;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+public class CvNativeActivity extends Activity {
+ private static final String TAG = "OCVSample::Activity";
+
+ private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
+ @Override
+ public void onManagerConnected(int status) {
+ switch (status) {
+ case LoaderCallbackInterface.SUCCESS:
+ {
+ Log.i(TAG, "OpenCV loaded successfully");
+ System.loadLibrary("native_activity");
+ Intent intent = new Intent(CvNativeActivity.this, android.app.NativeActivity.class);
+ CvNativeActivity.this.startActivity(intent);
+ } break;
+ default:
+ {
+ super.onManagerConnected(status);
+ } break;
+ }
+ }
+ };
+
+ public CvNativeActivity() {
+ Log.i(TAG, "Instantiated new " + this.getClass());
+ }
+
+ @Override
+ public void onResume()
+ {
+ super.onResume();
+ OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
+ }
+}
\ No newline at end of file