From 8533b45ce9b8de70f4e743a778a6ea8594a5496c Mon Sep 17 00:00:00 2001
From: Alexander Alekhin <alexander.a.alekhin@gmail.com>
Date: Fri, 29 Dec 2017 04:15:30 +0000
Subject: [PATCH] cmake: Java/Android SDK refactoring

---
 CMakeLists.txt                                |   9 +
 cmake/OpenCVDetectAndroidSDK.cmake            |   6 +-
 cmake/OpenCVModule.cmake                      |   3 +
 doc/CMakeLists.txt                            |   4 +-
 modules/core/misc/java/gen_dict.json          |   2 +
 .../misc/java/src/java/core+Core.jcode.in     |  14 +
 .../dnn/misc/java/src/cpp/dnn_converters.hpp  |   2 +-
 modules/java/CMakeLists.txt                   | 475 +-----------------
 modules/java/android_sdk/CMakeLists.txt       |  88 ++++
 .../android_sdk/android_lib/jni/Android.mk    |  15 -
 .../android_lib/jni/Application.mk            |   3 -
 modules/java/common.cmake                     |  16 +
 modules/java/generator/CMakeLists.txt         | 103 ++++
 modules/java/generator/gen_java.py            | 375 +++++++-------
 modules/java/generator/src/cpp/Mat.cpp        | 130 ++---
 modules/java/generator/src/cpp/common.h       |  19 +-
 modules/java/generator/src/cpp/converters.cpp |   4 +
 modules/java/generator/src/cpp/converters.h   |   4 +
 modules/java/generator/src/cpp/jni_part.cpp   |   4 +
 .../java/generator/src/cpp/opencv_java.cpp    |   8 +
 .../java/generator/src/cpp/opencv_java.hpp    |  21 +-
 modules/java/generator/src/cpp/utils.cpp      |   8 +-
 .../opencv/osgi/OpenCVNativeLoader.java.in    |   2 +-
 .../generator/templates/cpp_module.template   |  52 ++
 .../generator/templates/java_class.prolog     |  15 +
 .../templates/java_class_inherited.prolog     |  12 +
 .../generator/templates/java_module.prolog    |  10 +
 modules/java/jar/CMakeLists.txt               |  53 ++
 modules/java/jar/build.xml.in                 |  27 +-
 modules/java/jni/CMakeLists.txt               |  90 ++++
 modules/java/test/android_test/CMakeLists.txt |  54 +-
 modules/java/test/pure_test/CMakeLists.txt    |  94 ++--
 modules/java/test/pure_test/build.xml         |  15 +-
 modules/ts/misc/run_suite.py                  |   2 +-
 modules/ts/misc/run_utils.py                  |   4 +-
 samples/android/15-puzzle/CMakeLists.txt      |   2 +-
 .../android/camera-calibration/CMakeLists.txt |   2 +-
 .../color-blob-detection/CMakeLists.txt       |   2 +-
 samples/android/face-detection/CMakeLists.txt |   4 +-
 .../image-manipulations/CMakeLists.txt        |   2 +-
 .../mobilenet-objdetect/CMakeLists.txt        |   2 +-
 .../tutorial-1-camerapreview/CMakeLists.txt   |   2 +-
 .../tutorial-2-mixedprocessing/CMakeLists.txt |   2 +-
 .../tutorial-3-cameracontrol/CMakeLists.txt   |   2 +-
 .../android/tutorial-4-opencl/CMakeLists.txt  |   4 +-
 45 files changed, 917 insertions(+), 850 deletions(-)
 create mode 100644 modules/core/misc/java/src/java/core+Core.jcode.in
 create mode 100644 modules/java/android_sdk/CMakeLists.txt
 delete mode 100644 modules/java/android_sdk/android_lib/jni/Android.mk
 delete mode 100644 modules/java/android_sdk/android_lib/jni/Application.mk
 create mode 100644 modules/java/common.cmake
 create mode 100644 modules/java/generator/CMakeLists.txt
 create mode 100644 modules/java/generator/src/cpp/opencv_java.cpp
 create mode 100644 modules/java/generator/templates/cpp_module.template
 create mode 100644 modules/java/generator/templates/java_class.prolog
 create mode 100644 modules/java/generator/templates/java_class_inherited.prolog
 create mode 100644 modules/java/generator/templates/java_module.prolog
 create mode 100644 modules/java/jar/CMakeLists.txt
 create mode 100644 modules/java/jni/CMakeLists.txt

diff --git a/CMakeLists.txt b/CMakeLists.txt
index e90f7db03c..e600d1cd2c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -458,6 +458,14 @@ if(INSTALL_TO_MANGLED_PATHS)
   endforeach()
 endif()
 
+if(ANDROID)
+  ocv_update(OPENCV_JNI_INSTALL_PATH "${OPENCV_LIB_INSTALL_PATH}")
+elseif(INSTALL_CREATE_DISTRIB)
+  ocv_update(OPENCV_JNI_INSTALL_PATH "${OPENCV_JAR_INSTALL_PATH}/${OpenCV_ARCH}")
+else()
+  ocv_update(OPENCV_JNI_INSTALL_PATH "${OPENCV_JAR_INSTALL_PATH}")
+endif()
+ocv_update(OPENCV_JNI_BIN_INSTALL_PATH "${OPENCV_JNI_INSTALL_PATH}")
 
 if(WIN32)
   # Postfix of DLLs:
@@ -608,6 +616,7 @@ include(cmake/OpenCVFindLAPACK.cmake)
 # ----------------------------------------------------------------------------
 
 # --- Doxygen for documentation ---
+add_custom_target(opencv_docs ALL)
 if(BUILD_DOCS)
   find_package(Doxygen)
 else()
diff --git a/cmake/OpenCVDetectAndroidSDK.cmake b/cmake/OpenCVDetectAndroidSDK.cmake
index 60832f0e59..e0bbd2860a 100644
--- a/cmake/OpenCVDetectAndroidSDK.cmake
+++ b/cmake/OpenCVDetectAndroidSDK.cmake
@@ -300,7 +300,7 @@ macro(add_android_project target path)
     if(android_proj_IGNORE_JAVA)
       set(android_proj_extra_deps "")
     else()
-      list(APPEND android_proj_extra_deps "${OpenCV_BINARY_DIR}/bin/classes.jar.dephelper" opencv_java)
+      list(APPEND android_proj_extra_deps opencv_java_android)
     endif()
     add_custom_command(
         OUTPUT "${android_proj_bin_dir}/bin/${target}-debug.apk"
@@ -333,10 +333,6 @@ macro(add_android_project target path)
       ocv_copyfiles_add_target(${target}_copy_libs NATIVE_COPY "Copy native libs for project: ${target}" ${_native_deps})
 
       add_dependencies(${target} ${target}_copy_libs)
-
-      if (ANDROID_EXAMPLES_WITH_LIBS)
-        add_dependencies(${target}_copy_libs opencv_java)
-      endif()
     endif()
 
     # There is some strange problem with concurrent Android .APK builds:
diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake
index f89e4cfd84..a73ccbd6bc 100644
--- a/cmake/OpenCVModule.cmake
+++ b/cmake/OpenCVModule.cmake
@@ -774,6 +774,9 @@ endmacro()
 #   ocv_create_module()
 macro(ocv_create_module)
   ocv_debug_message("${the_module}: ocv_create_module(" ${ARGN} ")")
+  if(OPENCV_MODULE_${the_module}_CLASS STREQUAL "BINDINGS")
+    message(FATAL_ERROR "Bindings module can't call ocv_create_module()")
+  endif()
   if(NOT " ${ARGN}" STREQUAL " ")
     set(OPENCV_MODULE_${the_module}_LINK_DEPS "${OPENCV_MODULE_${the_module}_LINK_DEPS};${ARGN}" CACHE INTERNAL "")
   endif()
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index aa049f9189..f1f5dc22b2 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -33,7 +33,7 @@ endif(HAVE_DOC_GENERATOR)
 
 if(BUILD_DOCS AND DOXYGEN_FOUND)
   # not documented modules list
-  list(APPEND blacklist "ts" "java" "python_bindings_generator" "python2" "python3" "js" "world")
+  list(APPEND blacklist "ts" "java_bindings_generator" "java" "python_bindings_generator" "python2" "python3" "js" "world")
   unset(CMAKE_DOXYGEN_TUTORIAL_CONTRIB_ROOT)
   unset(CMAKE_DOXYGEN_TUTORIAL_JS_ROOT)
 
@@ -252,4 +252,6 @@ if(BUILD_DOCS AND DOXYGEN_FOUND)
       DEPENDS doxygen
       COMMAND "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=docs -P "${CMAKE_BINARY_DIR}/cmake_install.cmake"
   )
+
+  add_dependencies(opencv_docs doxygen)
 endif()
