// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include #include #include #include QT_BEGIN_NAMESPACE using namespace QtJniTypes; std::map QAndroidItemModelProxy::s_mutexes = std::map{}; jint QAndroidItemModelProxy::columnCount(const QModelIndex &parent) const { Q_ASSERT(jInstance.isValid()); const QMutexLocker lock = getMutexLocker(this); auto parentIndex = QAndroidModelIndexProxy::jInstance(parent); return jInstance.callMethod("columnCount", parentIndex); } bool QAndroidItemModelProxy::canFetchMore(const QModelIndex &parent) const { Q_ASSERT(jInstance.isValid()); const QMutexLocker lock = getMutexLocker(this); auto parentIndex = QAndroidModelIndexProxy::jInstance(parent); return jInstance.callMethod("canFetchMore", parentIndex); } bool QAndroidItemModelProxy::canFetchMoreDefault(const QModelIndex &parent) const { return QAbstractItemModel::canFetchMore(parent); } QVariant QAndroidItemModelProxy::data(const QModelIndex &index, int role) const { Q_ASSERT(jInstance.isValid()); const QMutexLocker lock = getMutexLocker(this); auto jIndex = QAndroidModelIndexProxy::jInstance(index); QJniObject jData = jInstance.callMethod("data", jIndex, role); return QAndroidTypeConverter::toQVariant(jData); } QModelIndex QAndroidItemModelProxy::index(int row, int column, const QModelIndex &parent) const { Q_ASSERT(jInstance.isValid()); const QMutexLocker lock = getMutexLocker(this); JQtModelIndex jIndex = jInstance.callMethod( "index", row, column, QAndroidModelIndexProxy::jInstance(parent)); return QAndroidModelIndexProxy::qInstance(jIndex); } QModelIndex QAndroidItemModelProxy::parent(const QModelIndex &index) const { Q_ASSERT(jInstance.isValid()); const QMutexLocker lock = getMutexLocker(this); auto jIndex = QAndroidModelIndexProxy::jInstance(index); return QAndroidModelIndexProxy::qInstance( jInstance.callMethod("parent", jIndex)); } int QAndroidItemModelProxy::rowCount(const QModelIndex &parent) const { Q_ASSERT(jInstance.isValid()); const QMutexLocker lock = getMutexLocker(this); auto parentIndex = QAndroidModelIndexProxy::jInstance(parent); return jInstance.callMethod("rowCount", parentIndex); } QHash QAndroidItemModelProxy::roleNames() const { Q_ASSERT(jInstance.isValid()); const QMutexLocker lock = getMutexLocker(this); QHash roleNames; HashMap hashMap = jInstance.callMethod("roleNames"); Set set = hashMap.callMethod("keySet"); const QJniArray keyArray = set.callMethod("toArray"); for (const auto &key : keyArray) { const QJniObject roleName = hashMap.callMethod("get", key); const int intKey = QJniObject(key).callMethod("intValue"); const QByteArray roleByteArray = String(roleName).toString().toLatin1(); roleNames.insert(intKey, roleByteArray); } return roleNames; } QHash QAndroidItemModelProxy::defaultRoleNames() const { return QAbstractItemModel::roleNames(); } void QAndroidItemModelProxy::fetchMore(const QModelIndex &parent) { Q_ASSERT(jInstance.isValid()); const QMutexLocker lock = getMutexLocker(this); auto parentIndex = QAndroidModelIndexProxy::jInstance(parent); jInstance.callMethod("fetchMore", parentIndex); } void QAndroidItemModelProxy::fetchMoreDefault(const QModelIndex &parent) { QAbstractItemModel::fetchMore(parent); } bool QAndroidItemModelProxy::hasChildren(const QModelIndex &parent) const { Q_ASSERT(jInstance.isValid()); const QMutexLocker lock = getMutexLocker(this); auto parentIndex = QAndroidModelIndexProxy::jInstance(parent); return jInstance.callMethod("hasChildren", parentIndex); } bool QAndroidItemModelProxy::hasChildrenDefault(const QModelIndex &parent) const { return QAbstractItemModel::hasChildren(parent); } QModelIndex QAndroidItemModelProxy::sibling(int row, int column, const QModelIndex &parent) const { Q_ASSERT(jInstance.isValid()); const QMutexLocker lock = getMutexLocker(this); return QAndroidModelIndexProxy::qInstance(jInstance.callMethod( "sibling", row, column, QAndroidModelIndexProxy::jInstance(parent))); } QModelIndex QAndroidItemModelProxy::siblingDefault(int row, int column, const QModelIndex &parent) { return QAbstractItemModel::sibling(row, column, parent); } bool QAndroidItemModelProxy::setData(const QModelIndex &index, const QVariant &value, int role) { Q_ASSERT(jInstance.isValid()); const QMutexLocker lock = getMutexLocker(this); auto jIndex = QAndroidModelIndexProxy::jInstance(index); auto jValue = QAndroidTypeConverter::toJavaObject(value, QJniEnvironment::getJniEnv()); return jInstance.callMethod("setData", jIndex, jValue, role); } bool QAndroidItemModelProxy::setDataDefault(const QModelIndex &index, const QVariant &value, int role) { return QAbstractItemModel::setData(index, value, role); } Q_REQUIRED_RESULT QAbstractItemModel * QAndroidItemModelProxy::nativeInstance(JQtAbstractItemModel itemModel) { jlong nativeReference = itemModel.callMethod("nativeReference"); return reinterpret_cast(nativeReference); } Q_REQUIRED_RESULT QAbstractItemModel * QAndroidItemModelProxy::createNativeProxy(QJniObject itemModel) { QAbstractItemModel *nativeProxy = nativeInstance(itemModel); if (!nativeProxy) { Q_ASSERT(QCoreApplication::instance()); nativeProxy = new QAndroidItemModelProxy(itemModel); QThread *qtMainThread = QCoreApplication::instance()->thread(); if (nativeProxy->thread() != qtMainThread) nativeProxy->moveToThread(qtMainThread); itemModel.callMethod("setNativeReference", reinterpret_cast(nativeProxy)); connect(nativeProxy, &QAndroidItemModelProxy::destroyed, nativeProxy, [](QObject *obj) { auto proxy = qobject_cast(obj); if (proxy) { const QMutexLocker lock = getMutexLocker(proxy); proxy->jInstance.callMethod("detachFromNative"); } }); connect(nativeProxy, &QAndroidItemModelProxy::dataChanged, nativeProxy, [nativeProxy](const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList &roles) { auto proxy = qobject_cast(nativeProxy); if (proxy) { QJniObject jInstance = proxy->jInstance; const QMutexLocker lock = getMutexLocker(proxy); jInstance.callMethod("handleDataChanged", QAndroidModelIndexProxy::jInstance(topLeft), QAndroidModelIndexProxy::jInstance(bottomRight), QJniArray(roles)); } }); } return nativeProxy; } QJniObject QAndroidItemModelProxy::createProxy(QAbstractItemModel *itemModel) { return JQtAndroidItemModelProxy(reinterpret_cast(itemModel)); } int QAndroidItemModelProxy::jni_columnCount(JNIEnv *env, jobject object, JQtModelIndex parent) { const QModelIndex nativeParent = QAndroidModelIndexProxy::qInstance(parent); return invokeNativeMethod(env, object, &QAbstractItemModel::columnCount, nativeParent); } jobject QAndroidItemModelProxy::jni_data(JNIEnv *env, jobject object, JQtModelIndex index, jint role) { const QModelIndex nativeIndex = QAndroidModelIndexProxy::qInstance(index); const QVariant data = invokeNativeMethod(env, object, &QAbstractItemModel::data, nativeIndex, role); return QAndroidTypeConverter::toJavaObject(data, env); } jobject QAndroidItemModelProxy::jni_index(JNIEnv *env, jobject object, jint row, jint column, JQtModelIndex parent) { auto nativeParent = QAndroidModelIndexProxy::qInstance(parent); const QModelIndex modelIndex = invokeNativeMethod(env, object, &QAbstractItemModel::index, row, column, nativeParent); return env->NewLocalRef(QAndroidModelIndexProxy::jInstance(modelIndex).object()); } jobject QAndroidItemModelProxy::jni_parent(JNIEnv *env, jobject object, JQtModelIndex index) { const QModelIndex nativeIndex = QAndroidModelIndexProxy::qInstance(index); QModelIndex (QAbstractItemModel::*parentOverloadPtr)(const QModelIndex &) const = &QAbstractItemModel::parent; const QModelIndex parent = invokeNativeMethod(env, object, parentOverloadPtr, nativeIndex); return env->NewLocalRef(QAndroidModelIndexProxy::jInstance(parent).object()); } jint QAndroidItemModelProxy::jni_rowCount(JNIEnv *env, jobject object, JQtModelIndex parent) { return invokeNativeMethod(env, object, &QAbstractItemModel::rowCount, QAndroidModelIndexProxy::qInstance(parent)); } jobject QAndroidItemModelProxy::jni_roleNames(JNIEnv *env, jobject object) { auto roleNames = invokeNativeImpl(env, object, &QAndroidItemModelProxy::defaultRoleNames, &QAbstractItemModel::roleNames); HashMap jRoleNames{}; for (auto [role, roleName] : roleNames.asKeyValueRange()) { const Integer jRole(role); jRoleNames.callMethod("put", jRole.object(), QString::fromUtf8(roleName)); } return env->NewLocalRef(jRoleNames.object()); } jobject QAndroidItemModelProxy::jni_createIndex(JNIEnv *env, jobject object, jint row, jint column, jlong id) { QModelIndex (QAndroidItemModelProxy::*createIndexPtr)(int, int, quintptr) const = &QAndroidItemModelProxy::createIndex; const QModelIndex index = invokeNativeProxyMethod(env, object, createIndexPtr, row, column, quintptr(id)); return env->NewLocalRef(QAndroidModelIndexProxy::jInstance(index).object()); } jboolean QAndroidItemModelProxy::jni_canFetchMore(JNIEnv *env, jobject object, JQtModelIndex parent) { return invokeNativeImpl(env, object, &QAndroidItemModelProxy::canFetchMoreDefault, &QAbstractItemModel::canFetchMore, QAndroidModelIndexProxy::qInstance(parent)); } void QAndroidItemModelProxy::jni_fetchMore(JNIEnv *env, jobject object, JQtModelIndex parent) { return invokeNativeImpl(env, object, &QAndroidItemModelProxy::fetchMoreDefault, &QAbstractItemModel::fetchMore, QAndroidModelIndexProxy::qInstance(parent)); } jboolean QAndroidItemModelProxy::jni_hasChildren(JNIEnv *env, jobject object, JQtModelIndex parent) { return invokeNativeImpl(env, object, &QAndroidItemModelProxy::hasChildrenDefault, &QAbstractItemModel::hasChildren, QAndroidModelIndexProxy::qInstance(parent)); } jboolean QAndroidItemModelProxy::jni_hasIndex(JNIEnv *env, jobject object, jint row, jint column, JQtModelIndex parent) { return invokeNativeMethod(env, object, &QAbstractItemModel::hasIndex, row, column, QAndroidModelIndexProxy::qInstance(parent)); } void QAndroidItemModelProxy::jni_beginInsertColumns(JNIEnv *env, jobject object, JQtModelIndex parent, jint first, jint last) { invokeNativeProxyMethod(env, object, &QAndroidItemModelProxy::beginInsertColumns, QAndroidModelIndexProxy::qInstance(parent), first, last); } void QAndroidItemModelProxy::jni_beginInsertRows(JNIEnv *env, jobject object, JQtModelIndex parent, jint first, jint last) { invokeNativeProxyMethod(env, object, &QAndroidItemModelProxy::beginInsertRows, QAndroidModelIndexProxy::qInstance(parent), first, last); } jboolean QAndroidItemModelProxy::jni_beginMoveColumns(JNIEnv *env, jobject object, JQtModelIndex sourceParent, jint sourceFirst, jint sourceLast, JQtModelIndex destinationParent, jint destinationChild) { return invokeNativeProxyMethod( env, object, &QAndroidItemModelProxy::beginMoveColumns, QAndroidModelIndexProxy::qInstance(sourceParent), sourceFirst, sourceLast, QAndroidModelIndexProxy::qInstance(destinationParent), destinationChild); } jboolean QAndroidItemModelProxy::jni_beginMoveRows(JNIEnv *env, jobject object, JQtModelIndex sourceParent, jint sourceFirst, jint sourceLast, JQtModelIndex destinationParent, jint destinationChild) { return invokeNativeProxyMethod( env, object, &QAndroidItemModelProxy::beginMoveRows, QAndroidModelIndexProxy::qInstance(sourceParent), sourceFirst, sourceLast, QAndroidModelIndexProxy::qInstance(destinationParent), destinationChild); } void QAndroidItemModelProxy::jni_beginRemoveColumns(JNIEnv *env, jobject object, JQtModelIndex parent, jint first, jint last) { invokeNativeProxyMethod(env, object, &QAndroidItemModelProxy::beginRemoveColumns, QAndroidModelIndexProxy::qInstance(parent), first, last); } void QAndroidItemModelProxy::jni_beginRemoveRows(JNIEnv *env, jobject object, JQtModelIndex parent, jint first, jint last) { invokeNativeProxyMethod(env, object, &QAndroidItemModelProxy::beginRemoveRows, QAndroidModelIndexProxy::qInstance(parent), first, last); } void QAndroidItemModelProxy::jni_beginResetModel(JNIEnv *env, jobject object) { invokeNativeProxyMethod(env, object, &QAndroidItemModelProxy::beginResetModel); } void QAndroidItemModelProxy::jni_endInsertColumns(JNIEnv *env, jobject object) { invokeNativeProxyMethod(env, object, &QAndroidItemModelProxy::endInsertColumns); } void QAndroidItemModelProxy::jni_endInsertRows(JNIEnv *env, jobject object) { invokeNativeProxyMethod(env, object, &QAndroidItemModelProxy::endInsertRows); } void QAndroidItemModelProxy::jni_endMoveColumns(JNIEnv *env, jobject object) { invokeNativeProxyMethod(env, object, &QAndroidItemModelProxy::endMoveColumns); } void QAndroidItemModelProxy::jni_endMoveRows(JNIEnv *env, jobject object) { invokeNativeProxyMethod(env, object, &QAndroidItemModelProxy::endMoveRows); } void QAndroidItemModelProxy::jni_endRemoveColumns(JNIEnv *env, jobject object) { invokeNativeProxyMethod(env, object, &QAndroidItemModelProxy::endRemoveColumns); } void QAndroidItemModelProxy::jni_endRemoveRows(JNIEnv *env, jobject object) { invokeNativeProxyMethod(env, object, &QAndroidItemModelProxy::endRemoveRows); } void QAndroidItemModelProxy::jni_endResetModel(JNIEnv *env, jobject object) { invokeNativeProxyMethod(env, object, &QAndroidItemModelProxy::endResetModel); } jobject QAndroidItemModelProxy::jni_sibling(JNIEnv *env, jobject object, jint row, jint column, JQtModelIndex parent) { const QModelIndex index = invokeNativeImpl(env, object, &QAndroidItemModelProxy::siblingDefault, &QAbstractItemModel::sibling, row, column, QAndroidModelIndexProxy::qInstance(parent)); return env->NewLocalRef(QAndroidModelIndexProxy::jInstance(index).object()); } jboolean QAndroidItemModelProxy::jni_setData(JNIEnv *env, jobject object, JQtModelIndex index, jobject value, jint role) { const QModelIndex nativeIndex = QAndroidModelIndexProxy::qInstance(index); const QVariant qValue = QAndroidTypeConverter::toQVariant(QJniObject(value)); return invokeNativeImpl(env, object, &QAndroidItemModelProxy::setDataDefault, &QAbstractItemModel::setData, nativeIndex, qValue, role); } void QAndroidItemModelProxy::jni_dataChanged(JNIEnv *env, jobject object, JQtModelIndex topLeft, JQtModelIndex bottomRight, QJniArray roles) { const QModelIndex nativeTopLeft = QAndroidModelIndexProxy::qInstance(topLeft); const QModelIndex nativeBottomRight = QAndroidModelIndexProxy::qInstance(bottomRight); const QList nativeRoles = roles.toContainer(); invokeNativeImpl(env, object, &QAndroidItemModelProxy::dataChanged, &QAbstractItemModel::dataChanged, nativeTopLeft, nativeBottomRight, nativeRoles); } bool QAndroidItemModelProxy::registerAbstractNatives(QJniEnvironment &env) { return env.registerNativeMethods( Traits::className(), { Q_JNI_NATIVE_SCOPED_METHOD(jni_roleNames, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_canFetchMore, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_createIndex, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_fetchMore, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_hasChildren, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_hasIndex, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_beginInsertColumns, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_beginInsertRows, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_beginMoveColumns, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_beginMoveRows, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_beginRemoveColumns, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_beginRemoveRows, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_beginResetModel, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_endInsertColumns, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_endInsertRows, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_endMoveColumns, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_endMoveRows, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_endRemoveColumns, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_endRemoveRows, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_endResetModel, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_sibling, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_setData, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_dataChanged, QAndroidItemModelProxy) }); } bool QAndroidItemModelProxy::registerProxyNatives(QJniEnvironment &env) { return env.registerNativeMethods( Traits::className(), { Q_JNI_NATIVE_SCOPED_METHOD(jni_columnCount, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_data, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_index, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_parent, QAndroidItemModelProxy), Q_JNI_NATIVE_SCOPED_METHOD(jni_rowCount, QAndroidItemModelProxy) }); } QT_END_NAMESPACE