#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "AndroidHelper.h" #include #include QAndroidJniObject __interfaceActivity; QAndroidJniObject __loginCompletedListener; QAndroidJniObject __loadCompleteListener; QAndroidJniObject __usernameChangedListener; void tempMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { if (!message.isEmpty()) { const char * local=message.toStdString().c_str(); switch (type) { case QtDebugMsg: __android_log_write(ANDROID_LOG_DEBUG,"Interface",local); break; case QtInfoMsg: __android_log_write(ANDROID_LOG_INFO,"Interface",local); break; case QtWarningMsg: __android_log_write(ANDROID_LOG_WARN,"Interface",local); break; case QtCriticalMsg: __android_log_write(ANDROID_LOG_ERROR,"Interface",local); break; case QtFatalMsg: default: __android_log_write(ANDROID_LOG_FATAL,"Interface",local); abort(); } } } AAssetManager* g_assetManager = nullptr; void withAssetData(const char* filename, const std::function& callback) { auto asset = AAssetManager_open(g_assetManager, filename, AASSET_MODE_BUFFER); if (!asset) { throw std::runtime_error("Failed to open file"); } auto buffer = AAsset_getBuffer(asset); off64_t size = AAsset_getLength64(asset); callback(size, buffer); AAsset_close(asset); } QStringList readAssetLines(const char* filename) { QStringList result; withAssetData(filename, [&](off64_t size, const void* data){ QByteArray buffer = QByteArray::fromRawData((const char*)data, size); { QTextStream in(&buffer); while (!in.atEnd()) { QString line = in.readLine(); result << line; } } }); return result; } void copyAsset(const char* sourceAsset, const QString& destFilename) { withAssetData(sourceAsset, [&](off64_t size, const void* data){ QFile file(destFilename); if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) { throw std::runtime_error("Unable to open output file for writing"); } if (!file.resize(size)) { throw std::runtime_error("Unable to resize output file"); } if (size != 0) { auto mapped = file.map(0, size); if (!mapped) { throw std::runtime_error("Unable to map output file"); } memcpy(mapped, data, size); } file.close(); }); } void unpackAndroidAssets() { const QString DEST_PREFIX = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/"; QStringList filesToCopy = readAssetLines("cache_assets.txt"); QString dateStamp = filesToCopy.takeFirst(); QString dateStampFilename = DEST_PREFIX + dateStamp; qDebug() << "Checking date stamp" << dateStamp; if (QFileInfo(dateStampFilename).exists()) { return; } auto rootDir = QDir::root(); for (const auto& fileToCopy : filesToCopy) { auto destFileName = DEST_PREFIX + fileToCopy; auto destFolder = QFileInfo(destFileName).absoluteDir(); if (!destFolder.exists()) { qDebug() << "Creating folder" << destFolder.absolutePath(); if (!rootDir.mkpath(destFolder.absolutePath())) { throw std::runtime_error("Error creating output path"); } } if (QFile::exists(destFileName)) { qDebug() << "Removing old file" << destFileName; if (!QFile::remove(destFileName)) { throw std::runtime_error("Failed to remove existing file"); } } qDebug() << "Copying asset " << fileToCopy << "to" << destFileName; copyAsset(fileToCopy.toStdString().c_str(), destFileName); } { qDebug() << "Writing date stamp" << dateStamp; QFile file(dateStampFilename); if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) { throw std::runtime_error("Can't write date stamp"); } QTextStream(&file) << "touch" << endl; file.close(); } } extern "C" { JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnCreate(JNIEnv* env, jobject obj, jobject instance, jobject asset_mgr) { g_assetManager = AAssetManager_fromJava(env, asset_mgr); qRegisterMetaType("QAndroidJniObject"); __interfaceActivity = QAndroidJniObject(instance); auto oldMessageHandler = qInstallMessageHandler(tempMessageHandler); unpackAndroidAssets(); qInstallMessageHandler(oldMessageHandler); QObject::connect(&AndroidHelper::instance(), &AndroidHelper::androidActivityRequested, [](const QString& a, const bool backToScene) { QAndroidJniObject string = QAndroidJniObject::fromString(a); jboolean jBackToScene = (jboolean) backToScene; __interfaceActivity.callMethod("openAndroidActivity", "(Ljava/lang/String;Z)V", string.object(), jBackToScene); }); QObject::connect(&AndroidHelper::instance(), &AndroidHelper::hapticFeedbackRequested, [](int duration) { jint iDuration = (jint) duration; __interfaceActivity.callMethod("performHapticFeedback", "(I)V", iDuration); }); } JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnDestroy(JNIEnv* env, jobject obj) { QObject::disconnect(&AndroidHelper::instance(), &AndroidHelper::androidActivityRequested, nullptr, nullptr); } JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeGotoUrl(JNIEnv* env, jobject obj, jstring url) { QAndroidJniObject jniUrl("java/lang/String", "(Ljava/lang/String;)V", url); DependencyManager::get()->loadSettings(jniUrl.toString()); } JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnPause(JNIEnv* env, jobject obj) { } JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnResume(JNIEnv* env, jobject obj) { } JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnExitVr(JNIEnv* env, jobject obj) { } // HifiUtils JNIEXPORT jstring JNICALL Java_io_highfidelity_hifiinterface_HifiUtils_getCurrentAddress(JNIEnv *env, jobject instance) { QSharedPointer addressManager = DependencyManager::get(); if (!addressManager) { return env->NewString(nullptr, 0); } QString str; if (!addressManager->getPlaceName().isEmpty()) { str = addressManager->getPlaceName(); } else if (!addressManager->getHost().isEmpty()) { str = addressManager->getHost(); } QRegExp pathRegEx("(\\/[^\\/]+)"); if (!addressManager->currentPath().isEmpty() && addressManager->currentPath().contains(pathRegEx) && pathRegEx.matchedLength() > 0) { str += pathRegEx.cap(0); } return env->NewStringUTF(str.toLatin1().data()); } JNIEXPORT jstring JNICALL Java_io_highfidelity_hifiinterface_HifiUtils_protocolVersionSignature(JNIEnv *env, jobject instance) { return env->NewStringUTF(protocolVersionsSignatureBase64().toLatin1().data()); } JNIEXPORT jstring JNICALL Java_io_highfidelity_hifiinterface_fragment_HomeFragment_nativeGetLastLocation(JNIEnv *env, jobject instance) { Setting::Handle currentAddressHandle(QStringList() << "AddressManager" << "address", QString()); QUrl lastLocation = currentAddressHandle.get(); return env->NewStringUTF(lastLocation.toString().toLatin1().data()); } JNIEXPORT void JNICALL Java_io_highfidelity_hifiinterface_fragment_LoginFragment_nativeLogin(JNIEnv *env, jobject instance, jstring username_, jstring password_, jobject usernameChangedListener) { const char *c_username = env->GetStringUTFChars(username_, 0); const char *c_password = env->GetStringUTFChars(password_, 0); QString username = QString(c_username); QString password = QString(c_password); env->ReleaseStringUTFChars(username_, c_username); env->ReleaseStringUTFChars(password_, c_password); auto accountManager = DependencyManager::get(); __loginCompletedListener = QAndroidJniObject(instance); __usernameChangedListener = QAndroidJniObject(usernameChangedListener); QObject::connect(accountManager.data(), &AccountManager::loginComplete, [](const QUrl& authURL) { jboolean jSuccess = (jboolean) true; __loginCompletedListener.callMethod("handleLoginCompleted", "(Z)V", jSuccess); }); QObject::connect(accountManager.data(), &AccountManager::loginFailed, []() { jboolean jSuccess = (jboolean) false; __loginCompletedListener.callMethod("handleLoginCompleted", "(Z)V", jSuccess); }); QObject::connect(accountManager.data(), &AccountManager::usernameChanged, [](const QString& username) { QAndroidJniObject string = QAndroidJniObject::fromString(username); __usernameChangedListener.callMethod("handleUsernameChanged", "(Ljava/lang/String;)V", string.object()); }); QMetaObject::invokeMethod(accountManager.data(), "requestAccessToken", Q_ARG(const QString&, username), Q_ARG(const QString&, password)); } JNIEXPORT void JNICALL Java_io_highfidelity_hifiinterface_SplashActivity_registerLoadCompleteListener(JNIEnv *env, jobject instance) { __loadCompleteListener = QAndroidJniObject(instance); QObject::connect(&AndroidHelper::instance(), &AndroidHelper::qtAppLoadComplete, []() { __loadCompleteListener.callMethod("onAppLoadedComplete", "()V"); __interfaceActivity.callMethod("onAppLoadedComplete", "()V"); QObject::disconnect(&AndroidHelper::instance(), &AndroidHelper::qtAppLoadComplete, nullptr, nullptr); }); } JNIEXPORT jboolean JNICALL Java_io_highfidelity_hifiinterface_MainActivity_nativeIsLoggedIn(JNIEnv *env, jobject instance) { return DependencyManager::get()->isLoggedIn(); } JNIEXPORT void JNICALL Java_io_highfidelity_hifiinterface_MainActivity_nativeLogout(JNIEnv *env, jobject instance) { DependencyManager::get()->logout(); } JNIEXPORT jstring JNICALL Java_io_highfidelity_hifiinterface_MainActivity_nativeGetDisplayName(JNIEnv *env, jobject instance) { QString username = DependencyManager::get()->getAccountInfo().getUsername(); return env->NewStringUTF(username.toLatin1().data()); } JNIEXPORT void JNICALL Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeEnterBackground(JNIEnv *env, jobject obj) { AndroidHelper::instance().notifyEnterBackground(); } JNIEXPORT void JNICALL Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeEnterForeground(JNIEnv *env, jobject obj) { AndroidHelper::instance().notifyEnterForeground(); } }