diff --git a/modules/core/misc/java/gen_dict.json b/modules/core/misc/java/gen_dict.json
index e3ce880651..36afe4e57c 100644
--- a/modules/core/misc/java/gen_dict.json
+++ b/modules/core/misc/java/gen_dict.json
@@ -1,4 +1,6 @@
 {
+    "module_imports": [ "java.lang.String" ],
+    "module_j_code": "src/java/core+Core.jcode.in",
     "class_ignore_list" : [
         "FileNode",
         "FileStorage",
diff --git a/modules/core/misc/java/src/java/core+Core.jcode.in b/modules/core/misc/java/src/java/core+Core.jcode.in
new file mode 100644
index 0000000000..1a0fe21cd8
--- /dev/null
+++ b/modules/core/misc/java/src/java/core+Core.jcode.in
@@ -0,0 +1,14 @@
+    // these constants are wrapped inside functions to prevent inlining
+    private static String getVersion() { return "@OPENCV_VERSION@"; }
+    private static String getNativeLibraryName() { return "opencv_java@OPENCV_VERSION_MAJOR@@OPENCV_VERSION_MINOR@@OPENCV_VERSION_PATCH@"; }
+    private static int getVersionMajor() { return @OPENCV_VERSION_MAJOR@; }
+    private static int getVersionMinor() { return @OPENCV_VERSION_MINOR@; }
+    private static int getVersionRevision() { return @OPENCV_VERSION_PATCH@; }
+    private static String getVersionStatus() { return "@OPENCV_VERSION_STATUS@"; }
+
+    public static final String VERSION = getVersion();
+    public static final String NATIVE_LIBRARY_NAME = getNativeLibraryName();
+    public static final int VERSION_MAJOR = getVersionMajor();
+    public static final int VERSION_MINOR = getVersionMinor();
+    public static final int VERSION_REVISION = getVersionRevision();
+    public static final String VERSION_STATUS = getVersionStatus();
diff --git a/modules/dnn/misc/java/src/cpp/dnn_converters.hpp b/modules/dnn/misc/java/src/cpp/dnn_converters.hpp
index 1e152780fb..6a7270174c 100644
--- a/modules/dnn/misc/java/src/cpp/dnn_converters.hpp
+++ b/modules/dnn/misc/java/src/cpp/dnn_converters.hpp
@@ -8,7 +8,7 @@
 #define	DNN_CONVERTERS_HPP
 
 #include <jni.h>
-#include "opencv2/java.hpp"
+#include "opencv_java.hpp"
 #include "opencv2/core.hpp"
 #include "opencv2/dnn/dnn.hpp"
 
diff --git a/modules/java/CMakeLists.txt b/modules/java/CMakeLists.txt
index 76bfe6235a..8cc4bd07a6 100644
--- a/modules/java/CMakeLists.txt
+++ b/modules/java/CMakeLists.txt
@@ -1,6 +1,8 @@
-# ----------------------------------------------------------------------------
-#  CMake file for java support
-# ----------------------------------------------------------------------------
+if(OPENCV_INITIAL_PASS)
+  # generator for JNI/JAR source code and documentation signatures
+  add_subdirectory(generator)
+endif()
+
 if(APPLE_FRAMEWORK OR WINRT OR NOT PYTHON_DEFAULT_AVAILABLE OR NOT ANT_EXECUTABLE
     OR NOT (JNI_FOUND OR (ANDROID AND ANDROID_NATIVE_API_LEVEL GREATER 7))
     OR BUILD_opencv_world
@@ -9,75 +11,21 @@ if(APPLE_FRAMEWORK OR WINRT OR NOT PYTHON_DEFAULT_AVAILABLE OR NOT ANT_EXECUTABL
 endif()
 
 set(the_description "The java bindings")
-ocv_add_module(java BINDINGS opencv_core opencv_imgproc)
-
-if(EXISTS ${CMAKE_BINARY_DIR}/src)
-  execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_BINARY_DIR}/src")
-endif()
-if(EXISTS ${CMAKE_BINARY_DIR}/gen)
-  execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory "${CMAKE_BINARY_DIR}/gen")
-endif()
-
-if(ANDROID)
-  set(LIB_NAME_SUFIX "${OPENCV_VERSION_MAJOR}")
-else()
-  set(LIB_NAME_SUFIX "${OPENCV_VERSION_MAJOR}${OPENCV_VERSION_MINOR}${OPENCV_VERSION_PATCH}")
-endif()
-
-ocv_module_include_directories("${CMAKE_CURRENT_SOURCE_DIR}/generator/src/cpp")
-ocv_module_include_directories("${OpenCV_SOURCE_DIR}/include")
-
-if(NOT ANDROID)
-  include_directories(${JNI_INCLUDE_DIRS})
-endif()
-
-# output locations
-set(JAVA_INSTALL_ROOT "sdk/java")
-set(JNI_INSTALL_ROOT  "sdk/native")
-
-# get list of modules to wrap
-# message(STATUS "Wrapped in java:")
-set(OPENCV_JAVA_MODULES)
-foreach(m ${OPENCV_MODULES_BUILD})
-  if (";${OPENCV_MODULE_${m}_WRAPPERS};" MATCHES ";java;" AND HAVE_${m})
-    string(REPLACE "opencv_" "" m ${m})
-    list(APPEND OPENCV_JAVA_MODULES ${m})
-    # message(STATUS "\topencv_${m}")
-  endif()
-endforeach()
+ocv_add_module(java BINDINGS opencv_core opencv_imgproc PRIVATE_REQUIRED opencv_java_bindings_generator)
 
-######################################################################################################################################
-
-# UTILITY: make C headers go first
-macro(sort_headers_c_cpp __lst)
-  set(__cpp ${${__lst}})
-  ocv_list_filterout(__cpp "\\\\.h$")
-  if(__cpp)
-    list(REMOVE_ITEM ${__lst} ${__cpp})
-    list(APPEND ${__lst} ${__cpp})
-  endif()
-  unset(__cpp)
-endmacro()
-
-# UTILITY: force cmake to rerun when files from list have changed
-macro(add_cmake_dependencies)
-  foreach (f ${ARGN})
-    get_filename_component(f_name "${f}" NAME)
-    configure_file(${f} ${OpenCV_BINARY_DIR}/junk/${f_name}.junk COPYONLY)
-  endforeach()
-endmacro()
+include(${CMAKE_CURRENT_SOURCE_DIR}/common.cmake)
 
 # UTILITY: glob specific sources and append them to list (type is in H, CPP, JAVA, AIDL)
 macro(glob_more_specific_sources _type _root _output)
   unset(_masks)
   if(${_type} STREQUAL "H")
-    set(_masks "${_root}/src/cpp/*.h" "${root}/src/cpp/*.hpp")
+    set(_masks "${_root}/cpp/*.h" "${root}/cpp/*.hpp")
   elseif(${_type} STREQUAL "CPP")
-    set(_masks "${_root}/src/cpp/*.cpp")
+    set(_masks "${_root}/cpp/*.cpp")
   elseif(${_type} STREQUAL "JAVA")
-    set(_masks "${_root}/src/java/*.java" "${_root}/src/java/*.java.in")
+    set(_masks "${_root}/java/*.java" "${_root}/java/*.java.in")
   elseif(${_type} STREQUAL "AIDL")
-    set(_masks "${_root}/src/java/*.aidl")
+    set(_masks "${_root}/java/*.aidl")
   endif()
   if (_masks)
     file(GLOB _result ${_masks})
@@ -90,8 +38,8 @@ endmacro()
 # UTILITY: copy common java test files and add them to _deps
 # copy_common_tests(<source-folder> <destination-folder> <variable-to-store-deps>)
 macro(copy_common_tests _src_location _dst_location _deps)
-  set(_src ${${_src_location}})
-  set(_dst ${${_dst_location}})
+  set(_src ${_src_location})
+  set(_dst ${_dst_location})
   file(GLOB_RECURSE _files RELATIVE "${_src}" "${_src}/res/*" "${_src}/src/*")
   foreach(f ${_files})
     add_custom_command(
@@ -106,405 +54,18 @@ macro(copy_common_tests _src_location _dst_location _deps)
   unset(_dst)
 endmacro()
 
-# UTILITY: copy all java tests for specific module and add them to _deps
-# copy_modules_tests(<modules-list> <destination-folder> <variable-to-store-deps>)
-macro(copy_modules_tests _modules _dst_location _deps)
-  set(_dst ${${_dst_location}})
-  foreach(module ${${_modules}})
-    set(_src "${OPENCV_MODULE_opencv_${module}_LOCATION}/misc/java/test")
-    set(_tree "src/org/opencv/test/${module}")
-    file(GLOB _files RELATIVE "${_src}" "${_src}/*.java")
-    foreach (f ${_files})
-      add_custom_command(
-          OUTPUT "${_dst}/${_tree}/${f}"
-          COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_src}/${f}" "${_dst}/${_tree}/${f}"
-          MAIN_DEPENDENCY "${_src}/${f}"
-          COMMENT "Copying ${f}")
-      list(APPEND ${_deps} "${_src}/${f}" "${_dst}/${_tree}/${f}")
-    endforeach()
-    unset(_files)
-    unset(_src)
-    unset(_tree)
-  endforeach()
-  unset(_dst)
-endmacro()
-
-######################################################################################################################################
-
-# scripts
-set(scripts_gen_java "${CMAKE_CURRENT_SOURCE_DIR}/generator/gen_java.py")
-set(scripts_hdr_parser "${CMAKE_CURRENT_SOURCE_DIR}/../python/src2/hdr_parser.py")
-
-# directory to store temporary files generated on first gen_java.py run
-set(probe_dir "${CMAKE_CURRENT_BINARY_DIR}/test_gen")
-
-# handwritten C/C++ and Java sources
-glob_more_specific_sources(H "${CMAKE_CURRENT_SOURCE_DIR}/generator" handwritten_h_sources)
-glob_more_specific_sources(CPP "${CMAKE_CURRENT_SOURCE_DIR}/generator" handwritten_cpp_sources)
-glob_more_specific_sources(JAVA "${CMAKE_CURRENT_SOURCE_DIR}/generator" handwritten_java_sources)
-glob_more_specific_sources(AIDL "${CMAKE_CURRENT_SOURCE_DIR}/generator" handwritten_aidl_sources)
-
-# headers of OpenCV modules
-set(opencv_public_headers "")
-set(opencv_type_dicts "")
-set(generated_cpp_sources "")
-set(generated_java_sources "")
-foreach(module ${OPENCV_JAVA_MODULES})
-  set(module_java_dir "${OPENCV_MODULE_opencv_${module}_LOCATION}/misc/java")
-  # custom mappings between c and java/JNI types
-  set(custom_type_dict "${module_java_dir}/gen_dict.json")
-  if(EXISTS "${custom_type_dict}")
-    list(APPEND opencv_java_type_dict_${module} ${custom_type_dict})
-  endif()
-
-  set(custom_header_list "${module_java_dir}/filelist")
-  if(EXISTS "${custom_header_list}")
-    file(STRINGS "${custom_header_list}" module_headers)
-    ocv_list_add_prefix(module_headers "${OPENCV_MODULE_opencv_${module}_LOCATION}/")
-  else()
-    set(module_headers "")
-    # TODO Implement module's "_HEADERS_PUBLIC" variable (OpenCVModule.cmake)
-    foreach(hdr ${OPENCV_MODULE_opencv_${module}_HEADERS})
-      string(REGEX REPLACE "^.*opencv2/" "opencv2/" hdr2 "${hdr}")
-      if(NOT hdr2 MATCHES "opencv2/${m}/private.*" AND hdr2 MATCHES "^(opencv2/?.*)/[^/]+.h(..)?$" )
-        list(APPEND module_headers "${hdr}")
-      endif()
-    endforeach()
-  endif()
-
-  if(NOT module_headers)
-    message(WARNING "Module ${module} does not have headers to wrap for java")
-  endif()
-
-  sort_headers_c_cpp(module_headers)
-
-  set(opencv_public_headers_${module} ${module_headers})
-  list(APPEND opencv_public_headers ${module_headers})
-  list(APPEND generated_cpp_sources "${CMAKE_CURRENT_BINARY_DIR}/gen/${module}.cpp")
-
-  include_directories("${module_java_dir}/src/cpp")
-
-  set(common_header_list "${module_java_dir}/filelist_common")
-  if(EXISTS "${common_header_list}")
-    file(STRINGS "${common_header_list}" __headers)
-    ocv_list_add_prefix(__headers "${OPENCV_MODULE_opencv_${module}_LOCATION}/")
-    list(APPEND opencv_java_common_headers_${module} ${__headers})
-  endif()
-
-  foreach(m ${OPENCV_MODULE_opencv_${module}_DEPS})
-    set(common_header_list "${OPENCV_MODULE_${m}_LOCATION}/misc/java/filelist_common")
-    if(EXISTS "${common_header_list}")
-      file(STRINGS "${common_header_list}" __headers)
-      ocv_list_add_prefix(__headers "${OPENCV_MODULE_${m}_LOCATION}/")
-      list(APPEND opencv_java_common_headers_${module} ${__headers})
-    endif()
-    set(custom_type_dict "${OPENCV_MODULE_${m}_LOCATION}/misc/java/gen_dict.json")
-    if(EXISTS "${custom_type_dict}")
-      list(APPEND opencv_java_type_dict_${module} ${custom_type_dict})
-      list(APPEND opencv_type_dicts ${custom_type_dicts})
-    endif()
-  endforeach()
-
-  glob_more_specific_sources(H "${module_java_dir}" handwritten_h_sources)
-  glob_more_specific_sources(CPP "${module_java_dir}" handwritten_cpp_sources)
-  glob_more_specific_sources(JAVA "${module_java_dir}" handwritten_java_sources)
-  glob_more_specific_sources(AIDL "${module_java_dir}" handwritten_aidl_sources)
-
-  # first run of gen_java.py (to get list of generated files)
-  file(REMOVE_RECURSE "${probe_dir}")
-  file(MAKE_DIRECTORY "${probe_dir}")
-  execute_process(COMMAND ${PYTHON_DEFAULT_EXECUTABLE} "${scripts_gen_java}" "-p" "${scripts_hdr_parser}" "-m" ${module} "-s" ${opencv_public_headers_${module}} "-c" ${opencv_java_common_headers_${module}} "-t" ${opencv_java_type_dict_${module}}
-                  WORKING_DIRECTORY "${probe_dir}"
-                  OUTPUT_QUIET ERROR_QUIET)
-  file(GLOB_RECURSE generated_java_sources_${module} RELATIVE "${probe_dir}" "${probe_dir}/*.java")
-  ocv_list_add_prefix(generated_java_sources_${module} "${CMAKE_CURRENT_BINARY_DIR}/gen/")
-  list(APPEND generated_java_sources ${generated_java_sources_${module}})
-endforeach()
-
-file(REMOVE_RECURSE "${probe_dir}")
-
-if(NOT ANDROID)
-  ocv_list_filterout(handwritten_java_sources "/(engine|android)\\\\+")
-  ocv_list_filterout(handwritten_aidl_sources "/(engine|android)\\\\+")
-else()
-  file(GLOB_RECURSE handwrittren_lib_project_files_rel RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/android_lib/" "${CMAKE_CURRENT_SOURCE_DIR}/android_lib/*")
-  list(REMOVE_ITEM handwrittren_lib_project_files_rel "${ANDROID_MANIFEST_FILE}")
-
-  #  calc default SDK Target
-  android_get_compatible_target(android_sdk_target ${ANDROID_NATIVE_API_LEVEL} ${ANDROID_SDK_TARGET} 11)
-  string(REGEX REPLACE "android-" "" android_sdk_target_num ${android_sdk_target})
-  # filter out
-  if( (ANDROID_SDK_TARGET AND ANDROID_SDK_TARGET LESS 21) OR (android_sdk_target_num LESS 21) )
-    message(STATUS "[OpenCV for Android SDK]: A new OpenGL Camera Bridge (CameraGLSurfaceView, CameraGLRendererBase, CameraRenderer, Camera2Renderer) is disabled, because ANDROID_SDK_TARGET (${android_sdk_target_num}) < 21")
-    ocv_list_filterout(handwritten_java_sources "android\\\\+CameraGL")
-    ocv_list_filterout(handwritten_java_sources "android\\\\+Camera.?Renderer")
-  endif()
-endif()
-
-# IMPORTANT: add dependencies to cmake (we should rerun cmake if any of these files are modified)
-add_cmake_dependencies(${scripts_gen_java} ${scripts_hdr_parser} ${opencv_public_headers} ${opencv_type_dicts})
-
-######################################################################################################################################
-
-# step 1: generate .cpp/.java from OpenCV headers
-file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/gen")
-set(step1_depends "${scripts_gen_java}" "${scripts_hdr_parser}" ${opencv_public_headers} ${opencv_type_dicts})
-foreach(module ${OPENCV_JAVA_MODULES})
-  # second run of gen_java.py (at build time)
-  add_custom_command(OUTPUT ${generated_java_sources_${module}} "${CMAKE_CURRENT_BINARY_DIR}/gen/${module}.cpp"
-                     COMMAND ${PYTHON_DEFAULT_EXECUTABLE} "${scripts_gen_java}" "-p" "${scripts_hdr_parser}" "-m" ${module} "-s" ${opencv_public_headers_${module}} "-c" ${opencv_java_common_headers_${module}} "-t" ${opencv_java_type_dict_${module}}
-                     WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/gen/"
-                     DEPENDS "${scripts_gen_java}" "${scripts_hdr_parser}" ${opencv_public_headers_${module}} ${opencv_java_common_headers_${module}} ${opencv_java_type_dict_${module}}
-                    )
-endforeach()
-
-# step 2: TODO: generate documentation somewhere
-
-# step 3: copy files to destination
-set(step3_input_files ${generated_java_sources} ${handwritten_java_sources} ${handwritten_aidl_sources})
-set(copied_files "")
-set(java_src_dir "${OpenCV_BINARY_DIR}/src")
-foreach(java_file ${step3_input_files})
-  get_filename_component(java_file_name "${java_file}" NAME)
-  set(__configure 0)
-  if (java_file_name MATCHES "\\.in$")
-    string(REGEX REPLACE "\\.in$" "" java_file_name "${java_file_name}")
-    set(__configure 1)
-  endif()
-  string(REPLACE "-jdoc.java" ".java" java_file_name "${java_file_name}")
-  if(EXISTS "${java_file}")
-    file(STRINGS "${java_file}" PACKAGE_STR LIMIT_COUNT 1 REGEX "package.*;$")
-  else()
-    set(PACKAGE_STR "")
-  endif()
-  if(PACKAGE_STR)
-    list(GET PACKAGE_STR 0 package_name)
-    string(REGEX REPLACE "^package[ ]+" "" package_name "${package_name}")
-    string(REGEX REPLACE ";$" "" package_name "${package_name}")
-
-    string(REGEX REPLACE ".*\\+" "" java_file_name "${java_file_name}")
-    string(REPLACE "." "/" package_path "${package_name}")
-    set(output_name "${package_path}/${java_file_name}")
-  else()
-    string(REPLACE "+" "/" java_file_name "${java_file_name}")
-    set(output_name "org/opencv/${java_file_name}")
-  endif()
-  if(__configure)
-    configure_file("${java_file}" "${java_src_dir}/${output_name}" @ONLY)
-  elseif(NOT "${java_file}" MATCHES "${OpenCV_BINARY_DIR}/")
-    configure_file("${java_file}" "${java_src_dir}/${output_name}" COPYONLY)
-  else()
-    add_custom_command(OUTPUT "${java_src_dir}/${output_name}"
-                       COMMAND ${CMAKE_COMMAND} -E copy_if_different "${java_file}" "${java_src_dir}/${output_name}"
-                       MAIN_DEPENDENCY "${java_file}"
-                       DEPENDS "${java_file}"
-                       COMMENT "Generating src/${output_name}"
-    )
-  endif()
-  list(APPEND copied_files "${java_src_dir}/${output_name}")
-
-  if(ANDROID)
-    get_filename_component(install_subdir "${output_name}" PATH)
-    install(FILES "${java_src_dir}/${output_name}" DESTINATION "${JAVA_INSTALL_ROOT}/src/${install_subdir}" COMPONENT java)
-  endif()
-endforeach()
-
-if(ANDROID)
-  set(android_copied_files "")
-  set(android_step3_input_files "")
-  foreach(file ${handwrittren_lib_project_files_rel})
-    configure_file("${CMAKE_CURRENT_SOURCE_DIR}/android_lib/${file}" "${OpenCV_BINARY_DIR}/${file}" @ONLY)
-    list(APPEND android_copied_files "${OpenCV_BINARY_DIR}/${file}")
-    list(APPEND android_step3_input_files "${CMAKE_CURRENT_SOURCE_DIR}/android_lib/${file}")
-
-    if(NOT file MATCHES "jni/.+")
-      get_filename_component(install_subdir "${file}" PATH)
-      install(FILES "${OpenCV_BINARY_DIR}/${file}" DESTINATION "${JAVA_INSTALL_ROOT}/${install_subdir}" COMPONENT java)
-    endif()
-  endforeach()
-
-  # library project jni sources (nothing really depends on them so we will not add them to step3_input_files)
-  foreach(jni_file ${handwritten_cpp_sources} ${handwritten_h_sources} ${generated_cpp_sources})
-    get_filename_component(jni_file_name "${jni_file}" NAME)
-    add_custom_command(OUTPUT "${OpenCV_BINARY_DIR}/jni/${jni_file_name}"
-                       COMMAND ${CMAKE_COMMAND} -E copy_if_different "${jni_file}" "${OpenCV_BINARY_DIR}/jni/${jni_file_name}"
-                       DEPENDS "${jni_file}" ${java_hdr_deps}
-                       COMMENT "Generating jni/${jni_file_name}"
-                      )
-    list(APPEND android_copied_files "${OpenCV_BINARY_DIR}/jni/${jni_file_name}")
-  endforeach()
-endif(ANDROID)
-
-# step 3.5: generate Android library project
-if(ANDROID AND ANDROID_EXECUTABLE)
-  set(lib_target_files ${ANDROID_LIB_PROJECT_FILES})
-  ocv_list_add_prefix(lib_target_files "${OpenCV_BINARY_DIR}/")
-
-  android_get_compatible_target(lib_target_sdk_target ${ANDROID_NATIVE_API_LEVEL} ${ANDROID_SDK_TARGET} 14)
-  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/android_lib/${ANDROID_MANIFEST_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" @ONLY)
-
-  add_custom_command(OUTPUT ${lib_target_files} "${OpenCV_BINARY_DIR}/${ANDROID_MANIFEST_FILE}"
-                     COMMAND ${CMAKE_COMMAND} -E remove ${lib_target_files}
-                     COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" "${OpenCV_BINARY_DIR}/${ANDROID_MANIFEST_FILE}"
-                     COMMAND ${ANDROID_EXECUTABLE} --silent create lib-project --path \"${OpenCV_BINARY_DIR}\" --target \"${lib_target_sdk_target}\" --name OpenCV --package org.opencv 2>\"${CMAKE_CURRENT_BINARY_DIR}/create_lib_project.log\"
-                     COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" "${OpenCV_BINARY_DIR}/${ANDROID_MANIFEST_FILE}"
-                     MAIN_DEPENDENCY "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_MANIFEST_FILE}"
-                     DEPENDS ${android_step3_input_files} ${android_copied_files}
-                     COMMENT "Generating OpenCV Android library project. SDK target: ${lib_target_sdk_target}"
-                    )
-  list(APPEND copied_files ${lib_target_files} "${OpenCV_BINARY_DIR}/${ANDROID_MANIFEST_FILE}")
-  list(APPEND step3_input_files "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_MANIFEST_FILE}")
-
-  install(FILES "${OpenCV_BINARY_DIR}/${ANDROID_PROJECT_PROPERTIES_FILE}" DESTINATION ${JAVA_INSTALL_ROOT} COMPONENT java)
-  install(FILES "${OpenCV_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" DESTINATION ${JAVA_INSTALL_ROOT} COMPONENT java)
-
-  # creating empty 'gen' and 'res' folders
-  file(MAKE_DIRECTORY ${OpenCV_BINARY_DIR}/sdk/java/gen ${OpenCV_BINARY_DIR}/sdk/java/res )
-
-  install(DIRECTORY ${OpenCV_BINARY_DIR}/sdk/java/gen DESTINATION ${JAVA_INSTALL_ROOT}/ COMPONENT java)
-  install(DIRECTORY ${OpenCV_BINARY_DIR}/sdk/java/res DESTINATION ${JAVA_INSTALL_ROOT}/ COMPONENT java)
-endif(ANDROID AND ANDROID_EXECUTABLE)
-
-set(step3_depends ${step2_depends} ${step3_input_files} ${copied_files})
-
-file(MAKE_DIRECTORY "${OpenCV_BINARY_DIR}/bin")
-
-# step 4: build jar
-if(ANDROID)
-  set(JAR_FILE "${OpenCV_BINARY_DIR}/bin/classes.jar")
-  if(ANDROID_TOOLS_Pkg_Revision GREATER 13)
-    # build the library project
-    # normally we should do this after a native part, but for a library project we can build the java part first
-    add_custom_command(OUTPUT "${JAR_FILE}" "${JAR_FILE}.dephelper"
-                       COMMAND ${ANT_EXECUTABLE} -q -noinput -k debug -Djava.target=1.6 -Djava.source=1.6
-                       COMMAND ${CMAKE_COMMAND} -E touch "${JAR_FILE}.dephelper" # can not rely on classes.jar because different versions of SDK update timestamp at different times
-                       WORKING_DIRECTORY "${OpenCV_BINARY_DIR}"
-                       DEPENDS ${step3_depends}
-                       COMMENT "Building OpenCV Android library project"
-                      )
-  else()
-    # ditto
-    add_custom_command(OUTPUT "${JAR_FILE}" "${JAR_FILE}.dephelper"
-                       COMMAND ${CMAKE_COMMAND} -E touch "${JAR_FILE}"
-                       COMMAND ${CMAKE_COMMAND} -E touch "${JAR_FILE}.dephelper"
-                       WORKING_DIRECTORY "${OpenCV_BINARY_DIR}"
-                       DEPENDS ${step3_depends}
-                       COMMENT ""
-                      )
-  endif()
-else(ANDROID)
-  set(JAR_NAME opencv-${LIB_NAME_SUFIX}.jar)
-  set(JAR_FILE "${OpenCV_BINARY_DIR}/bin/${JAR_NAME}")
-  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/build.xml.in" "${OpenCV_BINARY_DIR}/build.xml" @ONLY)
-  list(APPEND step3_depends "${OpenCV_BINARY_DIR}/build.xml")
-
-  add_custom_command(OUTPUT "${JAR_FILE}" "${JAR_FILE}.dephelper"
-                     COMMAND ${ANT_EXECUTABLE} -q -noinput -k jar javadoc
-                     COMMAND ${CMAKE_COMMAND} -E touch "${JAR_FILE}.dephelper"
-                     WORKING_DIRECTORY "${OpenCV_BINARY_DIR}"
-                     DEPENDS ${step3_depends}
-                     COMMENT "Generating ${JAR_NAME}"
-                    )
-
-  install(FILES ${JAR_FILE} OPTIONAL DESTINATION ${OPENCV_JAR_INSTALL_PATH} COMPONENT java)
-endif(ANDROID)
-
-# step 5: build native part
-
-# workarounding lack of `__attribute__ ((visibility("default")))` in jni_md.h/JNIEXPORT
-string(REPLACE "-fvisibility=hidden" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
-if(MSVC)
-  ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4996)
-else()
-  ocv_warnings_disable(CMAKE_CXX_FLAGS -Wunused-const-variable -Wundef -Wdeprecated-declarations)
-endif()
-
-
-ocv_add_library(${the_module} SHARED ${handwritten_h_sources} ${handwritten_cpp_sources} ${generated_cpp_sources}
-                                ${copied_files}
-                               "${JAR_FILE}" "${JAR_FILE}.dephelper")
-
-if(BUILD_FAT_JAVA_LIB)
-  set(__deps ${OPENCV_MODULE_${the_module}_DEPS} ${OPENCV_MODULES_BUILD})
-  foreach(m ${OPENCV_MODULES_BUILD}) # filterout INTERNAL (like opencv_ts) and BINDINGS (like opencv_python) modules
-    ocv_assert(DEFINED OPENCV_MODULE_${m}_CLASS)
-    if(NOT OPENCV_MODULE_${m}_CLASS STREQUAL "PUBLIC")
-      list(REMOVE_ITEM __deps ${m})
-    endif()
-  endforeach()
-  ocv_list_unique(__deps)
-  set(__extradeps ${__deps})
-  ocv_list_filterout(__extradeps "^opencv_")
-  if(__extradeps)
-    list(REMOVE_ITEM __deps ${__extradeps})
-  endif()
-  if(APPLE)
-    foreach(_dep ${__deps})
-      ocv_target_link_libraries(${the_module} LINK_PRIVATE -Wl,-force_load "${_dep}")
-    endforeach()
-  elseif(((CMAKE_COMPILER_IS_GNUCXX OR UNIX) OR (OPENCV_FORCE_FAT_JAVA_LIB_LD_RULES)) AND (NOT OPENCV_SKIP_FAT_JAVA_LIB_LD_RULES))
-    ocv_target_link_libraries(${the_module} LINK_PRIVATE -Wl,-whole-archive ${__deps} -Wl,-no-whole-archive)
-  else()
-    ocv_target_link_libraries(${the_module} LINK_PRIVATE ${__deps})
-  endif()
-  ocv_target_link_libraries(${the_module} LINK_PRIVATE ${__extradeps} ${OPENCV_LINKER_LIBS})
-else()
-  ocv_target_link_libraries(${the_module} LINK_PRIVATE ${OPENCV_MODULE_${the_module}_DEPS} ${OPENCV_LINKER_LIBS})
-endif()
-
-# Additional target properties
-set_target_properties(${the_module} PROPERTIES
-    OUTPUT_NAME "${the_module}${LIB_NAME_SUFIX}"
-    ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH}
-    LIBRARY_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH}
-    RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}
-    DEFINE_SYMBOL CVAPI_EXPORTS
-    )
 
+add_subdirectory(jni)  # generates ${the_module} target (${the_module}_jni doesn't work properly with Android samples)
 if(ANDROID)
-  ocv_target_link_libraries(${the_module} LINK_PUBLIC jnigraphics) # for Mat <=> Bitmap converters
-  ocv_target_link_libraries(${the_module} LINK_PUBLIC log dl z)
-  ocv_target_link_libraries(${the_module} LINK_PRIVATE ${OPENCV_LINKER_LIBS})
-
-  # force strip library after the build command
-  # because samples and tests will make a copy of the library before install
-  # Turn off stripping in debug build
-  if ( NOT (CMAKE_BUILD_TYPE MATCHES "Debug"))
-    add_custom_command(TARGET ${the_module} POST_BUILD COMMAND ${CMAKE_STRIP} --strip-unneeded "$<TARGET_FILE:${the_module}>")
-  endif()
-endif()
-
-if(WIN32)
-  set_target_properties(${the_module} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH})
-endif()
-
-if(ENABLE_SOLUTION_FOLDERS)
-  set_target_properties(${the_module} PROPERTIES FOLDER "bindings")
-endif()
-
-if(BUILD_FAT_JAVA_LIB)
-  ocv_install_target(${the_module} OPTIONAL EXPORT OpenCVModules
-          RUNTIME DESTINATION ${OPENCV_BIN_INSTALL_PATH} COMPONENT java
-          LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT java
-          ARCHIVE DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT java)
+  add_subdirectory(android_sdk)  # generates ${the_module}_android target
 else()
-  if(NOT INSTALL_CREATE_DISTRIB)
-    ocv_install_target(${the_module} OPTIONAL
-            RUNTIME DESTINATION ${OPENCV_JAR_INSTALL_PATH} COMPONENT java
-            LIBRARY DESTINATION ${OPENCV_JAR_INSTALL_PATH} COMPONENT java)
-  else()
-    ocv_install_target(${the_module} OPTIONAL
-            RUNTIME DESTINATION ${OPENCV_JAR_INSTALL_PATH}/${OpenCV_ARCH} COMPONENT java
-            LIBRARY DESTINATION ${OPENCV_JAR_INSTALL_PATH}/${OpenCV_ARCH} COMPONENT java)
-  endif()
+  add_subdirectory(jar)  # generates ${the_module}_jar target
 endif()
 
-######################################################################################################################################
-
 if(BUILD_TESTS)
   if(ANDROID)
-    add_subdirectory(android_test)
+    add_subdirectory(test/android_test)
   else()
-    add_subdirectory(pure_test)
+    add_subdirectory(test/pure_test)
   endif()
 endif()
diff --git a/modules/java/android_sdk/CMakeLists.txt b/modules/java/android_sdk/CMakeLists.txt
new file mode 100644
index 0000000000..f8cada1e49
--- /dev/null
+++ b/modules/java/android_sdk/CMakeLists.txt
@@ -0,0 +1,88 @@
+if(NOT ANDROID_EXECUTABLE)
+  message(WARNING "'android' tool required to build Android SDK")
+  return()
+endif()
+ocv_assert(ANDROID_TOOLS_Pkg_Revision GREATER 13)
+
+project(${the_module}_android)
+
+set(OPENCV_JAVA_DIR "${OpenCV_BINARY_DIR}/android_sdk" CACHE INTERNAL "")
+set(OPENCV_ANDROID_LIB_DIR "${OPENCV_JAVA_DIR}" CACHE INTERNAL "")  # for OpenCV samples
+
+file(REMOVE_RECURSE "${OPENCV_JAVA_DIR}")
+file(MAKE_DIRECTORY "${OPENCV_JAVA_DIR}/bin")
+set(java_src_dir "${OPENCV_JAVA_DIR}/src")
+file(MAKE_DIRECTORY "${java_src_dir}")
+
+ocv_copyfiles_append_dir(JAVA_SRC_COPY "${OPENCV_JAVA_BINDINGS_DIR}/gen/java" "${java_src_dir}")
+ocv_copyfiles_append_dir(JAVA_SRC_COPY "${OPENCV_JAVA_BINDINGS_DIR}/gen/android/java" "${java_src_dir}")
+
+# calc default SDK Target
+android_get_compatible_target(android_sdk_target ${ANDROID_NATIVE_API_LEVEL} ${ANDROID_SDK_TARGET} 14)
+string(REGEX REPLACE "android-" "" android_sdk_target_num ${android_sdk_target})
+if( (ANDROID_SDK_TARGET AND ANDROID_SDK_TARGET LESS 21) OR (android_sdk_target_num LESS 21) )
+  message(STATUS "[OpenCV for Android SDK]: A new OpenGL Camera Bridge (CameraGLSurfaceView, CameraGLRendererBase, CameraRenderer, Camera2Renderer) is disabled, because ANDROID_SDK_TARGET (${android_sdk_target_num}) < 21")
+else()
+  ocv_copyfiles_append_dir(JAVA_SRC_COPY "${OPENCV_JAVA_BINDINGS_DIR}/gen/android-21/java" "${java_src_dir}")
+endif()
+
+# copy boilerplate
+file(GLOB_RECURSE seed_project_files_rel RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/android_lib/" "${CMAKE_CURRENT_SOURCE_DIR}/android_lib/*")
+list(REMOVE_ITEM seed_project_files_rel "${ANDROID_MANIFEST_FILE}")
+foreach(file ${seed_project_files_rel})
+  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/android_lib/${file}" "${OPENCV_JAVA_DIR}/${file}" @ONLY)
+  list(APPEND depends "${CMAKE_CURRENT_SOURCE_DIR}/android_lib/${file}")
+  get_filename_component(install_subdir "${file}" PATH)
+  install(FILES "${OPENCV_JAVA_DIR}/${file}" DESTINATION "${JAVA_INSTALL_ROOT}/${install_subdir}" COMPONENT java)
+endforeach()
+
+list(APPEND depends gen_opencv_java_source "${OPENCV_DEPHELPER}/gen_opencv_java_source")
+ocv_copyfiles_add_target(${the_module}_android_source_copy JAVA_SRC_COPY "Copy Java(Andoid SDK) source files" ${depends})
+file(REMOVE "${OPENCV_DEPHELPER}/${the_module}_android_source_copy")  # force rebuild after CMake run
+
+set(depends ${the_module}_android_source_copy "${OPENCV_DEPHELPER}/${the_module}_android_source_copy")
+
+# generate Android library project
+set(android_sdk_project_files ${ANDROID_LIB_PROJECT_FILES})  # build.xml;local.properties;proguard-project.txt;project.properties
+ocv_list_add_prefix(android_sdk_project_files "${OPENCV_JAVA_DIR}/")
+
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/android_lib/${ANDROID_MANIFEST_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" @ONLY)
+
+add_custom_command(
+    OUTPUT ${android_sdk_project_files} "${OPENCV_JAVA_DIR}/${ANDROID_MANIFEST_FILE}"
+    COMMAND ${CMAKE_COMMAND} -E remove ${android_sdk_project_files}
+    COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" "${OPENCV_JAVA_DIR}/${ANDROID_MANIFEST_FILE}"
+    COMMAND ${ANDROID_EXECUTABLE} --silent create lib-project --path \"${OPENCV_JAVA_DIR}\" --target \"${android_sdk_target}\" --name OpenCV --package org.opencv 2>\"${CMAKE_CURRENT_BINARY_DIR}/create_lib_project.log\"
+    COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_MANIFEST_FILE}" "${OPENCV_JAVA_DIR}/${ANDROID_MANIFEST_FILE}"
+    WORKING_DIRECTORY "${OPENCV_JAVA_DIR}"
+    MAIN_DEPENDENCY "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_MANIFEST_FILE}"
+    DEPENDS ${depends}
+    COMMENT "Generating OpenCV Android library project. SDK target: ${lib_target_sdk_target}"
+)
+list(APPEND depends ${android_sdk_project_files} "${OPENCV_JAVA_DIR}/${ANDROID_MANIFEST_FILE}")
+
+install(DIRECTORY "${OPENCV_JAVA_DIR}/src" DESTINATION "${JAVA_INSTALL_ROOT}" COMPONENT java)
+install(FILES "${OPENCV_JAVA_DIR}/${ANDROID_PROJECT_PROPERTIES_FILE}" DESTINATION ${JAVA_INSTALL_ROOT} COMPONENT java)
+install(FILES "${OPENCV_JAVA_DIR}/${ANDROID_MANIFEST_FILE}" DESTINATION ${JAVA_INSTALL_ROOT} COMPONENT java)
+
+# build jar
+set(JAR_FILE "${OpenCV_BINARY_DIR}/bin/classes.jar")
+# build the library project
+# normally we should do this after a native part, but for a library project we can build the java part first
+add_custom_command(
+    OUTPUT "${JAR_FILE}" "${OPENCV_DEPHELPER}/${the_module}_android"
+    COMMAND ${ANT_EXECUTABLE} -q -noinput -k debug -Djava.target=1.6 -Djava.source=1.6
+    COMMAND ${CMAKE_COMMAND} -E touch "${OPENCV_DEPHELPER}/${the_module}_android"
+    WORKING_DIRECTORY "${OPENCV_JAVA_DIR}"
+    DEPENDS ${depends}
+    COMMENT "Building OpenCV Android library project"
+)
+
+add_custom_target(${the_module}_android DEPENDS "${OPENCV_DEPHELPER}/${the_module}_android" SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/android_lib/${ANDROID_MANIFEST_FILE}")
+add_dependencies(${the_module} ${the_module}_android)
+
+# empty 'gen' and 'res' folders
+install(CODE "
+FILE(MAKE_DIRECTORY \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${JAVA_INSTALL_ROOT}/gen\")
+FILE(MAKE_DIRECTORY \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${JAVA_INSTALL_ROOT}/res\")
+" COMPONENT java)
diff --git a/modules/java/android_sdk/android_lib/jni/Android.mk b/modules/java/android_sdk/android_lib/jni/Android.mk
deleted file mode 100644
index 129f567a6d..0000000000
--- a/modules/java/android_sdk/android_lib/jni/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-include OpenCV.mk
-
-LOCAL_MODULE    := opencv_java
-
-MY_PREFIX := $(LOCAL_PATH)
-MY_SOURCES := $(wildcard $(MY_PREFIX)/*.cpp)
-LOCAL_SRC_FILES := $(MY_SOURCES:$(MY_PREFIX)%=%)
-
-LOCAL_LDLIBS +=  -llog -ldl -ljnigraphics
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/modules/java/android_sdk/android_lib/jni/Application.mk b/modules/java/android_sdk/android_lib/jni/Application.mk
deleted file mode 100644
index c7de9c761b..0000000000
--- a/modules/java/android_sdk/android_lib/jni/Application.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-APP_STL := gnustl_static
-APP_CPPFLAGS := -frtti -fexceptions
-APP_ABI := armeabi-v7a
diff --git a/modules/java/common.cmake b/modules/java/common.cmake
new file mode 100644
index 0000000000..df9c2de77b
--- /dev/null
+++ b/modules/java/common.cmake
@@ -0,0 +1,16 @@
+if(ANDROID)
+  ocv_update(OPENCV_JAVA_LIB_NAME_SUFFIX "${OPENCV_VERSION_MAJOR}")
+  ocv_update(JAVA_INSTALL_ROOT "sdk/java")
+else()
+  ocv_update(OPENCV_JAVA_LIB_NAME_SUFFIX "${OPENCV_VERSION_MAJOR}${OPENCV_VERSION_MINOR}${OPENCV_VERSION_PATCH}")
+endif()
+
+# get list of modules to wrap
+# message(STATUS "Wrapped in java:")
+set(OPENCV_JAVA_MODULES)
+foreach(m ${OPENCV_MODULES_BUILD})
+  if (";${OPENCV_MODULE_${m}_WRAPPERS};" MATCHES ";java;" AND HAVE_${m})
+    list(APPEND OPENCV_JAVA_MODULES ${m})
+    #message(STATUS "\t${m}")
+  endif()
+endforeach()
diff --git a/modules/java/generator/CMakeLists.txt b/modules/java/generator/CMakeLists.txt
new file mode 100644
index 0000000000..930c0cae30
--- /dev/null
+++ b/modules/java/generator/CMakeLists.txt
@@ -0,0 +1,103 @@
+set(MODULE_NAME "java_bindings_generator")
+set(OPENCV_MODULE_IS_PART_OF_WORLD FALSE)
+ocv_add_module(${MODULE_NAME} INTERNAL)
+
+set(OPENCV_JAVA_SIGNATURES_FILE "${CMAKE_CURRENT_BINARY_DIR}/opencv_java_signatures.json" CACHE INTERNAL "")
+set(OPENCV_JAVA_BINDINGS_DIR "${CMAKE_CURRENT_BINARY_DIR}" CACHE INTERNAL "")
+
+file(REMOVE_RECURSE "${OPENCV_JAVA_BINDINGS_DIR}/gen")
+file(REMOVE "${OPENCV_DEPHELPER}/gen_opencv_java_source")  # force re-run after CMake
+
+# This file is included from a subdirectory
+set(JAVA_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..")
+include(${JAVA_SOURCE_DIR}/common.cmake)
+
+set(__remap_config "") # list of remapped ".in" files (configure_file)
+set(__remap_targets "")
+
+macro(ocv_remap_files files_list_var)
+  set(target_dir "${OpenCV_BINARY_DIR}/configured")
+  foreach(f ${${files_list_var}})
+    if(NOT "${f}" MATCHES "^(.*)\\.in$")
+      continue()
+    endif()
+    set(f_ "${CMAKE_MATCH_1}")
+    file(RELATIVE_PATH rel_path0 "${OpenCV_SOURCE_DIR}" "${f}")
+    file(RELATIVE_PATH rel_path1 "${OpenCV_SOURCE_DIR}" "${f_}")
+    set(__target_file "${target_dir}/${rel_path1}")
+    configure_file("${f}" "${__target_file}" @ONLY)
+    if(__remap_config)
+      set(__remap_config "${__remap_config},\n")
+    endif()
+    set(__remap_config "${__remap_config}    { \"src\": \"${rel_path0}\", \"target\": \"${__target_file}\" }")
+    list(APPEND __remap_targets "${__target_file}")
+  endforeach()
+endmacro()
+
+# common files
+file(GLOB_RECURSE deps "${CMAKE_CURRENT_SOURCE_DIR}/src/*" "${CMAKE_CURRENT_SOURCE_DIR}/android*/*" "${CMAKE_CURRENT_SOURCE_DIR}/templates/*")
+ocv_remap_files(deps)
+
+set(__modules_config "") # list of OpenCV modules
+foreach(m ${OPENCV_JAVA_MODULES})
+  set(module_java_dir "${OPENCV_MODULE_${m}_LOCATION}/misc/java")
+  list(APPEND deps ${OPENCV_MODULE_${m}_HEADERS})
+  file(GLOB_RECURSE misc_files "${module_java_dir}/*")
+  list(APPEND deps ${misc_files})
+
+  string(REGEX REPLACE "^opencv_" "" m_ "${m}")
+  if(__modules_config)
+    set(__modules_config "${__modules_config},\n")
+  endif()
+  file(RELATIVE_PATH rel_path "${OpenCV_SOURCE_DIR}" "${OPENCV_MODULE_${m}_LOCATION}")
+  set(__modules_config "${__modules_config}    { \"name\": \"${m_}\", \"location\": \"${rel_path}\" }")
+
+  ocv_remap_files(misc_files)
+endforeach(m)
+
+set(CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/gen_java.json")
+set(__config_str "\
+{
+  \"rootdir\": \"${OpenCV_SOURCE_DIR}\",
+  \"modules\": [
+${__modules_config}
+  ],
+  \"files_remap\": [
+${__remap_config}
+  ]
+}
+")
+if(EXISTS "${CONFIG_FILE}")
+  file(READ "${CONFIG_FILE}" __content)
+else()
+  set(__content "")
+endif()
+if(NOT "${__content}" STREQUAL "${__config_str}")
+  file(WRITE "${CONFIG_FILE}" "${__config_str}")
+  file(REMOVE "${OPENCV_DEPHELPER}/gen_opencv_java_source")
+endif()
+unset(__config_str)
+
+set(java_generated_files
+    # "${OPENCV_JAVA_SIGNATURES_FILE}"
+    "${OPENCV_DEPHELPER}/gen_opencv_java_source"
+)
+
+add_custom_command(
+    OUTPUT ${java_generated_files}
+    COMMAND ${PYTHON_DEFAULT_EXECUTABLE} "${JAVA_SOURCE_DIR}/generator/gen_java.py" -p "${JAVA_SOURCE_DIR}/../python/src2/gen2.py" -c "${CONFIG_FILE}"
+    COMMAND ${CMAKE_COMMAND} -E touch "${OPENCV_DEPHELPER}/gen_opencv_java_source"
+    WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+    DEPENDS "${JAVA_SOURCE_DIR}/generator/gen_java.py"
+            "${JAVA_SOURCE_DIR}/../python/src2/gen2.py"
+            "${JAVA_SOURCE_DIR}/../python/src2/hdr_parser.py"
+            # don't, result of file(WRITE): "${CMAKE_CURRENT_BINARY_DIR}/gen_java.json"
+            ${deps} ${__remap_targets}
+            # not allowed (file(WRITE) result): "${CONFIG_FILE}"
+    COMMENT "Generate files for Java bindings"
+)
+
+add_custom_target(gen_opencv_java_source DEPENDS ${java_generated_files}
+    SOURCES "${JAVA_SOURCE_DIR}/generator/gen_java.py"
+            "${CMAKE_CURRENT_BINARY_DIR}/gen_java.json"
+)
diff --git a/modules/java/generator/gen_java.py b/modules/java/generator/gen_java.py
index ea36496df8..536ac43fbd 100755
--- a/modules/java/generator/gen_java.py
+++ b/modules/java/generator/gen_java.py
@@ -1,8 +1,9 @@
 #!/usr/bin/env python
 
-import sys, re, os.path
+import sys, re, os.path, errno, fnmatch
 import json
 import logging
+from shutil import copyfile
 from pprint import pformat
 from string import Template
 
@@ -11,6 +12,26 @@ if sys.version_info[0] >= 3:
 else:
     from cStringIO import StringIO
 
+SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+
+# list of modules + files remap
+config = None
+ROOT_DIR = None
+FILES_REMAP = {}
+def checkFileRemap(path):
+    path = os.path.realpath(path)
+    if path in FILES_REMAP:
+        return FILES_REMAP[path]
+    assert path[-3:] != '.in', path
+    return path
+
+total_files = 0
+updated_files = 0
+
+module_imports = []
+module_j_code = None
+module_jn_code = None
+
 # list of class names, which should be skipped by wrapper generator
 # the list is loaded from misc/java/gen_dict.json defined for the module and its dependencies
 class_ignore_list = []
@@ -52,136 +73,25 @@ ManualFuncs = {}
 # { class : { func : { arg_name : {"ctype" : ctype, "attrib" : [attrib]} } } }
 func_arg_fix = {}
 
-def getLibVersion(version_hpp_path):
-    version_file = open(version_hpp_path, "rt").read()
-    major = re.search("^W*#\W*define\W+CV_VERSION_MAJOR\W+(\d+)\W*$", version_file, re.MULTILINE).group(1)
-    minor = re.search("^W*#\W*define\W+CV_VERSION_MINOR\W+(\d+)\W*$", version_file, re.MULTILINE).group(1)
-    revision = re.search("^W*#\W*define\W+CV_VERSION_REVISION\W+(\d+)\W*$", version_file, re.MULTILINE).group(1)
-    status = re.search("^W*#\W*define\W+CV_VERSION_STATUS\W+\"(.*?)\"\W*$", version_file, re.MULTILINE).group(1)
-    return (major, minor, revision, status)
-
-def libVersionBlock():
-    (major, minor, revision, status) = getLibVersion(
-    (os.path.dirname(__file__) or '.') + '/../../core/include/opencv2/core/version.hpp')
-    version_str    = '.'.join( (major, minor, revision) ) + status
-    version_suffix =  ''.join( (major, minor, revision) )
-    return """
-    // these constants are wrapped inside functions to prevent inlining
-    private static String getVersion() { return "%(v)s"; }
-    private static String getNativeLibraryName() { return "opencv_java%(vs)s"; }
-    private static int getVersionMajor() { return %(ma)s; }
-    private static int getVersionMinor() { return %(mi)s; }
-    private static int getVersionRevision() { return %(re)s; }
-    private static String getVersionStatus() { return "%(st)s"; }
-
-    public static final String VERSION = getVersion();
-    public static final String NATIVE_LIBRARY_NAME = getNativeLibraryName();
-    public static final int VERSION_MAJOR = getVersionMajor();
-    public static final int VERSION_MINOR = getVersionMinor();
-    public static final int VERSION_REVISION = getVersionRevision();
-    public static final String VERSION_STATUS = getVersionStatus();
-""" % { 'v' : version_str, 'vs' : version_suffix, 'ma' : major, 'mi' : minor, 're' : revision, 'st': status }
-
-
-T_JAVA_START_INHERITED = """
-//
-// This file is auto-generated. Please don't modify it!
-//
-package org.opencv.$module;
-
-$imports
-
-$docs
-$annotation
-public class $jname extends $base {
-
-    protected $jname(long addr) { super(addr); }
-
-"""
-
-T_JAVA_START_ORPHAN = """
-//
-// This file is auto-generated. Please don't modify it!
-//
-package org.opencv.$module;
-
-$imports
-
-$docs
-$annotation
-public class $jname {
-
-    protected final long nativeObj;
-    protected $jname(long addr) { nativeObj = addr; }
-
-    public long getNativeObjAddr() { return nativeObj; }
-"""
-
-T_JAVA_START_MODULE = """
-//
-// This file is auto-generated. Please don't modify it!
-//
-package org.opencv.$module;
-
-$imports
-
-$docs
-$annotation
-public class $jname {
-"""
-
-T_CPP_MODULE = """
-//
-// This file is auto-generated, please don't edit!
-//
-
-#define LOG_TAG "org.opencv.$m"
-
-#include "common.h"
-
-#include "opencv2/opencv_modules.hpp"
-#ifdef HAVE_OPENCV_$M
-
-#include <string>
-
-#include "opencv2/$m.hpp"
-
-$includes
-
-using namespace cv;
-
-/// throw java exception
-static void throwJavaException(JNIEnv *env, const std::exception *e, const char *method) {
-  std::string what = "unknown exception";
-  jclass je = 0;
-
-  if(e) {
-    std::string exception_type = "std::exception";
-
-    if(dynamic_cast<const cv::Exception*>(e)) {
-      exception_type = "cv::Exception";
-      je = env->FindClass("org/opencv/core/CvException");
-    }
-
-    what = exception_type + ": " + e->what();
-  }
-
-  if(!je) je = env->FindClass("java/lang/Exception");
-  env->ThrowNew(je, what.c_str());
-
-  LOGE("%s caught %s", method, what.c_str());
-  (void)method;        // avoid "unused" warning
-}
-
-
-extern "C" {
-
-$code
-
-} // extern "C"
+def read_contents(fname):
+    with open(fname, 'r') as f:
+        data = f.read()
+    return data
+
+def mkdir_p(path):
+    ''' mkdir -p '''
+    try:
+        os.makedirs(path)
+    except OSError as exc:
+        if exc.errno == errno.EEXIST and os.path.isdir(path):
+            pass
+        else:
+            raise
 
-#endif // HAVE_OPENCV_$M
-"""
+T_JAVA_START_INHERITED = read_contents(os.path.join(SCRIPT_DIR, 'templates/java_class_inherited.prolog'))
+T_JAVA_START_ORPHAN = read_contents(os.path.join(SCRIPT_DIR, 'templates/java_class.prolog'))
+T_JAVA_START_MODULE = read_contents(os.path.join(SCRIPT_DIR, 'templates/java_module.prolog'))
+T_CPP_MODULE = Template(read_contents(os.path.join(SCRIPT_DIR, 'templates/cpp_module.template')))
 
 class GeneralInfo():
     def __init__(self, type, decl, namespaces):
@@ -195,7 +105,7 @@ class GeneralInfo():
         else:
             docstring=""
         if len(decl)>5 and decl[5]:
-            logging.info('docstring: %s', decl[5])
+            #logging.info('docstring: %s', decl[5])
             if re.search("(@|\\\\)deprecated", decl[5]):
                 self.annotation.append("@Deprecated")
 
@@ -335,9 +245,13 @@ class ClassInfo(GeneralInfo):
             else:
                 self.j_code.write(T_JAVA_START_MODULE)
         # misc handling
-        if self.name == 'Core':
-            self.imports.add("java.lang.String")
-            self.j_code.write(libVersionBlock())
+        if self.name == Module:
+          for i in module_imports or []:
+              self.imports.add(i)
+          if module_j_code:
+              self.j_code.write(module_j_code)
+          if module_jn_code:
+              self.jn_code.write(module_jn_code)
 
     def cleanupCodeStreams(self):
         self.j_code.close()
@@ -411,6 +325,7 @@ class FuncInfo(GeneralInfo):
 
 class JavaWrapperGenerator(object):
     def __init__(self):
+        self.cpp_files = []
         self.clear()
 
     def clear(self):
@@ -506,11 +421,18 @@ class JavaWrapperGenerator(object):
             self.def_args_hist[cnt] = self.def_args_hist.get(cnt, 0) + 1
 
     def save(self, path, buf):
-        f = open(path, "wt")
-        f.write(buf)
-        f.close()
-
-    def gen(self, srcfiles, module, output_path, common_headers):
+        global total_files, updated_files
+        total_files += 1
+        if os.path.exists(path):
+            with open(path, "rt") as f:
+                content = f.read()
+                if content == buf:
+                    return
+        with open(path, "wt") as f:
+            f.write(buf)
+        updated_files += 1
+
+    def gen(self, srcfiles, module, output_path, output_jni_path, output_java_path, common_headers):
         self.clear()
         self.module = module
         self.Module = module.capitalize()
@@ -534,7 +456,7 @@ class JavaWrapperGenerator(object):
             else:
                 logging.info("Ignore header: %s", hdr)
             for decl in decls:
-                logging.info("\n--- Incoming ---\n%s", pformat(decl, 4))
+                logging.info("\n--- Incoming ---\n%s", pformat(decl[:5], 4)) # without docstring
                 name = decl[0]
                 if name.startswith("struct") or name.startswith("class"):
                     self.add_class(decl)
@@ -545,17 +467,21 @@ class JavaWrapperGenerator(object):
 
         logging.info("\n\n===== Generating... =====")
         moduleCppCode = StringIO()
+        package_path = os.path.join(output_java_path, module)
+        mkdir_p(package_path)
         for ci in self.classes.values():
             if ci.name == "Mat":
                 continue
             ci.initCodeStreams(self.Module)
             self.gen_class(ci)
             classJavaCode = ci.generateJavaCode(self.module, self.Module)
-            self.save("%s/%s+%s.java" % (output_path, module, ci.jname), classJavaCode)
+            self.save("%s/%s/%s.java" % (output_java_path, module, ci.jname), classJavaCode)
             moduleCppCode.write(ci.generateCppCode())
             ci.cleanupCodeStreams()
-        self.save(output_path+"/"+module+".cpp", Template(T_CPP_MODULE).substitute(m = module, M = module.upper(), code = moduleCppCode.getvalue(), includes = "\n".join(includes)))
-        self.save(output_path+"/"+module+".txt", self.makeReport())
+        cpp_file = os.path.abspath(os.path.join(output_jni_path, module + ".inl.hpp"))
+        self.cpp_files.append(cpp_file)
+        self.save(cpp_file, T_CPP_MODULE.substitute(m = module, M = module.upper(), code = moduleCppCode.getvalue(), includes = "\n".join(includes)))
+        self.save(os.path.join(output_path, module+".txt"), self.makeReport())
 
     def makeReport(self):
         '''
@@ -734,7 +660,7 @@ class JavaWrapperGenerator(object):
 
             if(j_signature in j_signatures):
                 if args:
-                    pop(args)
+                    args.pop()
                     continue
                 else:
                     break
@@ -1078,17 +1004,55 @@ JNIEXPORT void JNICALL Java_org_opencv_%(module)s_%(j_cls)s_delete
             return "Ptr<" + fullname + ">"
         return fullname
 
+    def finalize(self, output_jni_path):
+        list_file = os.path.join(output_jni_path, "opencv_jni.hpp")
+        self.save(list_file, '\n'.join(['#include "%s"' % f for f in self.cpp_files]))
+
+
+def copy_java_files(java_files_dir, java_base_path):
+    global total_files, updated_files
+    java_files = []
+    re_filter = re.compile(r'^.+\.(java|aidl)(.in)?$')
+    for root, dirnames, filenames in os.walk(java_files_dir):
+       java_files += [os.path.join(root, filename) for filename in filenames if re_filter.match(filename)]
+    java_files = [f.replace('\\', '/') for f in java_files]
+
+    re_package = re.compile(r'^package +(.+);$')
+    re_prefix = re.compile(r'^.+[\+/]([^\+]+).(java|aidl)(.in)?$')
+    for java_file in java_files:
+        src = checkFileRemap(java_file)
+        with open(src, 'r') as f:
+            package_line = f.readline()
+        m = re_prefix.match(java_file)
+        target_fname = (m.group(1) + '.' + m.group(2)) if m else os.path.basename(java_file)
+        m = re_package.match(package_line)
+        if m:
+            package = m.group(1)
+            package_path = package.replace('.', '/')
+        else:
+            package_path = 'org/opencv/' + module
+        #print(java_file, package_path, target_fname)
+        dest = os.path.join(java_base_path, os.path.join(package_path, target_fname))
+        assert dest[-3:] != '.in', dest + ' | ' + target_fname
+        mkdir_p(os.path.dirname(dest))
+        total_files += 1
+        if (not os.path.exists(dest)) or (os.stat(src).st_mtime - os.stat(dest).st_mtime > 1):
+            copyfile(src, dest)
+            updated_files += 1
+
 
 if __name__ == "__main__":
+    # initialize logger
+    logging.basicConfig(filename='gen_java.log', format=None, filemode='w', level=logging.INFO)
+    handler = logging.StreamHandler()
+    handler.setLevel(logging.WARNING)
+    logging.getLogger().addHandler(handler)
 
     # parse command line parameters
     import argparse
     arg_parser = argparse.ArgumentParser(description='OpenCV Java Wrapper Generator')
     arg_parser.add_argument('-p', '--parser', required=True, help='OpenCV header parser')
-    arg_parser.add_argument('-m', '--module', required=True, help='OpenCV module name')
-    arg_parser.add_argument('-s', '--srcfiles', required=True, nargs='+', help='Source headers to be wrapped')
-    arg_parser.add_argument('-c', '--common', nargs='*', help='Common headers')
-    arg_parser.add_argument('-t', '--gendict', nargs='*', help='Custom module dictionaries for C++ to Java conversion')
+    arg_parser.add_argument('-c', '--config', required=True, help='OpenCV modules config')
 
     args=arg_parser.parse_args()
 
@@ -1099,38 +1063,95 @@ if __name__ == "__main__":
     sys.path.append(hdr_parser_path)
     import hdr_parser
 
-    module = args.module
-    srcfiles = args.srcfiles
-    common_headers= args.common
-    gen_dict_files = args.gendict
+    with open(args.config) as f:
+        config = json.load(f)
 
-    dstdir = "."
+    ROOT_DIR = config['rootdir']; assert os.path.exists(ROOT_DIR)
+    FILES_REMAP = { os.path.realpath(os.path.join(ROOT_DIR, f['src'])): f['target'] for f in config['files_remap'] }
+    logging.info("\nRemapped configured files (%d):\n%s", len(FILES_REMAP), pformat(FILES_REMAP))
 
-    # initialize logger
-    logging.basicConfig(filename='%s/%s.log' % (dstdir, module), format=None, filemode='w', level=logging.INFO)
-    handler = logging.StreamHandler()
-    handler.setLevel(logging.WARNING)
-    logging.getLogger().addHandler(handler)
+    dstdir = "./gen"
+    jni_path = os.path.join(dstdir, 'cpp'); mkdir_p(jni_path)
+    java_base_path = os.path.join(dstdir, 'java'); mkdir_p(java_base_path)
+    java_test_base_path = os.path.join(dstdir, 'test'); mkdir_p(java_test_base_path)
 
-    # load dictionaries
-    for gdf in gen_dict_files:
-        with open(gdf) as f:
-            gen_type_dict = json.load(f)
-            if "class_ignore_list" in gen_type_dict:
-                class_ignore_list += gen_type_dict["class_ignore_list"]
-            if "const_ignore_list" in gen_type_dict:
-                const_ignore_list += gen_type_dict["const_ignore_list"]
-            if "const_private_list" in gen_type_dict:
-                const_private_list += gen_type_dict["const_private_list"]
-            if "missing_consts" in gen_type_dict:
-                missing_consts.update(gen_type_dict["missing_consts"])
-            if "type_dict" in gen_type_dict:
-                type_dict.update(gen_type_dict["type_dict"])
-            if "ManualFuncs" in gen_type_dict:
-                ManualFuncs.update(gen_type_dict["ManualFuncs"])
-            if "func_arg_fix" in gen_type_dict:
-                func_arg_fix.update(gen_type_dict["func_arg_fix"])
+    for (subdir, target_subdir) in [('src/java', 'java'), ('android/java', None), ('android-21/java', None)]:
+        if target_subdir is None:
+            target_subdir = subdir
+        java_files_dir = os.path.join(SCRIPT_DIR, subdir)
+        if os.path.exists(java_files_dir):
+            target_path = os.path.join(dstdir, target_subdir); mkdir_p(target_path)
+            copy_java_files(java_files_dir, target_path)
 
     # launch Java Wrapper generator
     generator = JavaWrapperGenerator()
-    generator.gen(srcfiles, module, dstdir, common_headers)
+
+    gen_dict_files = []
+
+    print("JAVA: Processing OpenCV modules: %d" % len(config['modules']))
+    for e in config['modules']:
+        (module, module_location) = (e['name'], os.path.join(ROOT_DIR, e['location']))
+        logging.info("\n=== MODULE: %s (%s) ===\n" % (module, module_location))
+
+        java_path = os.path.join(java_base_path, 'org/opencv')
+        mkdir_p(java_path)
+
+        module_imports = []
+        module_j_code = None
+        module_jn_code = None
+        srcfiles = []
+        common_headers = []
+
+        misc_location = os.path.join(module_location, 'misc/java')
+
+        srcfiles_fname = os.path.join(misc_location, 'filelist')
+        if os.path.exists(srcfiles_fname):
+            with open(srcfiles_fname) as f:
+                srcfiles = [os.path.join(module_location, str(l).strip()) for l in f.readlines() if str(l).strip()]
+        else:
+            re_bad = re.compile(r'(private|.inl.hpp$|_inl.hpp$|.details.hpp$|_winrt.hpp$|/cuda/)')
+            # .h files before .hpp
+            h_files = []
+            hpp_files = []
+            for root, dirnames, filenames in os.walk(os.path.join(module_location, 'include')):
+               h_files += [os.path.join(root, filename) for filename in fnmatch.filter(filenames, '*.h')]
+               hpp_files += [os.path.join(root, filename) for filename in fnmatch.filter(filenames, '*.hpp')]
+            srcfiles = h_files + hpp_files
+            srcfiles = [f for f in srcfiles if not re_bad.search(f.replace('\\', '/'))]
+        logging.info("\nFiles (%d):\n%s", len(srcfiles), pformat(srcfiles))
+
+        common_headers_fname = os.path.join(misc_location, 'filelist_common')
+        if os.path.exists(common_headers_fname):
+            with open(common_headers_fname) as f:
+                common_headers = [os.path.join(module_location, str(l).strip()) for l in f.readlines() if str(l).strip()]
+        logging.info("\nCommon headers (%d):\n%s", len(common_headers), pformat(common_headers))
+
+        gendict_fname = os.path.join(misc_location, 'gen_dict.json')
+        if os.path.exists(gendict_fname):
+            with open(gendict_fname) as f:
+                gen_type_dict = json.load(f)
+            class_ignore_list += gen_type_dict.get("class_ignore_list", [])
+            const_ignore_list += gen_type_dict.get("const_ignore_list", [])
+            const_private_list += gen_type_dict.get("const_private_list", [])
+            missing_consts.update(gen_type_dict.get("missing_consts", {}))
+            type_dict.update(gen_type_dict.get("type_dict", {}))
+            ManualFuncs.update(gen_type_dict.get("ManualFuncs", {}))
+            func_arg_fix.update(gen_type_dict.get("func_arg_fix", {}))
+            if 'module_j_code' in gen_type_dict:
+                module_j_code = read_contents(checkFileRemap(os.path.join(misc_location, gen_type_dict['module_j_code'])))
+            if 'module_jn_code' in gen_type_dict:
+                module_jn_code = read_contents(checkFileRemap(os.path.join(misc_location, gen_type_dict['module_jn_code'])))
+            module_imports += gen_type_dict.get("module_imports", [])
+
+        java_files_dir = os.path.join(misc_location, 'src/java')
+        if os.path.exists(java_files_dir):
+            copy_java_files(java_files_dir, java_base_path)
+
+        java_test_files_dir = os.path.join(misc_location, 'test')
+        if os.path.exists(java_test_files_dir):
+            copy_java_files(java_test_files_dir, java_test_base_path)
+
+        generator.gen(srcfiles, module, dstdir, jni_path, java_path, common_headers)
+    generator.finalize(jni_path)
+
+    print('Generated files: %d (updated %d)' % (total_files, updated_files))
diff --git a/modules/java/generator/src/cpp/Mat.cpp b/modules/java/generator/src/cpp/Mat.cpp
index 14f5497a1d..7b7c82c69c 100644
--- a/modules/java/generator/src/cpp/Mat.cpp
+++ b/modules/java/generator/src/cpp/Mat.cpp
@@ -1,10 +1,11 @@
-#define LOG_TAG "org.opencv.core.Mat"
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html
 
-#include <stdexcept>
-#include <string>
+#include "opencv2/core.hpp"
 
+#define LOG_TAG "org.opencv.core.Mat"
 #include "common.h"
-#include "opencv2/core.hpp"
 
 using namespace cv;
 
@@ -1777,67 +1778,6 @@ JNIEXPORT void JNICALL Java_org_opencv_core_Mat_n_1delete
     delete (Mat*) self;
 }
 
-// unlike other nPut()-s this one (with double[]) should convert input values to correct type
-#define PUT_ITEM(T, R, C) { T*dst = (T*)me->ptr(R, C); for(int ch=0; ch<me->channels() && count>0; count--,ch++,src++,dst++) *dst = cv::saturate_cast<T>(*src); }
-
-JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutD
-    (JNIEnv* env, jclass, jlong self, jint row, jint col, jint count, jdoubleArray vals);
-
-JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutD
-    (JNIEnv* env, jclass, jlong self, jint row, jint col, jint count, jdoubleArray vals)
-{
-    static const char method_name[] = "Mat::nPutD()";
-    try {
-        LOGD("%s", method_name);
-        cv::Mat* me = (cv::Mat*) self;
-        if(!me || !me->data) return 0;  // no native object behind
-        if(me->rows<=row || me->cols<=col) return 0; // indexes out of range
-
-        int rest = ((me->rows - row) * me->cols - col) * me->channels();
-        if(count>rest) count = rest;
-        int res = count;
-        double* values = (double*)env->GetPrimitiveArrayCritical(vals, 0);
-        double* src = values;
-        int r, c;
-        for(c=col; c<me->cols && count>0; c++)
-        {
-            switch(me->depth()) {
-                case CV_8U:  PUT_ITEM(uchar,  row, c); break;
-                case CV_8S:  PUT_ITEM(schar,  row, c); break;
-                case CV_16U: PUT_ITEM(ushort, row, c); break;
-                case CV_16S: PUT_ITEM(short,  row, c); break;
-                case CV_32S: PUT_ITEM(int,    row, c); break;
-                case CV_32F: PUT_ITEM(float,  row, c); break;
-                case CV_64F: PUT_ITEM(double, row, c); break;
-            }
-        }
-
-        for(r=row+1; r<me->rows && count>0; r++)
-            for(c=0; c<me->cols && count>0; c++)
-            {
-                switch(me->depth()) {
-                    case CV_8U:  PUT_ITEM(uchar,  r, c); break;
-                    case CV_8S:  PUT_ITEM(schar,  r, c); break;
-                    case CV_16U: PUT_ITEM(ushort, r, c); break;
-                    case CV_16S: PUT_ITEM(short,  r, c); break;
-                    case CV_32S: PUT_ITEM(int,    r, c); break;
-                    case CV_32F: PUT_ITEM(float,  r, c); break;
-                    case CV_64F: PUT_ITEM(double, r, c); break;
-                }
-            }
-
-        env->ReleasePrimitiveArrayCritical(vals, values, 0);
-        return res;
-    } catch(const std::exception &e) {
-        throwJavaException(env, &e, method_name);
-    } catch (...) {
-        throwJavaException(env, 0, method_name);
-    }
-
-    return 0;
-}
-
-
 } // extern "C"
 
 namespace {
@@ -1963,6 +1903,66 @@ JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutF
   return java_mat_put(env, self, row, col, count, 0, vals);
 }
 
+// unlike other nPut()-s this one (with double[]) should convert input values to correct type
+#define PUT_ITEM(T, R, C) { T*dst = (T*)me->ptr(R, C); for(int ch=0; ch<me->channels() && count>0; count--,ch++,src++,dst++) *dst = cv::saturate_cast<T>(*src); }
+
+JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutD
+    (JNIEnv* env, jclass, jlong self, jint row, jint col, jint count, jdoubleArray vals);
+
+JNIEXPORT jint JNICALL Java_org_opencv_core_Mat_nPutD
+    (JNIEnv* env, jclass, jlong self, jint row, jint col, jint count, jdoubleArray vals)
+{
+    static const char* method_name = JavaOpenCVTrait<jdoubleArray>::put;
+    try {
+        LOGD("%s", method_name);
+        cv::Mat* me = (cv::Mat*) self;
+        if(!me || !me->data) return 0;  // no native object behind
+        if(me->rows<=row || me->cols<=col) return 0; // indexes out of range
+
+        int rest = ((me->rows - row) * me->cols - col) * me->channels();
+        if(count>rest) count = rest;
+        int res = count;
+        double* values = (double*)env->GetPrimitiveArrayCritical(vals, 0);
+        double* src = values;
+        int r, c;
+        for(c=col; c<me->cols && count>0; c++)
+        {
+            switch(me->depth()) {
+                case CV_8U:  PUT_ITEM(uchar,  row, c); break;
+                case CV_8S:  PUT_ITEM(schar,  row, c); break;
+                case CV_16U: PUT_ITEM(ushort, row, c); break;
+                case CV_16S: PUT_ITEM(short,  row, c); break;
+                case CV_32S: PUT_ITEM(int,    row, c); break;
+                case CV_32F: PUT_ITEM(float,  row, c); break;
+                case CV_64F: PUT_ITEM(double, row, c); break;
+            }
+        }
+
+        for(r=row+1; r<me->rows && count>0; r++)
+            for(c=0; c<me->cols && count>0; c++)
+            {
+                switch(me->depth()) {
+                    case CV_8U:  PUT_ITEM(uchar,  r, c); break;
+                    case CV_8S:  PUT_ITEM(schar,  r, c); break;
+                    case CV_16U: PUT_ITEM(ushort, r, c); break;
+                    case CV_16S: PUT_ITEM(short,  r, c); break;
+                    case CV_32S: PUT_ITEM(int,    r, c); break;
+                    case CV_32F: PUT_ITEM(float,  r, c); break;
+                    case CV_64F: PUT_ITEM(double, r, c); break;
+                }
+            }
+
+        env->ReleasePrimitiveArrayCritical(vals, values, 0);
+        return res;
+    } catch(const std::exception &e) {
+        throwJavaException(env, &e, method_name);
+    } catch (...) {
+        throwJavaException(env, 0, method_name);
+    }
+
+    return 0;
+}
+
 } // extern "C"
 
 template<typename T> static int mat_get(cv::Mat* m, int row, int col, int count, char* buff)
diff --git a/modules/java/generator/src/cpp/common.h b/modules/java/generator/src/cpp/common.h
index b629d94d4b..7c9b87d971 100644
--- a/modules/java/generator/src/cpp/common.h
+++ b/modules/java/generator/src/cpp/common.h
@@ -1,5 +1,14 @@
-#ifndef __JAVA_COMMON_H__
-#define __JAVA_COMMON_H__
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html
+
+#ifndef __OPENCV_JAVA_COMMON_H__
+#define __OPENCV_JAVA_COMMON_H__
+
+#include <stdexcept>
+#include <string>
+
+extern "C" {
 
 #if !defined(__ppc__)
 // to suppress warning from jni.h on OS X
@@ -7,7 +16,9 @@
 #endif
 #include <jni.h>
 
-#include "opencv2/java.hpp"
+} // extern "C"
+
+#include "opencv_java.hpp"
 #include "opencv2/core/utility.hpp"
 
 #include "converters.h"
@@ -17,4 +28,4 @@
 #  pragma warning(disable:4800 4244)
 #endif
 
-#endif //__JAVA_COMMON_H__
+#endif //__OPENCV_JAVA_COMMON_H__
diff --git a/modules/java/generator/src/cpp/converters.cpp b/modules/java/generator/src/cpp/converters.cpp
index 5929cf4f35..11511200e1 100644
--- a/modules/java/generator/src/cpp/converters.cpp
+++ b/modules/java/generator/src/cpp/converters.cpp
@@ -1,3 +1,7 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html
+
 #define LOG_TAG "org.opencv.utils.Converters"
 #include "common.h"
 
diff --git a/modules/java/generator/src/cpp/converters.h b/modules/java/generator/src/cpp/converters.h
index 25077b10c2..27c2ded3a8 100644
--- a/modules/java/generator/src/cpp/converters.h
+++ b/modules/java/generator/src/cpp/converters.h
@@ -1,3 +1,7 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html
+
 #include "opencv2/opencv_modules.hpp"
 #include "opencv2/core.hpp"
 
diff --git a/modules/java/generator/src/cpp/jni_part.cpp b/modules/java/generator/src/cpp/jni_part.cpp
index 3e6f895c0e..dc3bb5eeed 100644
--- a/modules/java/generator/src/cpp/jni_part.cpp
+++ b/modules/java/generator/src/cpp/jni_part.cpp
@@ -1,3 +1,7 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html
+
 #include "common.h"
 
 #include "opencv2/opencv_modules.hpp"
diff --git a/modules/java/generator/src/cpp/opencv_java.cpp b/modules/java/generator/src/cpp/opencv_java.cpp
new file mode 100644
index 0000000000..333c474537
--- /dev/null
+++ b/modules/java/generator/src/cpp/opencv_java.cpp
@@ -0,0 +1,8 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html
+
+#include "common.h"
+
+// Include all generated JNI code
+#include "opencv_jni.hpp"
diff --git a/modules/java/generator/src/cpp/opencv_java.hpp b/modules/java/generator/src/cpp/opencv_java.hpp
index 28cf9e5c05..e5cd622e31 100644
--- a/modules/java/generator/src/cpp/opencv_java.hpp
+++ b/modules/java/generator/src/cpp/opencv_java.hpp
@@ -4,9 +4,8 @@
 
 // Author: abratchik
 
-#ifndef JAVA_HPP
-#define	JAVA_HPP
-
+#undef LOGE
+#undef LOGD
 #ifdef __ANDROID__
 #  include <android/log.h>
 #  define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
@@ -20,6 +19,8 @@
 #  define LOGD(...)
 #endif
 
+#ifndef OPENCV_JAVA_HPP
+#define	OPENCV_JAVA_HPP
 
 #define MATOFINT(ENV) static_cast<jclass>(ENV->NewGlobalRef(ENV->FindClass("org/opencv/core/MatOfInt")))
 #define GETNATIVEOBJ(ENV, CLS, MAT) ENV->GetLongField(MAT, ENV->GetFieldID(CLS, "nativeObj", "J"))
@@ -34,16 +35,4 @@
 
 #define CHECK_MAT(cond) if(!(cond)){ LOGD("FAILED: " #cond); return; }
 
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-
-
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* JAVA_HPP */
\ No newline at end of file
+#endif	// OPENCV_JAVA_HPP
diff --git a/modules/java/generator/src/cpp/utils.cpp b/modules/java/generator/src/cpp/utils.cpp
index 2d409c863e..3340736027 100644
--- a/modules/java/generator/src/cpp/utils.cpp
+++ b/modules/java/generator/src/cpp/utils.cpp
@@ -1,5 +1,6 @@
-#define LOG_TAG "org.opencv.android.Utils"
-#include "common.h"
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html
 
 #include "opencv2/core.hpp"
 #include "opencv2/imgproc.hpp"
@@ -7,6 +8,9 @@
 #ifdef __ANDROID__
 #include <android/bitmap.h>
 
+#define LOG_TAG "org.opencv.android.Utils"
+#include "common.h"
+
 using namespace cv;
 
 extern "C" {
diff --git a/modules/java/generator/src/java/org/opencv/osgi/OpenCVNativeLoader.java.in b/modules/java/generator/src/java/org/opencv/osgi/OpenCVNativeLoader.java.in
index 77e290b1be..7281a45533 100644
--- a/modules/java/generator/src/java/org/opencv/osgi/OpenCVNativeLoader.java.in
+++ b/modules/java/generator/src/java/org/opencv/osgi/OpenCVNativeLoader.java.in
@@ -12,7 +12,7 @@ import java.util.logging.Logger;
 public class OpenCVNativeLoader implements OpenCVInterface {
 
     public void init() {
-        System.loadLibrary("opencv_java@LIB_NAME_SUFIX@");
+        System.loadLibrary("opencv_java@OPENCV_JAVA_LIB_NAME_SUFFIX@");
         Logger.getLogger("org.opencv.osgi").log(Level.INFO, "Successfully loaded OpenCV native library.");
     }
 }
diff --git a/modules/java/generator/templates/cpp_module.template b/modules/java/generator/templates/cpp_module.template
new file mode 100644
index 0000000000..25805c0746
--- /dev/null
+++ b/modules/java/generator/templates/cpp_module.template
@@ -0,0 +1,52 @@
+//
+// This file is auto-generated. Please don't modify it!
+//
+
+#undef LOG_TAG
+
+#include "opencv2/opencv_modules.hpp"
+#ifdef HAVE_OPENCV_$M
+
+#include <string>
+
+#include "opencv2/$m.hpp"
+
+$includes
+
+#define LOG_TAG "org.opencv.$m"
+#include "common.h"
+
+using namespace cv;
+
+/// throw java exception
+#undef throwJavaException
+#define throwJavaException throwJavaException_$m
+static void throwJavaException(JNIEnv *env, const std::exception *e, const char *method) {
+  std::string what = "unknown exception";
+  jclass je = 0;
+
+  if(e) {
+    std::string exception_type = "std::exception";
+
+    if(dynamic_cast<const cv::Exception*>(e)) {
+      exception_type = "cv::Exception";
+      je = env->FindClass("org/opencv/core/CvException");
+    }
+
+    what = exception_type + ": " + e->what();
+  }
+
+  if(!je) je = env->FindClass("java/lang/Exception");
+  env->ThrowNew(je, what.c_str());
+
+  LOGE("%s caught %s", method, what.c_str());
+  (void)method;        // avoid "unused" warning
+}
+
+extern "C" {
+
+$code
+
+} // extern "C"
+
+#endif // HAVE_OPENCV_$M
diff --git a/modules/java/generator/templates/java_class.prolog b/modules/java/generator/templates/java_class.prolog
new file mode 100644
index 0000000000..b771be9439
--- /dev/null
+++ b/modules/java/generator/templates/java_class.prolog
@@ -0,0 +1,15 @@
+//
+// This file is auto-generated. Please don't modify it!
+//
+package org.opencv.$module;
+
+$imports
+
+$docs
+$annotation
+public class $jname {
+
+    protected final long nativeObj;
+    protected $jname(long addr) { nativeObj = addr; }
+
+    public long getNativeObjAddr() { return nativeObj; }
diff --git a/modules/java/generator/templates/java_class_inherited.prolog b/modules/java/generator/templates/java_class_inherited.prolog
new file mode 100644
index 0000000000..e7b32080cf
--- /dev/null
+++ b/modules/java/generator/templates/java_class_inherited.prolog
@@ -0,0 +1,12 @@
+//
+// This file is auto-generated. Please don't modify it!
+//
+package org.opencv.$module;
+
+$imports
+
+$docs
+$annotation
+public class $jname extends $base {
+
+    protected $jname(long addr) { super(addr); }
diff --git a/modules/java/generator/templates/java_module.prolog b/modules/java/generator/templates/java_module.prolog
new file mode 100644
index 0000000000..41c453bd8c
--- /dev/null
+++ b/modules/java/generator/templates/java_module.prolog
@@ -0,0 +1,10 @@
+//
+// This file is auto-generated. Please don't modify it!
+//
+package org.opencv.$module;
+
+$imports
+
+$docs
+$annotation
+public class $jname {
diff --git a/modules/java/jar/CMakeLists.txt b/modules/java/jar/CMakeLists.txt
new file mode 100644
index 0000000000..3c3c47a1f7
--- /dev/null
+++ b/modules/java/jar/CMakeLists.txt
@@ -0,0 +1,53 @@
+project(${the_module}_jar)
+
+set(OPENCV_JAVA_DIR "${CMAKE_CURRENT_BINARY_DIR}/opencv" CACHE INTERNAL "")
+
+file(REMOVE_RECURSE "${OPENCV_JAVA_DIR}")
+file(REMOVE "${OPENCV_DEPHELPER}/${the_module}_jar_source_copy")
+
+file(MAKE_DIRECTORY "${OPENCV_JAVA_DIR}/build/classes")
+set(java_src_dir "${OPENCV_JAVA_DIR}/java")
+file(MAKE_DIRECTORY "${java_src_dir}")
+
+set(JAR_NAME opencv-${OPENCV_JAVA_LIB_NAME_SUFFIX}.jar)
+set(OPENCV_JAR_FILE "${OpenCV_BINARY_DIR}/bin/${JAR_NAME}" CACHE INTERNAL "")
+
+ocv_copyfiles_append_dir(JAVA_SRC_COPY "${OPENCV_JAVA_BINDINGS_DIR}/gen/java" "${java_src_dir}")
+
+set(depends gen_opencv_java_source "${OPENCV_DEPHELPER}/gen_opencv_java_source")
+ocv_copyfiles_add_target(${the_module}_jar_source_copy JAVA_SRC_COPY "Copy Java(JAR) source files" ${depends})
+set(depends ${the_module}_jar_source_copy "${OPENCV_DEPHELPER}/${the_module}_jar_source_copy")
+
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/build.xml.in" "${OPENCV_JAVA_DIR}/build.xml" @ONLY)
+list(APPEND depends "${OPENCV_JAVA_DIR}/build.xml")
+
+add_custom_command(OUTPUT "${OPENCV_DEPHELPER}/${the_module}_jar"
+    COMMAND ${ANT_EXECUTABLE} -noinput -k jar
+    COMMAND ${CMAKE_COMMAND} -E touch "${OPENCV_DEPHELPER}/${the_module}_jar"
+    WORKING_DIRECTORY "${OPENCV_JAVA_DIR}"
+    DEPENDS ${depends}
+    COMMENT "Generating ${JAR_NAME}"
+)
+add_custom_target(${the_module}_jar DEPENDS "${OPENCV_DEPHELPER}/${the_module}_jar")
+
+add_custom_command(OUTPUT "${OPENCV_DEPHELPER}/${the_module}doc"
+    COMMAND ${ANT_EXECUTABLE} -noinput -k javadoc
+    COMMAND ${CMAKE_COMMAND} -E touch "${OPENCV_DEPHELPER}/${the_module}doc"
+    WORKING_DIRECTORY "${OPENCV_JAVA_DIR}"
+    DEPENDS ${depends}
+    COMMENT "Generating Javadoc"
+)
+add_custom_target(${the_module}doc DEPENDS "${OPENCV_DEPHELPER}/${the_module}doc")
+
+install(FILES ${OPENCV_JAR_FILE} OPTIONAL DESTINATION ${OPENCV_JAR_INSTALL_PATH} COMPONENT java)
+
+add_dependencies(${the_module} ${the_module}_jar)
+
+if(BUILD_DOCS)
+  add_dependencies(opencv_docs ${the_module}doc)
+
+  install(DIRECTORY ${OpenCV_BINARY_DIR}/doc/javadoc
+    DESTINATION "${OPENCV_DOC_INSTALL_PATH}/javadoc"
+    COMPONENT "docs" OPTIONAL
+  )
+endif()
diff --git a/modules/java/jar/build.xml.in b/modules/java/jar/build.xml.in
index 954ff6273c..04356a6904 100644
--- a/modules/java/jar/build.xml.in
+++ b/modules/java/jar/build.xml.in
@@ -3,28 +3,39 @@
 <!-- interface files into OpenCV.jar -->
 <project name="OpenCV">
 
+    <tstamp>
+      <format property="timestamp" pattern="EEE MMM d yyyy HH:mm:ss z"/>
+    </tstamp>
+
   <target name="jar">
     <!-- This is to make a jar with a source attachment, for e.g. easy -->
     <!-- navigation in Eclipse. See this question: -->
     <!-- http://stackoverflow.com/questions/3584968/ant-how-to-compile-jar-that-includes-source-attachment -->
-    <javac sourcepath="" srcdir="src" destdir="src" debug="on" includeantruntime="false" >
+    <javac sourcepath="" srcdir="java" destdir="build/classes" debug="on" includeantruntime="false" >
       <include name="**/*.java"/>
       <compilerarg line="-encoding utf-8"/>
     </javac>
-    <jar basedir="src" destfile="bin/@JAR_NAME@"/>
+    <jar destfile="@OPENCV_JAR_FILE@">
+      <fileset dir="java"/>
+      <fileset dir="build/classes"/>
+      <manifest>
+        <attribute name="Specification-Title" value="OpenCV"/>
+        <attribute name="Specification-Version" value="@OPENCV_VERSION@"/>
+        <attribute name="Implementation-Title" value="OpenCV"/>
+        <attribute name="Implementation-Version" value="@OPENCV_VCSVERSION@"/>
+        <attribute name="Implementation-Date" value="${timestamp}"/>
+      </manifest>
+    </jar>
   </target>
 
   <target name="javadoc">
-    <tstamp>
-      <format property="doctimestamp" pattern="EEE MMM d yyyy HH:mm:ss z"/>
-    </tstamp>
     <javadoc
       packagenames="org.opencv.*"
-      sourcepath="src"
-      destdir="doc/javadoc"
+      sourcepath="java"
+      destdir="@OpenCV_BINARY_DIR@/doc/javadoc"
       Windowtitle="OpenCV @OPENCV_VERSION_PLAIN@ Java documentation"
       Doctitle="OpenCV Java documentation (@OPENCV_VERSION@)"
-      bottom="Generated on ${doctimestamp} / OpenCV @OPENCV_VCSVERSION@"
+      bottom="Generated on ${timestamp} / OpenCV @OPENCV_VCSVERSION@"
       />
   </target>
 
diff --git a/modules/java/jni/CMakeLists.txt b/modules/java/jni/CMakeLists.txt
new file mode 100644
index 0000000000..45fee3745d
--- /dev/null
+++ b/modules/java/jni/CMakeLists.txt
@@ -0,0 +1,90 @@
+project(${the_module})
+
+glob_more_specific_sources(H "${CMAKE_CURRENT_SOURCE_DIR}/../generator/src" handwritten_h_sources)
+glob_more_specific_sources(CPP "${CMAKE_CURRENT_SOURCE_DIR}/../generator/src" handwritten_cpp_sources)
+
+# grab C++ files from misc/java
+foreach(m ${OPENCV_MODULES_BUILD})
+  if (";${OPENCV_MODULE_${m}_WRAPPERS};" MATCHES ";java;" AND HAVE_${m})
+    set(module_java_dir "${OPENCV_MODULE_${m}_LOCATION}/misc/java")
+    include_directories("${module_java_dir}/src/cpp")
+    file(GLOB _result "${module_java_dir}/src/cpp/*.h" "${module_java_dir}/src/cpp/*.hpp" "${module_java_dir}/src/cpp/*.cpp")
+    list(APPEND handwritten_cpp_sources ${_result})
+  endif()
+endforeach()
+
+set(__type MODULE)
+if(BUILD_FAT_JAVA_LIB)
+  set(__type SHARED) # samples link to libopencv_java
+endif()
+ocv_add_library(${the_module} ${__type}
+    ${handwritten_h_sources} ${handwritten_cpp_sources} ${generated_cpp_sources}
+    ${copied_files}
+)
+add_dependencies(${the_module} gen_opencv_java_source)
+
+ocv_target_include_directories(${the_module} "${CMAKE_CURRENT_SOURCE_DIR}/../generator/src/cpp")
+ocv_target_include_directories(${the_module} "${OPENCV_JAVA_BINDINGS_DIR}/gen/cpp")
+ocv_target_include_modules(${the_module} ${OPENCV_MODULE_${the_module}_DEPS})
+if(NOT ANDROID)
+  ocv_target_include_directories(${the_module} SYSTEM ${JNI_INCLUDE_DIRS})
+endif()
+
+set(__deps ${OPENCV_MODULE_${the_module}_DEPS})
+list(REMOVE_ITEM __deps opencv_java_bindings_generator) # don't add dummy module to target_link_libraries list
+
+if(BUILD_FAT_JAVA_LIB)
+  ocv_list_unique(__deps)
+  set(__extradeps ${__deps})
+  ocv_list_filterout(__extradeps "^opencv_")
+  if(__extradeps)
+    list(REMOVE_ITEM __deps ${__extradeps})
+  endif()
+  if(APPLE)
+    foreach(_dep ${__deps})
+      ocv_target_link_libraries(${the_module} LINK_PRIVATE -Wl,-force_load "${_dep}")
+    endforeach()
+  elseif(((CMAKE_COMPILER_IS_GNUCXX OR UNIX) OR (OPENCV_FORCE_FAT_JAVA_LIB_LD_RULES)) AND (NOT OPENCV_SKIP_FAT_JAVA_LIB_LD_RULES))
+    ocv_target_link_libraries(${the_module} LINK_PRIVATE -Wl,-whole-archive ${__deps} -Wl,-no-whole-archive)
+  else()
+    ocv_target_link_libraries(${the_module} LINK_PRIVATE ${__deps})
+  endif()
+  ocv_target_link_libraries(${the_module} LINK_PRIVATE ${__extradeps} ${OPENCV_LINKER_LIBS})
+else()
+  ocv_target_link_libraries(${the_module} LINK_PRIVATE ${__deps} ${OPENCV_LINKER_LIBS})
+endif()
+
+# Additional target properties
+set_target_properties(${the_module} PROPERTIES
+    OUTPUT_NAME "${the_module}${OPENCV_JAVA_LIB_NAME_SUFFIX}"
+    ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH}
+    LIBRARY_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH}
+    RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}
+    DEFINE_SYMBOL CVAPI_EXPORTS
+    )
+
+if(ANDROID)
+  ocv_target_link_libraries(${the_module} LINK_PUBLIC jnigraphics) # for Mat <=> Bitmap converters
+  ocv_target_link_libraries(${the_module} LINK_PUBLIC log dl z)
+
+  # force strip library after the build command
+  # because samples and tests will make a copy of the library before install
+  if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
+    add_custom_command(TARGET ${the_module} POST_BUILD COMMAND ${CMAKE_STRIP} --strip-unneeded "$<TARGET_FILE:${the_module}>")
+  endif()
+endif()
+
+if(ENABLE_SOLUTION_FOLDERS)
+  set_target_properties(${the_module} PROPERTIES FOLDER "bindings")
+endif()
+
+set(__install_export "")
+if(BUILD_FAT_JAVA_LIB)
+  set(__install_export EXPORT OpenCVModules)
+endif()
+
+ocv_install_target(${the_module} OPTIONAL ${__install_export}
+    RUNTIME DESTINATION ${OPENCV_JNI_BIN_INSTALL_PATH} COMPONENT java
+    LIBRARY DESTINATION ${OPENCV_JNI_INSTALL_PATH} COMPONENT java
+    ARCHIVE DESTINATION ${OPENCV_JNI_INSTALL_PATH} COMPONENT java
+)
diff --git a/modules/java/test/android_test/CMakeLists.txt b/modules/java/test/android_test/CMakeLists.txt
index 2925d65e37..b6e727f50f 100644
--- a/modules/java/test/android_test/CMakeLists.txt
+++ b/modules/java/test/android_test/CMakeLists.txt
@@ -4,68 +4,74 @@ endif()
 
 project(opencv_test_java)
 
-set(opencv_test_java_bin_dir "${CMAKE_CURRENT_BINARY_DIR}/.build")
+set(OPENCV_JAVA_TEST_DIR "${OpenCV_BINARY_DIR}/android_test" CACHE INTERNAL "")
+file(REMOVE_RECURSE "${OPENCV_JAVA_TEST_DIR}")
+file(REMOVE "${OPENCV_DEPHELPER}/${the_module}_test_source_copy")
+
 set(test_dir "${CMAKE_CURRENT_SOURCE_DIR}")
-set(test_common_dir "${CMAKE_CURRENT_SOURCE_DIR}/../common_test")
 
-set(opencv_test_java_file_deps "")
+set(depends "")
 
 # 1. gather and copy common test files (resources, utils, etc.)
-copy_common_tests(test_common_dir opencv_test_java_bin_dir opencv_test_java_file_deps)
+copy_common_tests("${CMAKE_CURRENT_SOURCE_DIR}/../common_test" "${OPENCV_JAVA_TEST_DIR}" depends)
 
 # 2. gather and copy tests from each module
-copy_modules_tests(OPENCV_JAVA_MODULES opencv_test_java_bin_dir opencv_test_java_file_deps)
+ocv_copyfiles_append_dir(JAVA_TEST_SRC_COPY "${OPENCV_JAVA_BINDINGS_DIR}/gen/test" "${OPENCV_JAVA_TEST_DIR}/src")
+
+list(APPEND depends gen_opencv_java_source "${OPENCV_DEPHELPER}/gen_opencv_java_source")
+ocv_copyfiles_add_target(${the_module}_test_source_copy JAVA_TEST_SRC_COPY "Copy Java(Android test) source files" ${depends})
+set(depends ${the_module}_test_source_copy "${OPENCV_DEPHELPER}/${the_module}_test_source_copy")
 
 # 3. gather and copy specific files for Android
 file(GLOB_RECURSE test_files RELATIVE "${test_dir}" "${test_dir}/res/*" "${test_dir}/src/*")
 foreach(f ${test_files} ${ANDROID_MANIFEST_FILE} ".classpath" ".project")
   add_custom_command(
-      OUTPUT "${opencv_test_java_bin_dir}/${f}"
-      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${test_dir}/${f}" "${opencv_test_java_bin_dir}/${f}"
+      OUTPUT "${OPENCV_JAVA_TEST_DIR}/${f}"
+      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${test_dir}/${f}" "${OPENCV_JAVA_TEST_DIR}/${f}"
       MAIN_DEPENDENCY "${test_dir}/${f}"
       COMMENT "Copying ${f}")
-  list(APPEND opencv_test_java_file_deps "${test_dir}/${f}" "${opencv_test_java_bin_dir}/${f}")
+  list(APPEND depends "${test_dir}/${f}" "${OPENCV_JAVA_TEST_DIR}/${f}")
 endforeach()
 
 # fix Android project
 set(android_proj_target_files ${ANDROID_PROJECT_FILES})
-ocv_list_add_prefix(android_proj_target_files "${opencv_test_java_bin_dir}/")
-file(RELATIVE_PATH __dep "${opencv_test_java_bin_dir}" "${OpenCV_BINARY_DIR}")
+ocv_list_add_prefix(android_proj_target_files "${OPENCV_JAVA_TEST_DIR}/")
+file(RELATIVE_PATH __dep "${OPENCV_JAVA_TEST_DIR}" "${OpenCV_BINARY_DIR}/android_sdk")
 
 add_custom_command(
     OUTPUT ${android_proj_target_files}
     COMMAND ${CMAKE_COMMAND} -E remove ${android_proj_target_files}
-    COMMAND ${ANDROID_EXECUTABLE} --silent update test-project --path "${opencv_test_java_bin_dir}" --main "${OpenCV_BINARY_DIR}"
-    COMMAND ${ANDROID_EXECUTABLE} --silent update project --path "${opencv_test_java_bin_dir}" --library "${__dep}"
-    MAIN_DEPENDENCY "${opencv_test_java_bin_dir}/${ANDROID_MANIFEST_FILE}"
+    COMMAND ${ANDROID_EXECUTABLE} --silent update test-project --path "${OPENCV_JAVA_TEST_DIR}" --main "${OpenCV_BINARY_DIR}/android_sdk"
+    COMMAND ${ANDROID_EXECUTABLE} --silent update project --path "${OPENCV_JAVA_TEST_DIR}" --library "${__dep}"
+    MAIN_DEPENDENCY "${OPENCV_JAVA_TEST_DIR}/${ANDROID_MANIFEST_FILE}"
     DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${ANDROID_MANIFEST_FILE}"
     COMMENT "Updating Android Java API test project")
 
-list(APPEND opencv_test_java_file_deps ${android_proj_target_files})
+list(APPEND depends ${android_proj_target_files})
 
 # build java part
 add_custom_command(
-    OUTPUT "${opencv_test_java_bin_dir}/bin/OpenCVTest-debug.apk"
-    COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:opencv_java>" "${opencv_test_java_bin_dir}/libs/${ANDROID_NDK_ABI_NAME}/$<TARGET_FILE_NAME:opencv_java>"
+    OUTPUT "${OPENCV_JAVA_TEST_DIR}/bin/OpenCVTest-debug.apk"
+    COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:opencv_java>" "${OPENCV_JAVA_TEST_DIR}/libs/${ANDROID_NDK_ABI_NAME}/$<TARGET_FILE_NAME:opencv_java>"
     COMMAND ${ANT_EXECUTABLE} -q -noinput -k debug -Djava.target=1.6 -Djava.source=1.6
-    COMMAND ${CMAKE_COMMAND} -E touch "${opencv_test_java_bin_dir}/bin/OpenCVTest-debug.apk" # needed because ant does not update the timestamp of updated apk
-    WORKING_DIRECTORY "${opencv_test_java_bin_dir}"
-    MAIN_DEPENDENCY "${opencv_test_java_bin_dir}/${ANDROID_MANIFEST_FILE}"
-    DEPENDS "${JAR_FILE}.dephelper" opencv_java
-    DEPENDS ${opencv_test_java_file_deps})
+    COMMAND ${CMAKE_COMMAND} -E touch "${OPENCV_JAVA_TEST_DIR}/bin/OpenCVTest-debug.apk" # needed because ant does not update the timestamp of updated apk
+    WORKING_DIRECTORY "${OPENCV_JAVA_TEST_DIR}"
+    MAIN_DEPENDENCY "${OPENCV_JAVA_TEST_DIR}/${ANDROID_MANIFEST_FILE}"
+    DEPENDS opencv_java_android opencv_java
+    DEPENDS ${depends})
 
-add_custom_target(${PROJECT_NAME} ALL SOURCES "${opencv_test_java_bin_dir}/bin/OpenCVTest-debug.apk" )
+add_custom_target(${PROJECT_NAME} ALL SOURCES "${OPENCV_JAVA_TEST_DIR}/bin/OpenCVTest-debug.apk" "${CMAKE_CURRENT_SOURCE_DIR}/${ANDROID_MANIFEST_FILE}")
 add_dependencies(${PROJECT_NAME} opencv_java ${__android_project_chain})
 set(__android_project_chain ${PROJECT_NAME} CACHE INTERNAL "auxiliary variable used for Android progects chaining" FORCE)
 
 # put the final .apk to the OpenCV's bin folder
-add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${opencv_test_java_bin_dir}/bin/OpenCVTest-debug.apk" "${OpenCV_BINARY_DIR}/bin/${PROJECT_NAME}.apk")
+add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${OPENCV_JAVA_TEST_DIR}/bin/OpenCVTest-debug.apk" "${OpenCV_BINARY_DIR}/bin/${PROJECT_NAME}.apk")
 
 add_dependencies(opencv_tests ${PROJECT_NAME})
 
 if(PYTHON_DEFAULT_AVAILABLE)
   set(CHECK_TEST_COVERAGE "${OPENCV_MODULE_opencv_java_LOCATION}/check-tests.py")
   add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
-      COMMAND ${PYTHON_DEFAULT_EXECUTABLE} ${CHECK_TEST_COVERAGE} "${CMAKE_CURRENT_SOURCE_DIR}/src" "${OpenCV_BINARY_DIR}/src" > "${CMAKE_CURRENT_BINARY_DIR}/tests_coverage.log"
+      COMMAND ${PYTHON_DEFAULT_EXECUTABLE} ${CHECK_TEST_COVERAGE} "${OPENCV_JAVA_TEST_DIR}/src" "${OPENCV_ANDROID_LIB_DIR}/src" > "${CMAKE_CURRENT_BINARY_DIR}/tests_coverage.log"
       )
 endif()
diff --git a/modules/java/test/pure_test/CMakeLists.txt b/modules/java/test/pure_test/CMakeLists.txt
index 95b7e47512..82972fd147 100644
--- a/modules/java/test/pure_test/CMakeLists.txt
+++ b/modules/java/test/pure_test/CMakeLists.txt
@@ -1,72 +1,74 @@
 if(NOT ANT_EXECUTABLE
-  OR NOT BUILD_opencv_imgcodecs
-  OR NOT BUILD_opencv_calib3d)
+    OR NOT BUILD_opencv_imgcodecs
+    OR NOT BUILD_opencv_calib3d)
   return()
 endif()
 
 project(opencv_test_java)
 
-set(opencv_test_java_bin_dir "${CMAKE_CURRENT_BINARY_DIR}/.build")
-set(test_dir ${CMAKE_CURRENT_SOURCE_DIR})
-set(test_common_dir "${CMAKE_CURRENT_SOURCE_DIR}/../common_test")
+set(OPENCV_JAR_FILE "${OPENCV_JAR_FILE}")
+get_filename_component(JAR_NAME "${OPENCV_JAR_FILE}" NAME)
+
+set(OPENCV_JAVA_TEST_DIR "${OpenCV_BINARY_DIR}/java_test" CACHE INTERNAL "")
+file(REMOVE_RECURSE "${OPENCV_JAVA_TEST_DIR}")
+file(MAKE_DIRECTORY "${OPENCV_JAVA_TEST_DIR}")
+file(REMOVE "${OPENCV_DEPHELPER}/${the_module}_test_source_copy")
 
-set(opencv_test_java_file_deps "")
+set(test_dir ${CMAKE_CURRENT_SOURCE_DIR})
 
-# make sure the build directory exists
-file(MAKE_DIRECTORY "${opencv_test_java_bin_dir}")
+set(depends "")
 
 # 1. gather and copy common test files (resources, utils, etc.)
-copy_common_tests(test_common_dir opencv_test_java_bin_dir opencv_test_java_file_deps)
+copy_common_tests("${CMAKE_CURRENT_SOURCE_DIR}/../common_test" "${OPENCV_JAVA_TEST_DIR}" depends)
 
 # 2. gather and copy tests from each module
-copy_modules_tests(OPENCV_JAVA_MODULES opencv_test_java_bin_dir opencv_test_java_file_deps)
+ocv_copyfiles_append_dir(JAVA_TEST_SRC_COPY "${OPENCV_JAVA_BINDINGS_DIR}/gen/test" "${OPENCV_JAVA_TEST_DIR}/src")
+
+list(APPEND depends gen_opencv_java_source "${OPENCV_DEPHELPER}/gen_opencv_java_source")
+ocv_copyfiles_add_target(${the_module}_test_source_copy JAVA_TEST_SRC_COPY "Copy Java(Test) source files" ${depends})
+set(depends ${the_module}_test_source_copy "${OPENCV_DEPHELPER}/${the_module}_test_source_copy")
 
 # 3. gather and copy specific files for pure java
 file(GLOB_RECURSE test_files RELATIVE "${test_dir}" "${test_dir}/src/*")
 file(GLOB_RECURSE test_lib_files RELATIVE "${test_dir}" "${test_dir}/lib/*.jar")
 foreach(f ${test_files} ${test_lib_files})
-  add_custom_command(OUTPUT "${opencv_test_java_bin_dir}/${f}"
-                     COMMAND ${CMAKE_COMMAND} -E copy_if_different "${test_dir}/${f}" "${opencv_test_java_bin_dir}/${f}"
-                     DEPENDS "${test_dir}/${f}"
-                     COMMENT "Copying ${f}"
-                    )
-  list(APPEND opencv_test_java_file_deps "${test_dir}/${f}" "${opencv_test_java_bin_dir}/${f}")
+  add_custom_command(OUTPUT "${OPENCV_JAVA_TEST_DIR}/${f}"
+      COMMAND ${CMAKE_COMMAND} -E copy_if_different "${test_dir}/${f}" "${OPENCV_JAVA_TEST_DIR}/${f}"
+      DEPENDS "${test_dir}/${f}"
+      COMMENT "Copying ${f}"
+  )
+  list(APPEND depends "${test_dir}/${f}" "${OPENCV_JAVA_TEST_DIR}/${f}")
 endforeach()
 
 # Copy the OpenCV jar after it has been generated.
-add_custom_command(OUTPUT "${opencv_test_java_bin_dir}/bin/${JAR_NAME}"
-                   COMMAND ${CMAKE_COMMAND} -E copy_if_different "${JAR_FILE}" "${opencv_test_java_bin_dir}/bin/${JAR_NAME}"
-                   DEPENDS "${JAR_FILE}"
-                   COMMENT "Copying the OpenCV jar"
-                  )
-
-add_custom_command(OUTPUT "${opencv_test_java_bin_dir}/build.xml"
-                   COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/build.xml" "${opencv_test_java_bin_dir}/build.xml"
-                   DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/build.xml"
-                   COMMENT "Copying build.xml"
-                  )
-
-add_custom_command(OUTPUT "${opencv_test_java_bin_dir}/build/jar/opencv-test.jar"
-                   COMMAND "${ANT_EXECUTABLE}" build
-                   WORKING_DIRECTORY "${opencv_test_java_bin_dir}"
-                   DEPENDS ${opencv_test_java_file_deps} "${opencv_test_java_bin_dir}/build.xml" "${CMAKE_CURRENT_SOURCE_DIR}/build.xml" "${JAR_FILE}" "${opencv_test_java_bin_dir}/bin/${JAR_NAME}"
-                   COMMENT "Build Java tests"
-                  )
-
-# Not add_custom_command because generator expressions aren't supported in
-# OUTPUT file names, and we need to generate different files for different
-# configurations.
-add_custom_target(${PROJECT_NAME}_properties
-                  COMMAND "${CMAKE_COMMAND}" -E echo "opencv.lib.path = $<TARGET_FILE_DIR:${the_module}>"
-                    > "${opencv_test_java_bin_dir}/ant-$<CONFIGURATION>.properties"
-                  VERBATIM
-                 )
+add_custom_command(OUTPUT "${OPENCV_JAVA_TEST_DIR}/bin/${JAR_NAME}"
+    COMMAND ${CMAKE_COMMAND} -E copy_if_different "${OPENCV_JAR_FILE}" "${OPENCV_JAVA_TEST_DIR}/bin/${JAR_NAME}"
+    DEPENDS "${OPENCV_JAR_FILE}" "${OPENCV_DEPHELPER}/${the_module}_jar"
+    COMMENT "Copying the OpenCV jar"
+)
+
+add_custom_command(OUTPUT "${OPENCV_JAVA_TEST_DIR}/build.xml"
+    COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/build.xml" "${OPENCV_JAVA_TEST_DIR}/build.xml"
+    DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/build.xml"
+    COMMENT "Copying build.xml"
+)
+
+add_custom_command(OUTPUT "${OPENCV_JAVA_TEST_DIR}/build/jar/opencv-test.jar"
+    COMMAND "${ANT_EXECUTABLE}" -noinput -k build
+    WORKING_DIRECTORY "${OPENCV_JAVA_TEST_DIR}"
+    DEPENDS ${depends} "${OPENCV_JAVA_TEST_DIR}/build.xml" "${CMAKE_CURRENT_SOURCE_DIR}/build.xml" "${OPENCV_JAR_FILE}" "${OPENCV_JAVA_TEST_DIR}/bin/${JAR_NAME}"
+    COMMENT "Build Java tests"
+)
+
+file(GENERATE OUTPUT "${OPENCV_JAVA_TEST_DIR}/ant-$<CONFIGURATION>.properties" CONTENT "opencv.lib.path=$<TARGET_FILE_DIR:${the_module}>")
 
 add_custom_target(${PROJECT_NAME} ALL
-                  DEPENDS ${the_module} ${PROJECT_NAME}_properties
-                  SOURCES "${opencv_test_java_bin_dir}/build/jar/opencv-test.jar"
-                 )
+    DEPENDS ${the_module} "${OPENCV_JAVA_TEST_DIR}/build/jar/opencv-test.jar"
+    SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/build.xml"
+)
 
 if(ENABLE_SOLUTION_FOLDERS)
   set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER "tests accuracy")
 endif()
