Android: handle move operation with content uris
Allow moving content uris if the destination is provided a full content uri with a parent that's different from the source content uri (i.e. different folders). Note: since the underlaying Android APIs don't always know about the parent of a uri, we do some step to deduce that, but that's not always guaranteed to work. Task-number: QTBUG-98974 Change-Id: If21954e5963f4eb0b96c7ccd983943ea2cab5b24 Reviewed-by: Ville Voutilainen <ville.voutilainen@qt.io> (cherry picked from commit c203ec2720b694fd877512da531a227e0f3310cb)
This commit is contained in:
parent
361ebe6d21
commit
7873cda9f8
@ -442,6 +442,7 @@ const QLatin1String COLUMN_SIZE("_size");
|
|||||||
|
|
||||||
constexpr int FLAG_DIR_SUPPORTS_CREATE = 0x00000008;
|
constexpr int FLAG_DIR_SUPPORTS_CREATE = 0x00000008;
|
||||||
constexpr int FLAG_SUPPORTS_DELETE = 0x00000004;
|
constexpr int FLAG_SUPPORTS_DELETE = 0x00000004;
|
||||||
|
constexpr int FLAG_SUPPORTS_MOVE = 0x00000100;
|
||||||
constexpr int FLAG_SUPPORTS_RENAME = 0x00000040;
|
constexpr int FLAG_SUPPORTS_RENAME = 0x00000040;
|
||||||
constexpr int FLAG_SUPPORTS_WRITE = 0x00000002;
|
constexpr int FLAG_SUPPORTS_WRITE = 0x00000002;
|
||||||
constexpr int FLAG_VIRTUAL_DOCUMENT = 0x00000200;
|
constexpr int FLAG_VIRTUAL_DOCUMENT = 0x00000200;
|
||||||
@ -526,6 +527,23 @@ bool deleteDocument(const QJniObject &documentUri)
|
|||||||
documentUri.object());
|
documentUri.object());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QJniObject moveDocument(const QJniObject &sourceDocumentUri,
|
||||||
|
const QJniObject &sourceParentDocumentUri,
|
||||||
|
const QJniObject &targetParentDocumentUri)
|
||||||
|
{
|
||||||
|
const int flags = Cursor::queryColumn(sourceDocumentUri, Document::COLUMN_FLAGS).toInt();
|
||||||
|
if (!(flags & Document::FLAG_SUPPORTS_MOVE))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return QJniObject::callStaticObjectMethod("android/provider/DocumentsContract",
|
||||||
|
"moveDocument",
|
||||||
|
"(Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri;",
|
||||||
|
contentResolverInstance().object(),
|
||||||
|
sourceDocumentUri.object(),
|
||||||
|
sourceParentDocumentUri.object(),
|
||||||
|
targetParentDocumentUri.object());
|
||||||
|
}
|
||||||
|
|
||||||
QJniObject renameDocument(const QJniObject &documentUri, const QString &displayName)
|
QJniObject renameDocument(const QJniObject &documentUri, const QString &displayName)
|
||||||
{
|
{
|
||||||
const int flags = Cursor::queryColumn(documentUri, Document::COLUMN_FLAGS).toInt();
|
const int flags = Cursor::queryColumn(documentUri, Document::COLUMN_FLAGS).toInt();
|
||||||
@ -778,6 +796,12 @@ bool DocumentFile::rename(const QString &newName)
|
|||||||
displayName.remove(0, 3);
|
displayName.remove(0, 3);
|
||||||
|
|
||||||
uri = renameDocument(m_uri, displayName);
|
uri = renameDocument(m_uri, displayName);
|
||||||
|
} else {
|
||||||
|
// Move
|
||||||
|
QJniObject srcParentUri = fromTreeUri(parseUri(parent))->uri();
|
||||||
|
const QString destParent = newName.left(lastSeparatorIndex(newName));
|
||||||
|
QJniObject targetParentUri = fromTreeUri(parseUri(destParent))->uri();
|
||||||
|
uri = moveDocument(m_uri, srcParentUri, targetParentUri);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uri = renameDocument(m_uri, newName);
|
uri = renameDocument(m_uri, newName);
|
||||||
|
@ -196,31 +196,58 @@ void tst_ContentUris::fileOperations()
|
|||||||
QVERIFY(file.exists());
|
QVERIFY(file.exists());
|
||||||
|
|
||||||
// Rename
|
// Rename
|
||||||
const QString renamedFileName = "renamed_new_file.txt";
|
{
|
||||||
QVERIFY(file.rename(renamedFileName));
|
const QString renamedFileName = "renamed_new_file.txt";
|
||||||
const auto renamedUrl = url.replace(fileName, renamedFileName);
|
QVERIFY(file.rename(renamedFileName));
|
||||||
QVERIFY(file.fileName() == renamedUrl);
|
const auto renamedUrl = url.replace(fileName, renamedFileName);
|
||||||
|
QVERIFY(file.fileName() == renamedUrl);
|
||||||
|
|
||||||
// NOTE: The uri doesn't seem to stay usable after a rename and it needs to get
|
// NOTE: The uri doesn't seem to stay usable after a rename and it needs to get
|
||||||
// permission again via the SAF picker.
|
// permission again via the SAF picker.
|
||||||
showInstructionsDialog("Choose the file that was renamed");
|
showInstructionsDialog("Choose the file that was renamed");
|
||||||
QFileDialog::getOpenFileName(nullptr, tr("Open File"));
|
QFileDialog::getOpenFileName(nullptr, tr("Open File"));
|
||||||
QVERIFY(file.exists());
|
QVERIFY(file.exists());
|
||||||
|
|
||||||
// rename now with full content uri
|
// rename now with full content uri
|
||||||
const auto secondRenamedUrl = url.replace(renamedFileName, "second_nenamed_file.txt");
|
const auto secondRenamedUrl = url.replace(renamedFileName, "second_nenamed_file.txt");
|
||||||
QVERIFY(file.rename(secondRenamedUrl));
|
QVERIFY(file.rename(secondRenamedUrl));
|
||||||
QVERIFY(file.fileName() == secondRenamedUrl);
|
QVERIFY(file.fileName() == secondRenamedUrl);
|
||||||
|
|
||||||
// NOTE: The uri doesn't seem to stay usable after a rename and it needs to get
|
// NOTE: The uri doesn't seem to stay usable after a rename and it needs to get
|
||||||
// permission again via the SAF picker.
|
// permission again via the SAF picker.
|
||||||
showInstructionsDialog("Choose the file that was renamed");
|
showInstructionsDialog("Choose the file that was renamed");
|
||||||
QFileDialog::getOpenFileName(nullptr, tr("Open File"));
|
QFileDialog::getOpenFileName(nullptr, tr("Open File"));
|
||||||
QVERIFY(file.exists());
|
QVERIFY(file.exists());
|
||||||
|
}
|
||||||
|
|
||||||
// Remove
|
// Remove
|
||||||
QVERIFY(file.remove());
|
QVERIFY(file.remove());
|
||||||
QVERIFY(!file.exists());
|
QVERIFY(!file.exists());
|
||||||
|
|
||||||
|
// Move
|
||||||
|
{
|
||||||
|
showInstructionsDialog("Choose source directory of file to move");
|
||||||
|
const QString srcDir = QFileDialog::getExistingDirectory(nullptr, tr("Choose Directory"));
|
||||||
|
|
||||||
|
const QString fileName = "file_to_move.txt"_L1;
|
||||||
|
|
||||||
|
// Create a file
|
||||||
|
QFile file(srcDir + u'/' + fileName);
|
||||||
|
QVERIFY(file.open(QFile::WriteOnly));
|
||||||
|
QVERIFY(file.exists());
|
||||||
|
|
||||||
|
showInstructionsDialog("Choose target directory to where to move the file");
|
||||||
|
const QString destDir = QFileDialog::getExistingDirectory(nullptr, tr("Choose Directory"));
|
||||||
|
|
||||||
|
QVERIFY(file.rename(destDir + u'/' + fileName));
|
||||||
|
|
||||||
|
// NOTE: The uri doesn't seem to stay usable after a rename and it needs to get
|
||||||
|
// permission again via the SAF picker.
|
||||||
|
showInstructionsDialog("Choose the file that was moved");
|
||||||
|
QFileDialog::getOpenFileName(nullptr, tr("Open File"));
|
||||||
|
|
||||||
|
QVERIFY(file.remove());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_ContentUris)
|
QTEST_MAIN(tst_ContentUris)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user