diff --git a/src/android/gen_kiwix.sh b/src/android/gen_kiwix.sh index c6fb46b5c..9140ae010 100755 --- a/src/android/gen_kiwix.sh +++ b/src/android/gen_kiwix.sh @@ -4,8 +4,13 @@ set -e BUILD_PATH=$(pwd) -javac -d $BUILD_PATH/src/android $1 $2 $3 $4 +echo "javac -d $BUILD_PATH/src/android $@" +javac -d $BUILD_PATH/src/android/ "$@" + cd $BUILD_PATH/src/android +echo "javah -jni org.kiwix.kiwixlib" javah -jni org.kiwix.kiwixlib.JNIKiwix +javah -jni org.kiwix.kiwixlib.JNIKiwixReader +javah -jni org.kiwix.kiwixlib.JNIKiwixSearcher cd $BUILD_PATH diff --git a/src/android/kiwix.cpp b/src/android/kiwix.cpp index 10c9bd484..1dfef6903 100644 --- a/src/android/kiwix.cpp +++ b/src/android/kiwix.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2013 Emmanuel Engelhart + * Copyright (C) 2017 Matthieu Gautier * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,540 +21,24 @@ #include #include "org_kiwix_kiwixlib_JNIKiwix.h" -#include -#include - #include #include -#include "common/base64.h" -#include "reader.h" -#include "searcher.h" #include "unicode/putil.h" -#include -#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "kiwix", __VA_ARGS__) +#include "utils.h" -#include -#include -#include -#include -#include - -/* global variables */ -kiwix::Reader* reader = NULL; -kiwix::Searcher* searcher = NULL; - -static pthread_mutex_t readerLock = PTHREAD_MUTEX_INITIALIZER; -static pthread_mutex_t searcherLock = PTHREAD_MUTEX_INITIALIZER; - -/* c2jni type conversion functions */ -jboolean c2jni(const bool& val) -{ - return val ? JNI_TRUE : JNI_FALSE; -} -jstring c2jni(const std::string& val, JNIEnv* env) -{ - return env->NewStringUTF(val.c_str()); -} - -jint c2jni(const int val) -{ - return (jint)val; -} -jint c2jni(const unsigned val) -{ - return (unsigned)val; -} -/* jni2c type conversion functions */ -bool jni2c(const jboolean& val) -{ - return val == JNI_TRUE; -} -std::string jni2c(const jstring& val, JNIEnv* env) -{ - return std::string(env->GetStringUTFChars(val, 0)); -} - -int jni2c(const jint val) -{ - return (int)val; -} -/* Method to deal with variable passed by reference */ -void setStringObjValue(const std::string& value, const jobject obj, JNIEnv* env) -{ - jclass objClass = env->GetObjectClass(obj); - jfieldID objFid = env->GetFieldID(objClass, "value", "Ljava/lang/String;"); - env->SetObjectField(obj, objFid, c2jni(value, env)); -} - -void setIntObjValue(const int value, const jobject obj, JNIEnv* env) -{ - jclass objClass = env->GetObjectClass(obj); - jfieldID objFid = env->GetFieldID(objClass, "value", "I"); - env->SetIntField(obj, objFid, value); -} - -void setBoolObjValue(const bool value, const jobject obj, JNIEnv* env) -{ - jclass objClass = env->GetObjectClass(obj); - jfieldID objFid = env->GetFieldID(objClass, "value", "Z"); - env->SetIntField(obj, objFid, c2jni(value)); -} - -/* Kiwix library functions */ -JNIEXPORT jstring JNICALL -Java_org_kiwix_kiwixlib_JNIKiwix_getMainPage(JNIEnv* env, jobject obj) -{ - jstring url; - - pthread_mutex_lock(&readerLock); - if (reader != NULL) { - try { - std::string cUrl = reader->getMainPageUrl(); - url = c2jni(cUrl, env); - } catch (...) { - std::cerr << "Unable to get ZIM main page" << std::endl; - } - } - pthread_mutex_unlock(&readerLock); - - return url; -} - -JNIEXPORT jstring JNICALL Java_org_kiwix_kiwixlib_JNIKiwix_getId(JNIEnv* env, - jobject obj) -{ - jstring id; - - pthread_mutex_lock(&readerLock); - if (reader != NULL) { - try { - std::string cId = reader->getId(); - id = c2jni(cId, env); - } catch (...) { - std::cerr << "Unable to get ZIM id" << std::endl; - } - } - pthread_mutex_unlock(&readerLock); - - return id; -} - -JNIEXPORT jint JNICALL Java_org_kiwix_kiwixlib_JNIKiwix_getFileSize(JNIEnv* env, - jobject obj) -{ - jint size; - - pthread_mutex_lock(&readerLock); - if (reader != NULL) { - try { - int cSize = reader->getFileSize(); - size = c2jni(cSize); - } catch (...) { - std::cerr << "Unable to get ZIM file size" << std::endl; - } - } - pthread_mutex_unlock(&readerLock); - - return size; -} - -JNIEXPORT jstring JNICALL -Java_org_kiwix_kiwixlib_JNIKiwix_getCreator(JNIEnv* env, jobject obj) -{ - jstring creator; - - pthread_mutex_lock(&readerLock); - if (reader != NULL) { - try { - std::string cCreator = reader->getCreator(); - creator = c2jni(cCreator, env); - } catch (...) { - std::cerr << "Unable to get ZIM creator" << std::endl; - } - } - pthread_mutex_unlock(&readerLock); - - return creator; -} - -JNIEXPORT jstring JNICALL -Java_org_kiwix_kiwixlib_JNIKiwix_getPublisher(JNIEnv* env, jobject obj) -{ - jstring publisher; - - pthread_mutex_lock(&readerLock); - if (reader != NULL) { - try { - std::string cPublisher = reader->getPublisher(); - publisher = c2jni(cPublisher, env); - } catch (...) { - std::cerr << "Unable to get ZIM creator" << std::endl; - } - } - pthread_mutex_unlock(&readerLock); - - return publisher; -} - -JNIEXPORT jstring JNICALL Java_org_kiwix_kiwixlib_JNIKiwix_getName(JNIEnv* env, - jobject obj) -{ - jstring name; - - pthread_mutex_lock(&readerLock); - if (reader != NULL) { - try { - std::string cName = reader->getName(); - name = c2jni(cName, env); - } catch (...) { - std::cerr << "Unable to get ZIM name" << std::endl; - } - } - pthread_mutex_unlock(&readerLock); - - return name; -} - -JNIEXPORT jstring JNICALL -Java_org_kiwix_kiwixlib_JNIKiwix_getFavicon(JNIEnv* env, jobject obj) -{ - jstring favicon; - - pthread_mutex_lock(&readerLock); - if (reader != NULL) { - try { - std::string cContent; - std::string cMime; - reader->getFavicon(cContent, cMime); - favicon - = c2jni(base64_encode( - reinterpret_cast(cContent.c_str()), - cContent.length()), - env); - } catch (...) { - std::cerr << "Unable to get ZIM favicon" << std::endl; - } - } - pthread_mutex_unlock(&readerLock); - - return favicon; -} - -JNIEXPORT jstring JNICALL Java_org_kiwix_kiwixlib_JNIKiwix_getDate(JNIEnv* env, - jobject obj) -{ - jstring date; - - pthread_mutex_lock(&readerLock); - if (reader != NULL) { - try { - std::string cDate = reader->getDate(); - date = c2jni(cDate, env); - } catch (...) { - std::cerr << "Unable to get ZIM date" << std::endl; - } - } - pthread_mutex_unlock(&readerLock); - - return date; -} - -JNIEXPORT jstring JNICALL -Java_org_kiwix_kiwixlib_JNIKiwix_getLanguage(JNIEnv* env, jobject obj) -{ - jstring language; - - pthread_mutex_lock(&readerLock); - if (reader != NULL) { - try { - std::string cLanguage = reader->getLanguage(); - language = c2jni(cLanguage, env); - } catch (...) { - std::cerr << "Unable to get ZIM language" << std::endl; - } - } - pthread_mutex_unlock(&readerLock); - - return language; -} - -JNIEXPORT jstring JNICALL Java_org_kiwix_kiwixlib_JNIKiwix_getMimeType( - JNIEnv* env, jobject obj, jstring url) -{ - jstring mimeType; - - pthread_mutex_lock(&readerLock); - if (reader != NULL) { - std::string cUrl = jni2c(url, env); - try { - std::string cMimeType; - reader->getMimeTypeByUrl(cUrl, cMimeType); - mimeType = c2jni(cMimeType, env); - } catch (...) { - std::cerr << "Unable to get mime-type for url " << cUrl << std::endl; - } - } - pthread_mutex_unlock(&readerLock); - - return mimeType; -} - -JNIEXPORT jboolean JNICALL -Java_org_kiwix_kiwixlib_JNIKiwix_loadZIM(JNIEnv* env, jobject obj, jstring path) -{ - jboolean retVal = JNI_TRUE; - std::string cPath = jni2c(path, env); - - pthread_mutex_lock(&readerLock); - try { - if (reader != NULL) { - delete reader; - } - reader = new kiwix::Reader(cPath); - } catch (...) { - std::cerr << "Unable to load ZIM " << cPath << std::endl; - reader = NULL; - retVal = JNI_FALSE; - } - pthread_mutex_unlock(&readerLock); - - return retVal; -} - -JNIEXPORT jbyteArray JNICALL Java_org_kiwix_kiwixlib_JNIKiwix_getContent( - JNIEnv* env, jobject obj, jstring url, jobject titleObj, jobject mimeTypeObj, jobject sizeObj) -{ - /* Default values */ - setStringObjValue("", titleObj, env); - setStringObjValue("", mimeTypeObj, env); - setIntObjValue(0, sizeObj, env); - jbyteArray data = env->NewByteArray(0); - - /* Retrieve the content */ - if (reader != NULL) { - std::string cUrl = jni2c(url, env); - std::string cData; - std::string cTitle; - std::string cMimeType; - unsigned int cSize = 0; - - pthread_mutex_lock(&readerLock); - try { - if (reader->getContentByUrl(cUrl, cData, cTitle, cSize, cMimeType)) { - data = env->NewByteArray(cSize); - env->SetByteArrayRegion( - data, 0, cSize, reinterpret_cast(cData.c_str())); - setStringObjValue(cMimeType, mimeTypeObj, env); - setStringObjValue(cTitle, titleObj, env); - setIntObjValue(cSize, sizeObj, env); - } - } catch (...) { - std::cerr << "Unable to get content for url " << cUrl << std::endl; - } - pthread_mutex_unlock(&readerLock); - } - - return data; -} - -JNIEXPORT jboolean JNICALL Java_org_kiwix_kiwixlib_JNIKiwix_searchSuggestions( - JNIEnv* env, jobject obj, jstring prefix, jint count) -{ - jboolean retVal = JNI_FALSE; - std::string cPrefix = jni2c(prefix, env); - unsigned int cCount = jni2c(count); - - pthread_mutex_lock(&readerLock); - try { - if (reader != NULL) { - if (reader->searchSuggestionsSmart(cPrefix, cCount)) { - retVal = JNI_TRUE; - } - } - } catch (...) { - std::cerr << "Unable to search suggestions for pattern " << cPrefix - << std::endl; - } - pthread_mutex_unlock(&readerLock); - - return retVal; -} - -JNIEXPORT jboolean JNICALL Java_org_kiwix_kiwixlib_JNIKiwix_getNextSuggestion( - JNIEnv* env, jobject obj, jobject titleObj) -{ - jboolean retVal = JNI_FALSE; - std::string cTitle; - - pthread_mutex_lock(&readerLock); - try { - if (reader != NULL) { - if (reader->getNextSuggestion(cTitle)) { - setStringObjValue(cTitle, titleObj, env); - retVal = JNI_TRUE; - } - } - } catch (...) { - std::cerr << "Unable to get next suggestion" << std::endl; - } - pthread_mutex_unlock(&readerLock); - - return retVal; -} - -JNIEXPORT jboolean JNICALL Java_org_kiwix_kiwixlib_JNIKiwix_getPageUrlFromTitle( - JNIEnv* env, jobject obj, jstring title, jobject urlObj) -{ - jboolean retVal = JNI_FALSE; - std::string cTitle = jni2c(title, env); - std::string cUrl; - - pthread_mutex_lock(&readerLock); - try { - if (reader != NULL) { - if (reader->getPageUrlFromTitle(cTitle, cUrl)) { - setStringObjValue(cUrl, urlObj, env); - retVal = JNI_TRUE; - } - } - } catch (...) { - std::cerr << "Unable to get URL for title " << cTitle << std::endl; - } - pthread_mutex_unlock(&readerLock); - - return retVal; -} - -JNIEXPORT jboolean JNICALL Java_org_kiwix_kiwixlib_JNIKiwix_getTitle( - JNIEnv* env, jobject obj, jobject titleObj) -{ - jboolean retVal = JNI_FALSE; - std::string cTitle; - - pthread_mutex_lock(&readerLock); - try { - if (reader != NULL) { - std::string cTitle = reader->getTitle(); - setStringObjValue(cTitle, titleObj, env); - retVal = JNI_TRUE; - } - } catch (...) { - std::cerr << "Unable to get ZIM title" << std::endl; - } - pthread_mutex_unlock(&readerLock); - - return retVal; -} - -JNIEXPORT jstring JNICALL -Java_org_kiwix_kiwixlib_JNIKiwix_getDescription(JNIEnv* env, jobject obj) -{ - jstring description; - - pthread_mutex_lock(&readerLock); - if (reader != NULL) { - try { - std::string cDescription = reader->getDescription(); - description = c2jni(cDescription, env); - } catch (...) { - std::cerr << "Unable to get ZIM description" << std::endl; - } - } - pthread_mutex_unlock(&readerLock); - - return description; -} - -JNIEXPORT jboolean JNICALL Java_org_kiwix_kiwixlib_JNIKiwix_getRandomPage( - JNIEnv* env, jobject obj, jobject urlObj) -{ - jboolean retVal = JNI_FALSE; - std::string cUrl; - - pthread_mutex_lock(&readerLock); - try { - if (reader != NULL) { - std::string cUrl = reader->getRandomPageUrl(); - setStringObjValue(cUrl, urlObj, env); - retVal = JNI_TRUE; - } - } catch (...) { - std::cerr << "Unable to get random page" << std::endl; - } - pthread_mutex_unlock(&readerLock); - - return retVal; -} +pthread_mutex_t globalLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIKiwix_setDataDirectory( JNIEnv* env, jobject obj, jstring dirStr) { std::string cPath = jni2c(dirStr, env); - pthread_mutex_lock(&readerLock); + Lock l; try { u_setDataDirectory(cPath.c_str()); } catch (...) { std::cerr << "Unable to set data directory " << cPath << std::endl; } - pthread_mutex_unlock(&readerLock); -} - -JNIEXPORT jboolean JNICALL Java_org_kiwix_kiwixlib_JNIKiwix_loadFulltextIndex( - JNIEnv* env, jobject obj, jstring path) -{ - jboolean retVal = JNI_TRUE; - std::string cPath = jni2c(path, env); - - pthread_mutex_lock(&searcherLock); - try { - if (searcher != NULL) { - delete searcher; - } - if (!reader || !reader->hasFulltextIndex()) { - // Use old API (no embedded full text index). - searcher = new kiwix::Searcher(cPath, reader, ""); - } else { - // Use the new API. We don't care about the human readable name as - // we don't use it (in android). - searcher = new kiwix::Searcher(); - searcher->add_reader(reader, ""); - } - } catch (...) { - searcher = NULL; - retVal = JNI_FALSE; - std::cerr << "Unable to load full text index " << cPath << std::endl; - } - pthread_mutex_unlock(&searcherLock); - - return retVal; -} - -JNIEXPORT jstring JNICALL Java_org_kiwix_kiwixlib_JNIKiwix_indexedQuery( - JNIEnv* env, jclass obj, jstring query, jint count) -{ - std::string cQuery = jni2c(query, env); - unsigned int cCount = jni2c(count); - kiwix::Result* p_result; - std::string result; - - pthread_mutex_lock(&searcherLock); - try { - if (searcher != NULL) { - searcher->search(cQuery, 0, count); - while ((p_result = searcher->getNextResult()) - && !(p_result->get_title().empty()) - && !(p_result->get_url().empty())) { - result += p_result->get_title() + "\n"; - delete p_result; - } - } - } catch (...) { - std::cerr << "Unable to make indexed query " << cQuery << std::endl; - } - pthread_mutex_unlock(&searcherLock); - - return env->NewStringUTF(result.c_str()); } diff --git a/src/android/kiwixreader.cpp b/src/android/kiwixreader.cpp new file mode 100644 index 000000000..e50e100ca --- /dev/null +++ b/src/android/kiwixreader.cpp @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2013 Emmanuel Engelhart + * Copyright (C) 2017 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +#include +#include +#include "org_kiwix_kiwixlib_JNIKiwixReader.h" + +#include "common/base64.h" +#include "reader.h" +#include "utils.h" + +/* Kiwix Reader JNI functions */ +JNIEXPORT jlong JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getNativeReader( + JNIEnv* env, jobject obj, jstring filename) +{ + std::string cPath = jni2c(filename, env); + + Lock l; + kiwix::Reader* reader = nullptr; + try { + reader = new kiwix::Reader(cPath); + } catch (...) { + std::cerr << "Unable to load ZIM " << cPath << std::endl; + reader = NULL; + } + + return reinterpret_cast(new Handle(reader)); +} + +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_dispose(JNIEnv* env, jobject obj) +{ + Handle::dispose(env, obj); +} + +#define READER (Handle::getHandle(env, obj)) + +/* Kiwix library functions */ +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getMainPage(JNIEnv* env, jobject obj) +{ + jstring url; + + try { + std::string cUrl = READER->getMainPageUrl(); + url = c2jni(cUrl, env); + } catch (...) { + std::cerr << "Unable to get ZIM main page" << std::endl; + } + return url; +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getId(JNIEnv* env, jobject obj) +{ + jstring id; + + try { + std::string cId = READER->getId(); + id = c2jni(cId, env); + } catch (...) { + std::cerr << "Unable to get ZIM id" << std::endl; + } + + return id; +} + +JNIEXPORT jint JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getFileSize(JNIEnv* env, jobject obj) +{ + jint size; + + try { + int cSize = READER->getFileSize(); + size = c2jni(cSize); + } catch (...) { + std::cerr << "Unable to get ZIM file size" << std::endl; + } + + return size; +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getCreator(JNIEnv* env, jobject obj) +{ + jstring creator; + + try { + std::string cCreator = READER->getCreator(); + creator = c2jni(cCreator, env); + } catch (...) { + std::cerr << "Unable to get ZIM creator" << std::endl; + } + + return creator; +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getPublisher(JNIEnv* env, jobject obj) +{ + jstring publisher; + + try { + std::string cPublisher = READER->getPublisher(); + publisher = c2jni(cPublisher, env); + } catch (...) { + std::cerr << "Unable to get ZIM creator" << std::endl; + } + return publisher; +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getName(JNIEnv* env, jobject obj) +{ + jstring name; + + try { + std::string cName = READER->getName(); + name = c2jni(cName, env); + } catch (...) { + std::cerr << "Unable to get ZIM name" << std::endl; + } + return name; +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getFavicon(JNIEnv* env, jobject obj) +{ + jstring favicon; + + try { + std::string cContent; + std::string cMime; + READER->getFavicon(cContent, cMime); + favicon = c2jni( + base64_encode(reinterpret_cast(cContent.c_str()), + cContent.length()), + env); + } catch (...) { + std::cerr << "Unable to get ZIM favicon" << std::endl; + } + return favicon; +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getDate(JNIEnv* env, jobject obj) +{ + jstring date; + + try { + std::string cDate = READER->getDate(); + date = c2jni(cDate, env); + } catch (...) { + std::cerr << "Unable to get ZIM date" << std::endl; + } + return date; +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getLanguage(JNIEnv* env, jobject obj) +{ + jstring language; + + try { + std::string cLanguage = READER->getLanguage(); + language = c2jni(cLanguage, env); + } catch (...) { + std::cerr << "Unable to get ZIM language" << std::endl; + } + + return language; +} + +JNIEXPORT jstring JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getMimeType( + JNIEnv* env, jobject obj, jstring url) +{ + jstring mimeType; + + std::string cUrl = jni2c(url, env); + try { + std::string cMimeType; + READER->getMimeTypeByUrl(cUrl, cMimeType); + mimeType = c2jni(cMimeType, env); + } catch (...) { + std::cerr << "Unable to get mime-type for url " << cUrl << std::endl; + } + return mimeType; +} + +JNIEXPORT jbyteArray JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getContent( + JNIEnv* env, jobject obj, jstring url, jobject titleObj, jobject mimeTypeObj, jobject sizeObj) +{ + /* Default values */ + setStringObjValue("", titleObj, env); + setStringObjValue("", mimeTypeObj, env); + setIntObjValue(0, sizeObj, env); + jbyteArray data = env->NewByteArray(0); + + /* Retrieve the content */ + std::string cUrl = jni2c(url, env); + std::string cData; + std::string cTitle; + std::string cMimeType; + unsigned int cSize = 0; + + try { + if (READER->getContentByUrl(cUrl, cData, cTitle, cSize, cMimeType)) { + data = env->NewByteArray(cSize); + env->SetByteArrayRegion( + data, 0, cSize, reinterpret_cast(cData.c_str())); + setStringObjValue(cMimeType, mimeTypeObj, env); + setStringObjValue(cTitle, titleObj, env); + setIntObjValue(cSize, sizeObj, env); + } + } catch (...) { + std::cerr << "Unable to get content for url " << cUrl << std::endl; + } + + return data; +} + +JNIEXPORT jboolean JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_searchSuggestions(JNIEnv* env, + jobject obj, + jstring prefix, + jint count) +{ + jboolean retVal = JNI_FALSE; + std::string cPrefix = jni2c(prefix, env); + unsigned int cCount = jni2c(count); + + try { + if (READER->searchSuggestionsSmart(cPrefix, cCount)) { + retVal = JNI_TRUE; + } + } catch (...) { + std::cerr << "Unable to search suggestions for pattern " << cPrefix + << std::endl; + } + + return retVal; +} + +JNIEXPORT jboolean JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getNextSuggestion(JNIEnv* env, + jobject obj, + jobject titleObj) +{ + jboolean retVal = JNI_FALSE; + std::string cTitle; + + try { + if (READER->getNextSuggestion(cTitle)) { + setStringObjValue(cTitle, titleObj, env); + retVal = JNI_TRUE; + } + } catch (...) { + std::cerr << "Unable to get next suggestion" << std::endl; + } + + return retVal; +} + +JNIEXPORT jboolean JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getPageUrlFromTitle(JNIEnv* env, + jobject obj, + jstring title, + jobject urlObj) +{ + jboolean retVal = JNI_FALSE; + std::string cTitle = jni2c(title, env); + std::string cUrl; + + try { + if (READER->getPageUrlFromTitle(cTitle, cUrl)) { + setStringObjValue(cUrl, urlObj, env); + retVal = JNI_TRUE; + } + } catch (...) { + std::cerr << "Unable to get URL for title " << cTitle << std::endl; + } + + return retVal; +} + +JNIEXPORT jstring JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getTitle( + JNIEnv* env, jobject obj) +{ + jstring title; + + try { + std::string cTitle = READER->getTitle(); + title = c2jni(cTitle, env); + } catch (...) { + std::cerr << "Unable to get ZIM title" << std::endl; + } + return title; +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixReader_getDescription(JNIEnv* env, jobject obj) +{ + jstring description; + + try { + std::string cDescription = READER->getDescription(); + description = c2jni(cDescription, env); + } catch (...) { + std::cerr << "Unable to get ZIM description" << std::endl; + } + return description; +} + +JNIEXPORT jboolean JNICALL Java_org_kiwix_kiwixlib_JNIKiwixReader_getRandomPage( + JNIEnv* env, jobject obj, jobject urlObj) +{ + jboolean retVal = JNI_FALSE; + std::string cUrl; + + try { + std::string cUrl = READER->getRandomPageUrl(); + setStringObjValue(cUrl, urlObj, env); + retVal = JNI_TRUE; + } catch (...) { + std::cerr << "Unable to get random page" << std::endl; + } + + return retVal; +} diff --git a/src/android/kiwixsearcher.cpp b/src/android/kiwixsearcher.cpp new file mode 100644 index 000000000..317c50e8a --- /dev/null +++ b/src/android/kiwixsearcher.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2013 Emmanuel Engelhart + * Copyright (C) 2017 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +#include +#include "org_kiwix_kiwixlib_JNIKiwixSearcher.h" +#include "org_kiwix_kiwixlib_JNIKiwixSearcher_Result.h" + +#include "reader.h" +#include "searcher.h" +#include "utils.h" + +#define SEARCHER (Handle::getHandle(env, obj)) +#define RESULT (Handle::getHandle(env, obj)) + + +JNIEXPORT void JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixSearcher_dispose(JNIEnv* env, jobject obj) +{ + Handle::dispose(env, obj); +} + +/* Kiwix Reader JNI functions */ +JNIEXPORT jlong JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixSearcher_getNativeHandle(JNIEnv* env, + jobject obj) +{ + kiwix::Searcher* searcher = new kiwix::Searcher(); + return reinterpret_cast(new Handle(searcher)); +} + +/* Kiwix library functions */ +JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIKiwixSearcher_addReader( + JNIEnv* env, jobject obj, jobject reader) +{ + auto searcher = SEARCHER; + + searcher->add_reader(*(Handle::getHandle(env, reader)), ""); +} + +JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIKiwixSearcher_search( + JNIEnv* env, jobject obj, jstring query, jint count) +{ + std::string cquery = jni2c(query, env); + unsigned int ccount = jni2c(count); + + SEARCHER->search(cquery, 0, ccount); +} + +JNIEXPORT jobject JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixSearcher_getNextResult(JNIEnv* env, + jobject obj) +{ + jobject result = nullptr; + + kiwix::Result* cresult = SEARCHER->getNextResult(); + if (cresult != nullptr) { + jclass resultclass + = env->FindClass("org/kiwix/kiwixlib/JNIKiwixSearcher$Result"); + jmethodID ctor = env->GetMethodID( + resultclass, "", "(Lorg/kiwix/kiwixlib/JNIKiwixSearcher;JLorg/kiwix/kiwixlib/JNIKiwixSearcher;)V"); + result = env->NewObject(resultclass, ctor, obj, reinterpret_cast(new Handle(cresult)), obj); + } + return result; +} + +JNIEXPORT void JNICALL Java_org_kiwix_kiwixlib_JNIKiwixSearcher_00024Result_dispose( + JNIEnv* env, jobject obj) +{ + Handle::dispose(env, obj); +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixSearcher_00024Result_getUrl(JNIEnv* env, + jobject obj) +{ + try { + return c2jni(RESULT->get_url(), env); + } catch (...) { + return nullptr; + } +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixSearcher_00024Result_getTitle(JNIEnv* env, + jobject obj) +{ + try { + return c2jni(RESULT->get_title(), env); + } catch (...) { + return nullptr; + } +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixSearcher_00024Result_getSnippet(JNIEnv* env, + jobject obj) +{ + return c2jni(RESULT->get_snippet(), env); +} + +JNIEXPORT jstring JNICALL +Java_org_kiwix_kiwixlib_JNIKiwixSearcher_00024Result_getContent(JNIEnv* env, + jobject obj) +{ + return c2jni(RESULT->get_content(), env); +} diff --git a/src/android/meson.build b/src/android/meson.build index bb9bc8861..be6aeab24 100644 --- a/src/android/meson.build +++ b/src/android/meson.build @@ -3,14 +3,23 @@ jni_generator = find_program('gen_kiwix.sh') kiwix_jni = custom_target('jni', input: ['org/kiwix/kiwixlib/JNIKiwix.java', + 'org/kiwix/kiwixlib/JNIKiwixReader.java', + 'org/kiwix/kiwixlib/JNIKiwixSearcher.java', 'org/kiwix/kiwixlib/JNIKiwixInt.java', 'org/kiwix/kiwixlib/JNIKiwixString.java', 'org/kiwix/kiwixlib/JNIKiwixBool.java'], - output: ['org_kiwix_kiwixlib_JNIKiwix.h'], + output: ['org_kiwix_kiwixlib_JNIKiwix.h', + 'org_kiwix_kiwixlib_JNIKiwixReader.h', + 'org_kiwix_kiwixlib_JNIKiwixSearcher.h', + 'org_kiwix_kiwixlib_JNIKiwixSearcher_Result.h'], command:[jni_generator, '@INPUT@'] ) -kiwix_sources += ['android/kiwix.cpp', kiwix_jni] +kiwix_sources += [ + 'android/kiwix.cpp', + 'android/kiwixreader.cpp', + 'android/kiwixsearcher.cpp', + kiwix_jni] install_subdir('org', install_dir: 'kiwix-lib/java') install_subdir('res', install_dir: 'kiwix-lib') diff --git a/src/android/org/kiwix/kiwixlib/JNIKiwix.java b/src/android/org/kiwix/kiwixlib/JNIKiwix.java index 0c87396ab..0d79db697 100644 --- a/src/android/org/kiwix/kiwixlib/JNIKiwix.java +++ b/src/android/org/kiwix/kiwixlib/JNIKiwix.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2013 Emmanuel Engelhart + * Copyright (C) 2017 Matthieu Gautier * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,56 +20,12 @@ package org.kiwix.kiwixlib; +import org.kiwix.kiwixlib.JNIKiwixReader; import org.kiwix.kiwixlib.JNIKiwixString; -import org.kiwix.kiwixlib.JNIKiwixBool; -import org.kiwix.kiwixlib.JNIKiwixInt; public class JNIKiwix { static { System.loadLibrary("kiwix"); } - public native String getMainPage(); - - public native String getId(); - - public native String getLanguage(); - - public native String getMimeType(String url); - - public native boolean loadZIM(String path); - - public native boolean loadFulltextIndex(String path); - - public native byte[] getContent(String url, JNIKiwixString title, JNIKiwixString mimeType, JNIKiwixInt size); - - public native boolean searchSuggestions(String prefix, int count); - - public native boolean getNextSuggestion(JNIKiwixString title); - - public native boolean getPageUrlFromTitle(String title, JNIKiwixString url); - - public native boolean getTitle(JNIKiwixString title); - - public native String getDescription(); - - public native String getDate(); - - public native String getFavicon(); - - public native String getCreator(); - - public native String getPublisher(); - - public native String getName(); - - public native int getFileSize(); - - public native int getArticleCount(); - - public native int getMediaCount(); - - public native boolean getRandomPage(JNIKiwixString url); public native void setDataDirectory(String icuDataDir); - - public static native String indexedQuery(String db, int count); } diff --git a/src/android/org/kiwix/kiwixlib/JNIKiwixReader.java b/src/android/org/kiwix/kiwixlib/JNIKiwixReader.java new file mode 100644 index 000000000..db416d4bc --- /dev/null +++ b/src/android/org/kiwix/kiwixlib/JNIKiwixReader.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2013 Emmanuel Engelhart + * Copyright (C) 2017 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +package org.kiwix.kiwixlib; + +import org.kiwix.kiwixlib.JNIKiwixString; +import org.kiwix.kiwixlib.JNIKiwixInt; +import org.kiwix.kiwixlib.JNIKiwixSearcher; + +public class JNIKiwixReader +{ + public native String getMainPage(); + + public native String getTitle(); + + public native String getId(); + + public native String getLanguage(); + + public native String getMimeType(String url); + + public native byte[] getContent(String url, + JNIKiwixString title, + JNIKiwixString mimeType, + JNIKiwixInt size); + + public native boolean searchSuggestions(String prefix, int count); + + public native boolean getNextSuggestion(JNIKiwixString title); + + public native boolean getPageUrlFromTitle(String title, JNIKiwixString url); + + public native String getDescription(); + + public native String getDate(); + + public native String getFavicon(); + + public native String getCreator(); + + public native String getPublisher(); + + public native String getName(); + + public native int getFileSize(); + + public native int getArticleCount(); + + public native int getMediaCount(); + + public native boolean getRandomPage(JNIKiwixString url); + + public JNIKiwixSearcher search(String query, int count) + { + JNIKiwixSearcher searcher = new JNIKiwixSearcher(); + searcher.addKiwixReader(this); + searcher.search(query, count); + return searcher; + } + + public JNIKiwixReader(String filename) + { + nativeHandle = getNativeReader(filename); + } + public JNIKiwixReader() { + + } + public native void dispose(); + + private native long getNativeReader(String filename); + private long nativeHandle; +} diff --git a/src/android/org/kiwix/kiwixlib/JNIKiwixSearcher.java b/src/android/org/kiwix/kiwixlib/JNIKiwixSearcher.java new file mode 100644 index 000000000..ea21d8fe3 --- /dev/null +++ b/src/android/org/kiwix/kiwixlib/JNIKiwixSearcher.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2013 Emmanuel Engelhart + * Copyright (C) 2017 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +package org.kiwix.kiwixlib; + +import org.kiwix.kiwixlib.JNIKiwixReader; +import java.util.Vector; + +public class JNIKiwixSearcher +{ + public class Result + { + private long nativeHandle; + private JNIKiwixSearcher searcher; + public Result(long handle, JNIKiwixSearcher _searcher) + { + nativeHandle = handle; + searcher = _searcher; + } + public native String getUrl(); + public native String getTitle(); + public native String getContent(); + public native void dispose(); + } + + public JNIKiwixSearcher() + { + nativeHandle = getNativeHandle(); + usedReaders = new Vector(); + } + public native void dispose(); + + private native long getNativeHandle(); + private long nativeHandle; + private Vector usedReaders; + + public native void addReader(JNIKiwixReader reader); + public void addKiwixReader(JNIKiwixReader reader) + { + addReader(reader); + usedReaders.addElement(reader); + }; + + public native void search(String query, int count); + + public native Result getNextResult(); + public native boolean hasMoreResult(); +} diff --git a/src/android/utils.h b/src/android/utils.h new file mode 100644 index 000000000..f61114730 --- /dev/null +++ b/src/android/utils.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2013 Emmanuel Engelhart + * Copyright (C) 2017 Matthieu Gautier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +#ifndef _ANDROID_JNI_UTILS_H +#define _ANDROID_JNI_UTILS_H + +#include + +#include +#include + +extern pthread_mutex_t globalLock; + +inline jfieldID getHandleField(JNIEnv* env, jobject obj) +{ + jclass c = env->GetObjectClass(obj); + // J is the type signature for long: + return env->GetFieldID(c, "nativeHandle", "J"); +} + +class Lock +{ + protected: + pthread_mutex_t* lock; + + public: + Lock() : lock(&globalLock) { pthread_mutex_lock(lock); } + Lock(const Lock&) = delete; + Lock& operator=(const Lock&) = delete; + Lock(Lock&& other) : lock(&globalLock) { other.lock = nullptr; } + virtual ~Lock() + { + if (lock) { + pthread_mutex_unlock(lock); + } + } +}; + +template +class LockedHandle; + +template +class Handle +{ + protected: + T* h; + + public: + Handle(T* h) : h(h){}; + + // No destructor. This must and will be handled by dispose method. + + static LockedHandle getHandle(JNIEnv* env, jobject obj) + { + jlong handle = env->GetLongField(obj, getHandleField(env, obj)); + return LockedHandle(reinterpret_cast*>(handle)); + } + + static void dispose(JNIEnv* env, jobject obj) + { + auto lHandle = getHandle(env, obj); + auto handle = lHandle.h; + delete handle->h; + delete handle; + } + friend class LockedHandle; +}; + +template +struct LockedHandle : public Lock { + Handle* h; + LockedHandle(Handle* h) : h(h) {} + T* operator->() { return h->h; } + T* operator*() { return h->h; } + operator bool() const { return (h->h != nullptr); } +}; + +/* c2jni type conversion functions */ +inline jboolean c2jni(const bool& val) { return val ? JNI_TRUE : JNI_FALSE; } +inline jstring c2jni(const std::string& val, JNIEnv* env) +{ + return env->NewStringUTF(val.c_str()); +} + +inline jint c2jni(const int val) { return (jint)val; } +inline jint c2jni(const unsigned val) { return (unsigned)val; } +/* jni2c type conversion functions */ +inline bool jni2c(const jboolean& val) { return val == JNI_TRUE; } +inline std::string jni2c(const jstring& val, JNIEnv* env) +{ + const char* chars = env->GetStringUTFChars(val, 0); + std::string ret(chars); + env->ReleaseStringUTFChars(val, chars); + return ret; +} + +inline int jni2c(const jint val) { return (int)val; } +/* Method to deal with variable passed by reference */ +inline void setStringObjValue(const std::string& value, + const jobject obj, + JNIEnv* env) +{ + jclass objClass = env->GetObjectClass(obj); + jfieldID objFid = env->GetFieldID(objClass, "value", "Ljava/lang/String;"); + env->SetObjectField(obj, objFid, c2jni(value, env)); +} + +inline void setIntObjValue(const int value, const jobject obj, JNIEnv* env) +{ + jclass objClass = env->GetObjectClass(obj); + jfieldID objFid = env->GetFieldID(objClass, "value", "I"); + env->SetIntField(obj, objFid, value); +} + +inline void setBoolObjValue(const bool value, const jobject obj, JNIEnv* env) +{ + jclass objClass = env->GetObjectClass(obj); + jfieldID objFid = env->GetFieldID(objClass, "value", "Z"); + env->SetIntField(obj, objFid, c2jni(value)); +} + +#endif // _ANDROID_JNI_UTILS_H