+
+add_dependencies(opencv_tests ${PROJECT_NAME})
diff --git a/modules/java/test/pure_test/build.xml b/modules/java/test/pure_test/build.xml
index 6d7430634f..7729b41b2b 100644
--- a/modules/java/test/pure_test/build.xml
+++ b/modules/java/test/pure_test/build.xml
@@ -1,4 +1,4 @@
-<project>
+<project name="OpenCV-Test">
   <property environment="env"/>
   <property file="ant-${opencv.build.type}.properties"/>
   <property name="test.dir" value="testResults"/>
@@ -27,7 +27,7 @@
     </javac>
   </target>
 
-  <target name="jar">
+  <target name="jar" depends="compile">
     <mkdir dir="build/jar"/>
     <jar destfile="build/jar/opencv-test.jar" basedir="build/classes">
       <manifest>
@@ -36,7 +36,7 @@
     </jar>
   </target>
 
-  <target name="test">
+  <target name="test" depends="jar">
     <mkdir dir="${test.dir}"/>
     <junit printsummary="true" haltonfailure="false" haltonerror="false" showoutput="true" logfailedtests="true" maxmemory="256m">
       <sysproperty key="java.library.path" path="${opencv.lib.path}"/>
@@ -62,14 +62,9 @@
     </junitreport>
   </target>
 
