Android: Support custom FileProvider in openUrl
Previously, qtprovider was set as the default FileProvider, which causes issues when creating a custom FileProvider on application side or having multiple FileProviders. To address this problem, we have implemented a solution that checks for all available FileProviders and ,in case of multiple FileProviders, selects first non-default FileProvider. Fixes: QTBUG-117417 Pick-to: 6.7 6.5 Change-Id: I2a68983403f964036dc3177e13fe7ea4f9b4788b Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io> (cherry picked from commit d3508d48c0e4ef73a3a673ea14fb59cd8d930398) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
39524fabc4
commit
a96e0eddd9
@ -14,8 +14,12 @@
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
using namespace QtJniTypes;
|
||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
|
static constexpr auto s_defaultScheme = "file"_L1;
|
||||||
|
static constexpr auto s_defaultProvider = "qtprovider"_L1;
|
||||||
|
|
||||||
QAndroidPlatformServices::QAndroidPlatformServices()
|
QAndroidPlatformServices::QAndroidPlatformServices()
|
||||||
{
|
{
|
||||||
m_actionView = QJniObject::getStaticObjectField("android/content/Intent", "ACTION_VIEW",
|
m_actionView = QJniObject::getStaticObjectField("android/content/Intent", "ACTION_VIEW",
|
||||||
@ -39,57 +43,110 @@ QAndroidPlatformServices::QAndroidPlatformServices()
|
|||||||
}
|
}
|
||||||
|
|
||||||
Q_DECLARE_JNI_CLASS(FileProvider, "androidx/core/content/FileProvider");
|
Q_DECLARE_JNI_CLASS(FileProvider, "androidx/core/content/FileProvider");
|
||||||
|
Q_DECLARE_JNI_CLASS(PackageManager, "android/content/pm/PackageManager");
|
||||||
|
Q_DECLARE_JNI_CLASS(PackageInfo, "android/content/pm/PackageInfo");
|
||||||
|
Q_DECLARE_JNI_CLASS(ProviderInfo, "android/content/pm/ProviderInfo");
|
||||||
|
|
||||||
bool QAndroidPlatformServices::openUrl(const QUrl &theUrl)
|
bool QAndroidPlatformServices::openUrl(const QUrl &theUrl)
|
||||||
{
|
{
|
||||||
QString mime;
|
|
||||||
QUrl url(theUrl);
|
QUrl url(theUrl);
|
||||||
|
|
||||||
// avoid recursing back into self
|
// avoid recursing back into self
|
||||||
if (url == m_handlingUrl)
|
if (url == m_handlingUrl)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// if the file is local, we need to pass the MIME type, otherwise Android
|
|
||||||
// does not start an Intent to view this file
|
|
||||||
const auto fileScheme = "file"_L1;
|
|
||||||
|
|
||||||
// a real URL including the scheme is needed, else the Intent can not be started
|
// a real URL including the scheme is needed, else the Intent can not be started
|
||||||
if (url.scheme().isEmpty())
|
if (url.scheme().isEmpty())
|
||||||
url.setScheme(fileScheme);
|
url.setScheme(s_defaultScheme);
|
||||||
|
|
||||||
if (url.scheme() == fileScheme)
|
const int sdkVersion = QNativeInterface::QAndroidApplication::sdkVersion();
|
||||||
|
if (url.scheme() != s_defaultScheme || sdkVersion < 24 )
|
||||||
|
return openURL(url);
|
||||||
|
return openUrlWithFileProvider(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QAndroidPlatformServices::getMimeOfUrl(const QUrl &url) const
|
||||||
|
{
|
||||||
|
QString mime;
|
||||||
|
if (url.scheme() == s_defaultScheme)
|
||||||
mime = QMimeDatabase().mimeTypeForUrl(url).name();
|
mime = QMimeDatabase().mimeTypeForUrl(url).name();
|
||||||
|
return mime;
|
||||||
|
}
|
||||||
|
|
||||||
const QJniObject mimeString = QJniObject::fromString(mime);
|
bool QAndroidPlatformServices::openURL(const QUrl &url) const
|
||||||
|
{
|
||||||
using namespace QNativeInterface;
|
return QJniObject::callStaticMethod<jboolean>(
|
||||||
|
QtAndroid::applicationClass(), "openURL",
|
||||||
auto openUrl = [mimeString](const QJniObject &url) {
|
QNativeInterface::QAndroidApplication::context(),
|
||||||
return QJniObject::callStaticMethod<jboolean>(QtAndroid::applicationClass(), "openURL",
|
url.toString(),
|
||||||
QAndroidApplication::context(), url.object<jstring>(), mimeString.object<jstring>());
|
getMimeOfUrl(url));
|
||||||
};
|
}
|
||||||
|
|
||||||
if (url.scheme() != fileScheme || QNativeInterface::QAndroidApplication::sdkVersion() < 24)
|
|
||||||
return openUrl(QJniObject::fromString(url.toString()));
|
|
||||||
|
|
||||||
// Use FileProvider for file scheme with sdk >= 24
|
|
||||||
const QJniObject context = QAndroidApplication::context();
|
|
||||||
const auto appId = context.callMethod<jstring>("getPackageName").toString();
|
|
||||||
const auto providerName = QJniObject::fromString(appId + ".qtprovider"_L1);
|
|
||||||
|
|
||||||
const auto urlPath = QJniObject::fromString(url.path());
|
|
||||||
const auto urlFile = QJniObject(QtJniTypes::Traits<QtJniTypes::File>::className(),
|
|
||||||
urlPath.object<jstring>());
|
|
||||||
|
|
||||||
const auto fileProviderUri = QJniObject::callStaticMethod<QtJniTypes::Uri>(
|
|
||||||
QtJniTypes::Traits<QtJniTypes::FileProvider>::className(), "getUriForFile",
|
|
||||||
QAndroidApplication::context(), providerName.object<jstring>(),
|
|
||||||
urlFile.object<QtJniTypes::File>());
|
|
||||||
|
|
||||||
if (fileProviderUri.isValid())
|
|
||||||
return openUrl(fileProviderUri.callMethod<jstring>("toString"));
|
|
||||||
|
|
||||||
|
bool QAndroidPlatformServices::openUrlWithFileProvider(const QUrl &url)
|
||||||
|
{
|
||||||
|
const QJniObject context = QNativeInterface::QAndroidApplication::context();
|
||||||
|
auto authorities = getFileProviderAuthorities(context);
|
||||||
|
if (authorities.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
|
return openUrlWithAuthority(url, getAdequateFileproviderAuthority(authorities));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString QAndroidPlatformServices::getAdequateFileproviderAuthority(const QStringList &authorities) const
|
||||||
|
{
|
||||||
|
if (authorities.size() == 1)
|
||||||
|
return authorities[0];
|
||||||
|
|
||||||
|
QString nonQtAuthority;
|
||||||
|
for (const auto &authority : authorities) {
|
||||||
|
if (!authority.endsWith(s_defaultProvider, Qt::CaseSensitive)) {
|
||||||
|
nonQtAuthority = authority;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nonQtAuthority;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QAndroidPlatformServices::openUrlWithAuthority(const QUrl &url, const QString &authority)
|
||||||
|
{
|
||||||
|
const auto urlPath = QJniObject::fromString(url.path());
|
||||||
|
const auto urlFile = QJniObject(Traits<File>::className(),
|
||||||
|
urlPath.object<jstring>());
|
||||||
|
const auto fileProviderUri = QJniObject::callStaticMethod<Uri>(
|
||||||
|
Traits<FileProvider>::className(), "getUriForFile",
|
||||||
|
QNativeInterface::QAndroidApplication::context(), authority,
|
||||||
|
urlFile.object<File>());
|
||||||
|
if (fileProviderUri.isValid())
|
||||||
|
return openURL(url);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList QAndroidPlatformServices::getFileProviderAuthorities(const QJniObject &context) const
|
||||||
|
{
|
||||||
|
QStringList authorityList;
|
||||||
|
|
||||||
|
const auto packageManager = context.callMethod<PackageManager>("getPackageManager");
|
||||||
|
const auto packageName = context.callMethod<QString>("getPackageName");
|
||||||
|
const auto packageInfo = packageManager.callMethod<PackageInfo>("getPackageInfo",
|
||||||
|
packageName,
|
||||||
|
8 /* PackageManager.GET_PROVIDERS */);
|
||||||
|
const auto providersArray = packageInfo.getField<ProviderInfo[]>("providers");
|
||||||
|
|
||||||
|
if (providersArray.isValid()) {
|
||||||
|
const auto className = Traits<FileProvider>::className();
|
||||||
|
for (const auto &fileProvider : providersArray) {
|
||||||
|
auto providerName = fileProvider.getField<QString>("name");
|
||||||
|
if (providerName.replace(".", "/").contains(className.data())) {
|
||||||
|
const auto authority = fileProvider.getField<QString>("authority");
|
||||||
|
if (!authority.isEmpty())
|
||||||
|
authorityList << authority;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (authorityList.isEmpty())
|
||||||
|
qWarning() << "No file provider found in the AndroidManifest.xml.";
|
||||||
|
|
||||||
|
return authorityList;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QAndroidPlatformServices::openDocument(const QUrl &url)
|
bool QAndroidPlatformServices::openDocument(const QUrl &url)
|
||||||
|
@ -25,6 +25,15 @@ public:
|
|||||||
|
|
||||||
bool handleNewIntent(JNIEnv *env, jobject intent) override;
|
bool handleNewIntent(JNIEnv *env, jobject intent) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool openURL(const QUrl &url) const;
|
||||||
|
bool openUrlWithFileProvider(const QUrl &url);
|
||||||
|
bool openUrlWithAuthority(const QUrl &url, const QString &authority);
|
||||||
|
|
||||||
|
QString getMimeOfUrl(const QUrl &url) const;
|
||||||
|
QStringList getFileProviderAuthorities(const QJniObject &context) const;
|
||||||
|
QString getAdequateFileproviderAuthority(const QStringList &authorities) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QUrl m_handlingUrl;
|
QUrl m_handlingUrl;
|
||||||
QString m_actionView;
|
QString m_actionView;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user