AndroidSurfaceTexture::AndroidSurfaceTexture(unsigned int texName)
    : QObject()
    , m_texID(int(texName))
{
    // API level 11 or higher is required
    if (QtAndroidPrivate::androidSdkVersion() < 11) {
        qWarning("Camera preview and video playback require Android 3.0 (API level 11) or later.");
        return;
    }

    QJNIEnvironmentPrivate env;
    m_surfaceTexture = QJNIObjectPrivate("android/graphics/SurfaceTexture", "(I)V", jint(texName));
    if (env->ExceptionCheck()) {
#ifdef QT_DEBUG
        env->ExceptionDescribe();
#endif // QT_DEBUG
        env->ExceptionClear();
    }

    if (m_surfaceTexture.isValid())
        g_objectMap.insert(int(texName), this);

    QJNIObjectPrivate listener(QtSurfaceTextureListenerClassName, "(I)V", jint(texName));
    m_surfaceTexture.callMethod<void>("setOnFrameAvailableListener",
                                      "(Landroid/graphics/SurfaceTexture$OnFrameAvailableListener;)V",
                                      listener.object());
}
void AndroidMediaRecorder::setOrientationHint(int degrees)
{
    QJNIEnvironmentPrivate env;
    m_mediaRecorder.callMethod<void>("setOrientationHint", "(I)V", degrees);
    if (env->ExceptionCheck()) {
#ifdef QT_DEBUG
        env->ExceptionDescribe();
#endif
        env->ExceptionClear();
    }
}
void AndroidMediaRecorder::setOutputFormat(OutputFormat format)
{
    QJNIEnvironmentPrivate env;
    m_mediaRecorder.callMethod<void>("setOutputFormat", "(I)V", int(format));
    if (env->ExceptionCheck()) {
#ifdef QT_DEBUG
        env->ExceptionDescribe();
#endif
        env->ExceptionClear();
    }
}
void AndroidMediaRecorder::setVideoSize(const QSize &size)
{
    QJNIEnvironmentPrivate env;
    m_mediaRecorder.callMethod<void>("setVideoSize", "(II)V", size.width(), size.height());
    if (env->ExceptionCheck()) {
#ifdef QT_DEBUG
        env->ExceptionDescribe();
#endif
        env->ExceptionClear();
    }
}
void AndroidMediaRecorder::setVideoSource(VideoSource source)
{
    QJNIEnvironmentPrivate env;
    m_mediaRecorder.callMethod<void>("setVideoSource", "(I)V", int(source));
    if (env->ExceptionCheck()) {
#ifdef QT_DEBUG
        env->ExceptionDescribe();
#endif
        env->ExceptionClear();
    }
}
void AndroidMediaRecorder::setAudioEncoder(AudioEncoder encoder)
{
    QJNIEnvironmentPrivate env;
    m_mediaRecorder.callMethod<void>("setAudioEncoder", "(I)V", int(encoder));
    if (env->ExceptionCheck()) {
#ifdef QT_DEBUG
        env->ExceptionDescribe();
#endif
        env->ExceptionClear();
    }
}
void AndroidMediaRecorder::setVideoFrameRate(int rate)
{
    QJNIEnvironmentPrivate env;
    m_mediaRecorder.callMethod<void>("setVideoFrameRate", "(I)V", rate);
    if (env->ExceptionCheck()) {
#ifdef QT_DEBUG
        env->ExceptionDescribe();
#endif
        env->ExceptionClear();
    }
}
void AndroidMediaRecorder::stop()
{
    QJNIEnvironmentPrivate env;
    m_mediaRecorder.callMethod<void>("stop");
    if (env->ExceptionCheck()) {
#ifdef QT_DEBUG
        env->ExceptionDescribe();
#endif
        env->ExceptionClear();
    }
}
void AndroidMediaRecorder::setOutputFile(const QString &path)
{
    QJNIEnvironmentPrivate env;
    m_mediaRecorder.callMethod<void>("setOutputFile",
                                     "(Ljava/lang/String;)V",
                                     QJNIObjectPrivate::fromString(path).object());
    if (env->ExceptionCheck()) {
#ifdef QT_DEBUG
        env->ExceptionDescribe();
#endif
        env->ExceptionClear();
    }
}
bool AndroidMediaRecorder::start()
{
    QJNIEnvironmentPrivate env;
    m_mediaRecorder.callMethod<void>("start");
    if (env->ExceptionCheck()) {
#ifdef QT_DEBUG
        env->ExceptionDescribe();
#endif
        env->ExceptionClear();
        return false;
    }
    return true;
}
QMatrix4x4 AndroidSurfaceTexture::getTransformMatrix()
{
    QMatrix4x4 matrix;
    if (!m_surfaceTexture.isValid())
        return matrix;

    QJNIEnvironmentPrivate env;

    jfloatArray array = env->NewFloatArray(16);
    m_surfaceTexture.callMethod<void>("getTransformMatrix", "([F)V", array);
    env->GetFloatArrayRegion(array, 0, 16, matrix.data());
    env->DeleteLocalRef(array);

    return matrix;
}
void AndroidCameraPrivate::fetchLastPreviewFrame()
{
    QJNIEnvironmentPrivate env;
    QJNIObjectPrivate data = m_cameraListener.callObjectMethod("lockAndFetchPreviewBuffer", "()[B");
    if (!data.isValid()) {
        m_cameraListener.callMethod<void>("unlockPreviewBuffer");
        return;
    }
    const int arrayLength = env->GetArrayLength(static_cast<jbyteArray>(data.object()));
    QByteArray bytes(arrayLength, Qt::Uninitialized);
    env->GetByteArrayRegion(static_cast<jbyteArray>(data.object()),
                            0,
                            arrayLength,
                            reinterpret_cast<jbyte *>(bytes.data()));
    m_cameraListener.callMethod<void>("unlockPreviewBuffer");

    emit previewFetched(bytes);
}
QList<QByteArray> QAndroidTimeZonePrivate::availableTimeZoneIds() const
{
    QList<QByteArray> availableTimeZoneIdList;
    QJNIObjectPrivate androidAvailableIdList = QJNIObjectPrivate::callStaticObjectMethod("java.util.TimeZone", "getAvailableIDs", "()[Ljava/lang/String;");

    QJNIEnvironmentPrivate jniEnv;
    int androidTZcount = jniEnv->GetArrayLength( static_cast<jarray>(androidAvailableIdList.object()) );

    // need separate jobject and QAndroidJniObject here so that we can delete (DeleteLocalRef) the reference to the jobject
    // (or else the JNI reference table fills after 512 entries from GetObjectArrayElement)
    jobject androidTZobject;
    QJNIObjectPrivate androidTZ;
    for (int i=0; i<androidTZcount; i++ ) {
        androidTZobject = jniEnv->GetObjectArrayElement( static_cast<jobjectArray>( androidAvailableIdList.object() ), i );
        androidTZ = androidTZobject;
        availableTimeZoneIdList.append( androidTZ.toString().toUtf8() );
        jniEnv->DeleteLocalRef(androidTZobject);
    }

    return availableTimeZoneIdList;
}
QList<AndroidNetworkInfo> AndroidConnectivityManager::getAllNetworkInfo() const
{
    QJNIEnvironmentPrivate env;
    QJNIObjectPrivate objArray = m_connectivityManager.callObjectMethod("getAllNetworkInfo",
                                                                        "()[Landroid/net/NetworkInfo;");
    QList<AndroidNetworkInfo> list;
    if (!objArray.isValid())
        return list;

    const jsize length = env->GetArrayLength(static_cast<jarray>(objArray.object()));
    if (exceptionCheckAndClear(env))
        return list;

    for (int i = 0; i != length; ++i) {
        jobject lref = env->GetObjectArrayElement(static_cast<jobjectArray>(objArray.object()), i);
        if (exceptionCheckAndClear(env))
            break;

        list << AndroidNetworkInfo(lref);
        env->DeleteLocalRef(lref);
    }

    return list;
}
QAndroidInputContext::QAndroidInputContext()
    : QPlatformInputContext(), m_composingTextStart(-1), m_blockUpdateSelection(false),  m_batchEditNestingLevel(0), m_focusObject(0)
{
    jclass clazz = QJNIEnvironmentPrivate::findClass(QtNativeInputConnectionClassName);
    if (clazz == NULL) {
        qCritical() << "Native registration unable to find class '"
                    << QtNativeInputConnectionClassName
                    << "'";
        return;
    }

    QJNIEnvironmentPrivate env;
    if (env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
        qCritical() << "RegisterNatives failed for '"
                    << QtNativeInputConnectionClassName
                    << "'";
        return;
    }

    clazz = QJNIEnvironmentPrivate::findClass(QtExtractedTextClassName);
    if (clazz == NULL) {
        qCritical() << "Native registration unable to find class '"
                    << QtExtractedTextClassName
                    << "'";
        return;
    }

    m_extractedTextClass = static_cast<jclass>(env->NewGlobalRef(clazz));
    m_classConstructorMethodID = env->GetMethodID(m_extractedTextClass, "<init>", "()V");
    if (m_classConstructorMethodID == NULL) {
        qCritical() << "GetMethodID failed";
        return;
    }

    m_partialEndOffsetFieldID = env->GetFieldID(m_extractedTextClass, "partialEndOffset", "I");
    if (m_partialEndOffsetFieldID == NULL) {
        qCritical() << "Can't find field partialEndOffset";
        return;
    }

    m_partialStartOffsetFieldID = env->GetFieldID(m_extractedTextClass, "partialStartOffset", "I");
    if (m_partialStartOffsetFieldID == NULL) {
        qCritical() << "Can't find field partialStartOffset";
        return;
    }

    m_selectionEndFieldID = env->GetFieldID(m_extractedTextClass, "selectionEnd", "I");
    if (m_selectionEndFieldID == NULL) {
        qCritical() << "Can't find field selectionEnd";
        return;
    }

    m_selectionStartFieldID = env->GetFieldID(m_extractedTextClass, "selectionStart", "I");
    if (m_selectionStartFieldID == NULL) {
        qCritical() << "Can't find field selectionStart";
        return;
    }

    m_startOffsetFieldID = env->GetFieldID(m_extractedTextClass, "startOffset", "I");
    if (m_startOffsetFieldID == NULL) {
        qCritical() << "Can't find field startOffset";
        return;
    }

    m_textFieldID = env->GetFieldID(m_extractedTextClass, "text", "Ljava/lang/String;");
    if (m_textFieldID == NULL) {
        qCritical() << "Can't find field text";
        return;
    }
    qRegisterMetaType<QInputMethodEvent *>("QInputMethodEvent*");
    qRegisterMetaType<QInputMethodQueryEvent *>("QInputMethodQueryEvent*");
    m_androidInputContext = this;
}