-  <target name="build">
-    <antcall target="compile"/>
-    <antcall target="jar"/>
+  <target name="build" depends="jar">
   </target>
 
-  <target name="buildAndTest">
-    <antcall target="compile"/>
-    <antcall target="jar"/>
-    <antcall target="test"/>
+  <target name="buildAndTest" depends="test">
   </target>
 </project>
diff --git a/modules/ts/misc/run_suite.py b/modules/ts/misc/run_suite.py
index 8242c3a1db..64f4d7e418 100644
--- a/modules/ts/misc/run_suite.py
+++ b/modules/ts/misc/run_suite.py
@@ -127,7 +127,7 @@ class TestSuite(object):
         exe = os.path.abspath(path)
         if path == "java":
             cmd = [self.cache.ant_executable, "-Dopencv.build.type=%s" % self.cache.build_type, "buildAndTest"]
-            ret = execute(cmd, cwd = self.cache.java_test_binary_dir + "/.build")
+            ret = execute(cmd, cwd = self.cache.java_test_dir)
             return None, ret
         elif path in ['python2', 'python3']:
             executable = os.getenv('OPENCV_PYTHON_BINARY', None)
diff --git a/modules/ts/misc/run_utils.py b/modules/ts/misc/run_utils.py
index 8c07763d03..d7dbcf4f4e 100644
--- a/modules/ts/misc/run_utils.py
+++ b/modules/ts/misc/run_utils.py
@@ -168,7 +168,7 @@ parse_patterns = (
     {'name': "android_abi",              'default': None,       'pattern': re.compile(r"^ANDROID_ABI:STRING=(.*)$")},
     {'name': "android_executable",       'default': None,       'pattern': re.compile(r"^ANDROID_EXECUTABLE:FILEPATH=(.*android.*)$")},
     {'name': "ant_executable",           'default': None,       'pattern': re.compile(r"^ANT_EXECUTABLE:FILEPATH=(.*ant.*)$")},
-    {'name': "java_test_binary_dir",     'default': None,       'pattern': re.compile(r"^opencv_test_java_BINARY_DIR:STATIC=(.*)$")},
+    {'name': "java_test_dir",            'default': None,       'pattern': re.compile(r"^OPENCV_JAVA_TEST_DIR:\w+=(.*)$")},
     {'name': "is_x64",                   'default': "OFF",      'pattern': re.compile(r"^CUDA_64_BIT_DEVICE_CODE:BOOL=(ON)$")},#ugly(
     {'name': "cmake_generator",          'default': None,       'pattern': re.compile(r"^CMAKE_GENERATOR:INTERNAL=(.+)$")},
     {'name': "cxx_compiler",             'default': None,       'pattern': re.compile(r"^CMAKE_CXX_COMPILER:\w*PATH=(.+)$")},
@@ -269,7 +269,7 @@ class CMakeCache:
         return self.cuda_version and self.with_cuda == "ON" and self.cuda_library and not self.cuda_library.endswith("-NOTFOUND")
 
     def withJava(self):
-        return self.ant_executable and self.java_test_binary_dir
+        return self.ant_executable and os.path.exists(self.java_test_dir)
 
     def withPython2(self):
         return self.python2 == 'ON'
diff --git a/samples/android/15-puzzle/CMakeLists.txt b/samples/android/15-puzzle/CMakeLists.txt
index 4479319554..90a997f541 100644
--- a/samples/android/15-puzzle/CMakeLists.txt
+++ b/samples/android/15-puzzle/CMakeLists.txt
@@ -1,6 +1,6 @@
 set(sample example-15-puzzle)
 
-add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS ${OpenCV_BINARY_DIR} SDK_TARGET 11 ${ANDROID_SDK_TARGET})
+add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS "${OPENCV_ANDROID_LIB_DIR}" SDK_TARGET 11 "${ANDROID_SDK_TARGET}")
 if(TARGET ${sample})
   add_dependencies(opencv_android_examples ${sample})
 endif()
