From 0de2a09dda563dae11d6b21dab4b6a0b2a64b77c Mon Sep 17 00:00:00 2001 From: Victor Wads Date: Fri, 21 Jun 2024 18:16:05 -0300 Subject: [PATCH] list each user apps --- content/App.qml | 5 ++ content/CMakeLists.txt | 18 +++--- .../Devices}/ApplicationDelegate.qml | 0 content/{ => Tabs/Devices}/Device.qml | 0 .../{ => Tabs/Downloads}/DownloadDelegate.qml | 0 content/{ => Tabs/Downloads}/Downloads.qml | 0 .../{ => Tabs/Downloads}/LocalDelegate.qml | 0 content/{ => Tabs/Games}/GameDelegate.qml | 0 content/{ => Tabs/Games}/Games.qml | 0 content/{ => Tabs/Users}/Users.qml | 58 ++++++++++++++++++- content/Tabs/Users/UsersInstalledDelegate.qml | 29 ++++++++++ content/Tabs/Users/UsersToAddDelegate.qml | 29 ++++++++++ src/device_manager.cpp | 54 +++++++++++++++++ src/device_manager.h | 10 +++- src/models/user.h | 3 +- 15 files changed, 195 insertions(+), 11 deletions(-) rename content/{ => Tabs/Devices}/ApplicationDelegate.qml (100%) rename content/{ => Tabs/Devices}/Device.qml (100%) rename content/{ => Tabs/Downloads}/DownloadDelegate.qml (100%) rename content/{ => Tabs/Downloads}/Downloads.qml (100%) rename content/{ => Tabs/Downloads}/LocalDelegate.qml (100%) rename content/{ => Tabs/Games}/GameDelegate.qml (100%) rename content/{ => Tabs/Games}/Games.qml (100%) rename content/{ => Tabs/Users}/Users.qml (66%) create mode 100644 content/Tabs/Users/UsersInstalledDelegate.qml create mode 100644 content/Tabs/Users/UsersToAddDelegate.qml diff --git a/content/App.qml b/content/App.qml index 5a78a02..3f309a5 100644 --- a/content/App.qml +++ b/content/App.qml @@ -22,6 +22,11 @@ import QtQuick.Layouts import VrpManager import org.kde.kirigami as Kirigami +import "Tabs/Games/" +import "Tabs/Downloads/" +import "Tabs/Devices/" +import "Tabs/Users/" + Kirigami.ApplicationWindow { id: app diff --git a/content/CMakeLists.txt b/content/CMakeLists.txt index a380035..a08146b 100644 --- a/content/CMakeLists.txt +++ b/content/CMakeLists.txt @@ -5,14 +5,16 @@ qt6_add_qml_module(content RESOURCE_PREFIX "/qt/qml" QML_FILES App.qml - Games.qml - GameDelegate.qml - Downloads.qml - DownloadDelegate.qml - LocalDelegate.qml - Device.qml - Users.qml - ApplicationDelegate.qml + Tabs/Games/Games.qml + Tabs/Games/GameDelegate.qml + Tabs/Downloads/Downloads.qml + Tabs/Downloads/DownloadDelegate.qml + Tabs/Downloads/LocalDelegate.qml + Tabs/Devices/Device.qml + Tabs/Devices/ApplicationDelegate.qml + Tabs/Users/Users.qml + Tabs/Users/UsersInstalledDelegate.qml + Tabs/Users/UsersToAddDelegate.qml RESOURCES fonts/fonts.txt Image/matrix.png diff --git a/content/ApplicationDelegate.qml b/content/Tabs/Devices/ApplicationDelegate.qml similarity index 100% rename from content/ApplicationDelegate.qml rename to content/Tabs/Devices/ApplicationDelegate.qml diff --git a/content/Device.qml b/content/Tabs/Devices/Device.qml similarity index 100% rename from content/Device.qml rename to content/Tabs/Devices/Device.qml diff --git a/content/DownloadDelegate.qml b/content/Tabs/Downloads/DownloadDelegate.qml similarity index 100% rename from content/DownloadDelegate.qml rename to content/Tabs/Downloads/DownloadDelegate.qml diff --git a/content/Downloads.qml b/content/Tabs/Downloads/Downloads.qml similarity index 100% rename from content/Downloads.qml rename to content/Tabs/Downloads/Downloads.qml diff --git a/content/LocalDelegate.qml b/content/Tabs/Downloads/LocalDelegate.qml similarity index 100% rename from content/LocalDelegate.qml rename to content/Tabs/Downloads/LocalDelegate.qml diff --git a/content/GameDelegate.qml b/content/Tabs/Games/GameDelegate.qml similarity index 100% rename from content/GameDelegate.qml rename to content/Tabs/Games/GameDelegate.qml diff --git a/content/Games.qml b/content/Tabs/Games/Games.qml similarity index 100% rename from content/Games.qml rename to content/Tabs/Games/Games.qml diff --git a/content/Users.qml b/content/Tabs/Users/Users.qml similarity index 66% rename from content/Users.qml rename to content/Tabs/Users/Users.qml index 59fb156..15a86a2 100644 --- a/content/Users.qml +++ b/content/Tabs/Users/Users.qml @@ -35,7 +35,13 @@ RowLayout { user_running.text = app.deviceManager.selectedUserIsLogged ? qsTr("Yes") : qsTr("No"); - installed_apps.text = app.deviceManager.selectedUsersInstalledApps; + onUserAppsListChanged(); + } + + function onUserAppsListChanged() { + installed_apps.text = app.deviceManager.selectedUsersInstalledApps > -1 + ? app.deviceManager.selectedUsersInstalledApps + : qsTr("loading..."); } target: app.deviceManager @@ -106,4 +112,54 @@ RowLayout { } } } + + GridView { + id: apps_info + + Layout.fillHeight: true + Layout.fillWidth: true + snapMode: GridView.SnapToRow + clip: true + cellWidth: 220 + cellHeight: 180 + model: app.deviceManager.userAppsListModel() + + Dialog { + id: confirm_dialog + + property string packageName: "" + property int index: -1 + + anchors.centerIn: parent + title: "Uninstall " + packageName + standardButtons: Dialog.Ok | Dialog.Cancel + onAccepted: { + apps_info.model.remove(index); + app.deviceManager.uninstallApkQml(packageName); + } + } + + ScrollBar.vertical: ScrollBar { + visible: true + } + + delegate: UsersInstalledDelegate { + width: apps_info.cellWidth - 10 + height: apps_info.cellHeight - 10 + name: model.package_name + thumbnailPath: { + let path = app.vrp.getGameThumbnailPath(model.package_name); + if (path === "") + return "qrc:/qt/qml/content/Image/matrix.png"; + else + return "file://" + path; + } + // onUninstallButtonClicked: { + // confirm_dialog.packageName = model.package_name; + // confirm_dialog.index = index; + // confirm_dialog.open(); + // } + } + + } } diff --git a/content/Tabs/Users/UsersInstalledDelegate.qml b/content/Tabs/Users/UsersInstalledDelegate.qml new file mode 100644 index 0000000..4256c6f --- /dev/null +++ b/content/Tabs/Users/UsersInstalledDelegate.qml @@ -0,0 +1,29 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import VrpManager +import org.kde.kirigami as Kirigami + +Kirigami.Card { + property string name + property string thumbnailPath + + signal removeButtonClicked() + + banner { + source: thumbnailPath + title: name + titleAlignment: Qt.AlignLeft | Qt.AlignBottom + } + + footer: Button { + id: action_button + icon.name: "uninstall" + text: qsTr("Remove from user") + enabled: false + onClicked: { + removeButtonClicked(); + } + } + +} diff --git a/content/Tabs/Users/UsersToAddDelegate.qml b/content/Tabs/Users/UsersToAddDelegate.qml new file mode 100644 index 0000000..e1b3ea0 --- /dev/null +++ b/content/Tabs/Users/UsersToAddDelegate.qml @@ -0,0 +1,29 @@ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import VrpManager +import org.kde.kirigami as Kirigami + +Kirigami.Card { + property string name + property string thumbnailPath + + signal addButtonClicked() + + banner { + source: thumbnailPath + title: name + titleAlignment: Qt.AlignLeft | Qt.AlignBottom + } + + footer: Button { + id: action_button + icon.name: "install" + text: qsTr("Remove from user") + onClicked: { + addButtonClicked(); + } + } + +} diff --git a/src/device_manager.cpp b/src/device_manager.cpp index 02c05f4..278775a 100644 --- a/src/device_manager.cpp +++ b/src/device_manager.cpp @@ -40,6 +40,7 @@ DeviceManager::DeviceManager(QObject *parent) connect(&auto_update_timer_, &QTimer::timeout, this, &DeviceManager::updateSerials); connect(this, &DeviceManager::connectedDeviceChanged, this, &DeviceManager::updateDeviceInfo); connect(this, &DeviceManager::connectedDeviceChanged, this, &DeviceManager::updateUsers); + connect(this, &DeviceManager::userInfoChanged, this, &DeviceManager::listPackagesForUser); } DeviceManager::~DeviceManager() @@ -881,6 +882,11 @@ QCoro::Task DeviceManager::updateUsers() co_return; } + /* EXAMPLE OUTPUT: + Users: + UserInfo{0:Victor Wads:c13} running + UserInfo{10:Afonso Ivo Cunha:410} + */ QString output = basic_process.readAllStandardOutput(); QStringList lines = output.split("\n"); lines.removeAll(""); @@ -928,4 +934,52 @@ QCoro::Task DeviceManager::selectUser(int index) { selected_user_ = &users_list_[index]; emit userInfoChanged(); +} + +QCoro::Task DeviceManager::listPackagesForUser() +{ + + user_apps_list_model_.clear(); + if (!hasConnectedDevice()) { + emit userAppsListChanged(); + co_return; + } + auto serial = connectedDevice(); + + QProcess basic_process; + QString id = QString::number(selected_user_->id); + auto adb = qCoro(basic_process); + adb.start(resolvePrefix(ADB_PATH), {"-s", serial, "shell", "pm", "list", "packages", "--user", id, "--show-versioncode", "-3"}); + + co_await adb.waitForFinished(); + + if (basic_process.exitStatus() != QProcess::NormalExit || basic_process.exitCode() != 0) { + qWarning() << "Failed to get apps for device" << serial; + co_return; + } + + /* EXAMPLE OUTPUT: + package:com.facebook.arvr.quillplayer versionCode:135 + package:com.oculus.mobile_mrc_setup versionCode:1637173263 + */ + QString output = basic_process.readAllStandardOutput(); + QStringList lines = output.split("\n"); + lines.removeAll(""); + QRegularExpression re("package:(\\S+) versionCode:(\\d+)"); + + user_apps_list_model_.clear(); + for (const QString &line : lines) { + auto match = re.match(line); + if (match.hasMatch()) { + QString package_name = match.captured(1); + QString version_code = match.captured(2); + auto app = GameInfo{.package_name = package_name, .version_code = version_code}; + + user_apps_list_model_.append(app); + } + } + selected_user_->installedApps = user_apps_list_model_.size(); + + emit userAppsListChanged(); + co_return; } \ No newline at end of file diff --git a/src/device_manager.h b/src/device_manager.h index ff635ed..6610066 100644 --- a/src/device_manager.h +++ b/src/device_manager.h @@ -86,6 +86,11 @@ class DeviceManager : public QObject return &app_list_model_; } + Q_INVOKABLE GameInfoModel *userAppsListModel() + { + return &user_apps_list_model_; + } + Q_INVOKABLE QCoro::Task updateSerials(); Q_INVOKABLE void updateDeviceInfo(); Q_INVOKABLE QCoro::Task updateDeviceName(); @@ -100,6 +105,7 @@ class DeviceManager : public QObject Q_INVOKABLE QCoro::Task updateAppList(); Q_INVOKABLE QCoro::Task updateUsers(); Q_INVOKABLE QCoro::Task selectUser(int index); + Q_INVOKABLE QCoro::Task listPackagesForUser(); Q_INVOKABLE QString selectedUserName() const { @@ -118,7 +124,7 @@ class DeviceManager : public QObject Q_INVOKABLE int selectedUsersInstalledApps() const { - return 0; + return selected_user_ ? selected_user_->installedApps : -1; } Q_INVOKABLE QString runningUserName() const @@ -301,12 +307,14 @@ class DeviceManager : public QObject void oculusRuntimeVersionChanged(QString oculus_runtime_version); void androidVersionChanged(int android_version); void androidSdkVersionChanged(int android_sdk_version); + void userAppsListChanged(); void usersListChanged(); void userInfoChanged(); private: QStringList devices_list_; GameInfoModel app_list_model_; + GameInfoModel user_apps_list_model_; QTimer auto_update_timer_; QString connected_device_; QString device_name_; diff --git a/src/models/user.h b/src/models/user.h index e452886..06325f9 100644 --- a/src/models/user.h +++ b/src/models/user.h @@ -6,10 +6,11 @@ class User { public: User(int id, const QString &name, bool running) - : name(name), id(id), running(running) {} + : name(name), id(id), running(running), installedApps(-1) {} public: QString name; int id; bool running; + int installedApps; }; \ No newline at end of file