diff --git a/samples/android/camera-calibration/CMakeLists.txt b/samples/android/camera-calibration/CMakeLists.txt
index 83b11b3647..81483c6cc6 100644
--- a/samples/android/camera-calibration/CMakeLists.txt
+++ b/samples/android/camera-calibration/CMakeLists.txt
@@ -1,6 +1,6 @@
 set(sample example-camera-calibration)
 
-add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS ${OpenCV_BINARY_DIR} SDK_TARGET 11 ${ANDROID_SDK_TARGET})
+add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS "${OPENCV_ANDROID_LIB_DIR}" SDK_TARGET 11 "${ANDROID_SDK_TARGET}")
 if(TARGET ${sample})
   add_dependencies(opencv_android_examples ${sample})
 endif()
diff --git a/samples/android/color-blob-detection/CMakeLists.txt b/samples/android/color-blob-detection/CMakeLists.txt
index 42051bc3a2..3f59043104 100644
--- a/samples/android/color-blob-detection/CMakeLists.txt
+++ b/samples/android/color-blob-detection/CMakeLists.txt
@@ -1,6 +1,6 @@
 set(sample example-color-blob-detection)
 
-add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS ${OpenCV_BINARY_DIR} SDK_TARGET 11 ${ANDROID_SDK_TARGET})
+add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS "${OPENCV_ANDROID_LIB_DIR}" SDK_TARGET 11 "${ANDROID_SDK_TARGET}")
 if(TARGET ${sample})
   add_dependencies(opencv_android_examples ${sample})
 endif()
diff --git a/samples/android/face-detection/CMakeLists.txt b/samples/android/face-detection/CMakeLists.txt
index 0f89b2a362..bbcfa32f08 100644
--- a/samples/android/face-detection/CMakeLists.txt
+++ b/samples/android/face-detection/CMakeLists.txt
@@ -3,10 +3,10 @@ set(sample example-face-detection)
 if(BUILD_FAT_JAVA_LIB)
   set(native_deps opencv_java)
 else()
-  set(native_deps opencv_contrib)
+  set(native_deps opencv_objdetect)
 endif()
 
-add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS ${OpenCV_BINARY_DIR} SDK_TARGET 11 ${ANDROID_SDK_TARGET} NATIVE_DEPS ${native_deps})
+add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS "${OPENCV_ANDROID_LIB_DIR}" SDK_TARGET 11 "${ANDROID_SDK_TARGET}" NATIVE_DEPS ${native_deps})
 if(TARGET ${sample})
   add_dependencies(opencv_android_examples ${sample})
 endif()
diff --git a/samples/android/image-manipulations/CMakeLists.txt b/samples/android/image-manipulations/CMakeLists.txt
index 70adb1dbe1..bbb4cbc85d 100644
--- a/samples/android/image-manipulations/CMakeLists.txt
+++ b/samples/android/image-manipulations/CMakeLists.txt
@@ -1,6 +1,6 @@
 set(sample example-image-manipulations)
 
-add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS ${OpenCV_BINARY_DIR} SDK_TARGET 11 ${ANDROID_SDK_TARGET})
+add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS "${OPENCV_ANDROID_LIB_DIR}" SDK_TARGET 11 "${ANDROID_SDK_TARGET}")
 if(TARGET ${sample})
   add_dependencies(opencv_android_examples ${sample})
 endif()
diff --git a/samples/android/mobilenet-objdetect/CMakeLists.txt b/samples/android/mobilenet-objdetect/CMakeLists.txt
index 5aabd417ba..4e41ebfb28 100644
--- a/samples/android/mobilenet-objdetect/CMakeLists.txt
+++ b/samples/android/mobilenet-objdetect/CMakeLists.txt
@@ -1,6 +1,6 @@
 set(sample example-mobilenet-objdetect)
 
-add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS ${OpenCV_BINARY_DIR} SDK_TARGET 11 ${ANDROID_SDK_TARGET})
+add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS "${OPENCV_ANDROID_LIB_DIR}" SDK_TARGET 11 "${ANDROID_SDK_TARGET}")
 if(TARGET ${sample})
   add_dependencies(opencv_android_examples ${sample})
 endif()
diff --git a/samples/android/tutorial-1-camerapreview/CMakeLists.txt b/samples/android/tutorial-1-camerapreview/CMakeLists.txt
index 7e5c5cb1b5..8f8c25ae75 100644
--- a/samples/android/tutorial-1-camerapreview/CMakeLists.txt
+++ b/samples/android/tutorial-1-camerapreview/CMakeLists.txt
@@ -1,6 +1,6 @@
 set(sample example-tutorial-1-camerapreview)
 
-add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS ${OpenCV_BINARY_DIR} SDK_TARGET 11 ${ANDROID_SDK_TARGET})
+add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS "${OPENCV_ANDROID_LIB_DIR}" SDK_TARGET 11 "${ANDROID_SDK_TARGET}")
 if(TARGET ${sample})
   add_dependencies(opencv_android_examples ${sample})
 endif()
diff --git a/samples/android/tutorial-2-mixedprocessing/CMakeLists.txt b/samples/android/tutorial-2-mixedprocessing/CMakeLists.txt
index e8771ede1a..c254dd8f9a 100644
--- a/samples/android/tutorial-2-mixedprocessing/CMakeLists.txt
+++ b/samples/android/tutorial-2-mixedprocessing/CMakeLists.txt
@@ -6,7 +6,7 @@ else()
   set(native_deps opencv_features2d)
 endif()
 
-add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS ${OpenCV_BINARY_DIR} SDK_TARGET 11 ${ANDROID_SDK_TARGET} NATIVE_DEPS ${native_deps})
+add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS "${OPENCV_ANDROID_LIB_DIR}" SDK_TARGET 11 "${ANDROID_SDK_TARGET}" NATIVE_DEPS ${native_deps})
 if(TARGET ${sample})
   add_dependencies(opencv_android_examples ${sample})
 endif()
diff --git a/samples/android/tutorial-3-cameracontrol/CMakeLists.txt b/samples/android/tutorial-3-cameracontrol/CMakeLists.txt
index 3fd69c929c..69100a61a2 100644
--- a/samples/android/tutorial-3-cameracontrol/CMakeLists.txt
+++ b/samples/android/tutorial-3-cameracontrol/CMakeLists.txt
@@ -1,6 +1,6 @@
 set(sample example-tutorial-3-cameracontrol)
 
-add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS ${OpenCV_BINARY_DIR} SDK_TARGET 11 ${ANDROID_SDK_TARGET})
+add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS "${OPENCV_ANDROID_LIB_DIR}" SDK_TARGET 11 "${ANDROID_SDK_TARGET}")
 if(TARGET ${sample})
   add_dependencies(opencv_android_examples ${sample})
 endif()
diff --git a/samples/android/tutorial-4-opencl/CMakeLists.txt b/samples/android/tutorial-4-opencl/CMakeLists.txt
index e8036cfbd2..94e955fe36 100644
--- a/samples/android/tutorial-4-opencl/CMakeLists.txt
+++ b/samples/android/tutorial-4-opencl/CMakeLists.txt
@@ -17,8 +17,8 @@ endif()
 include_directories(${ANDROID_OPENCL_SDK}/include)
 link_directories(${ANDROID_OPENCL_SDK}/lib/${ANDROID_NDK_ABI_NAME})
 add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}"
-    LIBRARY_DEPS ${OpenCV_BINARY_DIR}
-    SDK_TARGET 21 ${ANDROID_SDK_TARGET}
+    LIBRARY_DEPS "${OPENCV_ANDROID_LIB_DIR}"
+    SDK_TARGET 21 "${ANDROID_SDK_TARGET}"
     NATIVE_DEPS ${native_deps} -lGLESv2 -lEGL -lOpenCL
     COPY_LIBS YES
 )