From 133ebab62cfb61a960689d912f33a4e8a8693e8e Mon Sep 17 00:00:00 2001 From: Andrew Lindesay Date: Wed, 17 Mar 2021 00:09:05 +1300 Subject: [PATCH] HaikuDepot: Increment Pkg View Counter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #16814 Change-Id: Idf451628b4680fb33563dbf4817bd11049c326b5 Reviewed-on: https://review.haiku-os.org/c/haiku/+/3803 Reviewed-by: Jérôme Duval --- src/apps/haikudepot/HaikuDepotConstants.h | 11 +- src/apps/haikudepot/Jamfile | 2 + src/apps/haikudepot/model/Model.cpp | 10 +- src/apps/haikudepot/model/Model.h | 8 +- src/apps/haikudepot/model/PackageInfo.cpp | 13 ++ src/apps/haikudepot/model/PackageInfo.h | 8 +- .../server/IncrementViewCounterProcess.cpp | 119 ++++++++++++++++++ .../server/IncrementViewCounterProcess.h | 38 ++++++ .../server/ProcessCoordinatorFactory.cpp | 18 ++- .../server/ProcessCoordinatorFactory.h | 11 +- .../haikudepot/server/WebAppInterface.cpp | 57 ++++++++- src/apps/haikudepot/server/WebAppInterface.h | 8 +- src/apps/haikudepot/ui/MainWindow.cpp | 109 ++++++++++++++-- src/apps/haikudepot/ui/MainWindow.h | 8 ++ src/apps/haikudepot/ui/SettingsWindow.cpp | 111 ++++++++++++++++ src/apps/haikudepot/ui/SettingsWindow.h | 45 +++++++ 16 files changed, 553 insertions(+), 23 deletions(-) create mode 100644 src/apps/haikudepot/server/IncrementViewCounterProcess.cpp create mode 100644 src/apps/haikudepot/server/IncrementViewCounterProcess.h create mode 100644 src/apps/haikudepot/ui/SettingsWindow.cpp create mode 100644 src/apps/haikudepot/ui/SettingsWindow.h diff --git a/src/apps/haikudepot/HaikuDepotConstants.h b/src/apps/haikudepot/HaikuDepotConstants.h index 1a85273988..b3b9cbdefb 100644 --- a/src/apps/haikudepot/HaikuDepotConstants.h +++ b/src/apps/haikudepot/HaikuDepotConstants.h @@ -1,5 +1,5 @@ /* - * Copyright 2018-2020, Andrew Lindesay . + * Copyright 2018-2021, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ #ifndef HAIKU_DEPOT_CONSTANTS_H @@ -73,6 +73,15 @@ enum BitmapSize { #define KEY_MAIN_SETTINGS "main_settings" +#define SETTING_SHOW_AVAILABLE_PACKAGES "show available packages" +#define SETTING_SHOW_INSTALLED_PACKAGES "show installed packages" +#define SETTING_SHOW_DEVELOP_PACKAGES "show develop packages" +#define SETTING_SHOW_SOURCE_PACKAGES "show source packages" +#define SETTING_CAN_SHARE_ANONYMOUS_USER_DATA "can share anonymous usage data" +#define SETTING_PACKAGE_LIST_VIEW_MODE "packageListViewMode" + // unfortunately historical difference in casing. + + // These constants reference resources in 'HaikuDepot.ref' enum { RSRC_STAR_BLUE = 510, diff --git a/src/apps/haikudepot/Jamfile b/src/apps/haikudepot/Jamfile index 67875ff2ec..6fbb92ff5a 100644 --- a/src/apps/haikudepot/Jamfile +++ b/src/apps/haikudepot/Jamfile @@ -120,6 +120,7 @@ local applicationSources = FeaturedPackagesView.cpp FilterView.cpp IconTarPtr.cpp + IncrementViewCounterProcess.cpp JobStateListener.cpp LanguageModel.cpp LinkView.cpp @@ -146,6 +147,7 @@ local applicationSources = support.cpp ScreenshotWindow.cpp ScrollableGroupView.cpp + SettingsWindow.cpp SharedBitmap.cpp ToLatestUserUsageConditionsWindow.cpp UserCredentials.cpp diff --git a/src/apps/haikudepot/model/Model.cpp b/src/apps/haikudepot/model/Model.cpp index fa2fd66f54..7827cdbd19 100644 --- a/src/apps/haikudepot/model/Model.cpp +++ b/src/apps/haikudepot/model/Model.cpp @@ -212,7 +212,8 @@ Model::Model() fShowAvailablePackages(true), fShowInstalledPackages(true), fShowSourcePackages(false), - fShowDevelopPackages(false) + fShowDevelopPackages(false), + fCanShareAnonymousUsageData(false) { } @@ -436,6 +437,13 @@ Model::SetPackageListViewMode(package_list_view_mode mode) } +void +Model::SetCanShareAnonymousUsageData(bool value) +{ + fCanShareAnonymousUsageData = value; +} + + void Model::SetShowAvailablePackages(bool show) { diff --git a/src/apps/haikudepot/model/Model.h b/src/apps/haikudepot/model/Model.h index 268efaf012..1636fe0b67 100644 --- a/src/apps/haikudepot/model/Model.h +++ b/src/apps/haikudepot/model/Model.h @@ -129,6 +129,9 @@ public: void SetShowDevelopPackages(bool show); bool ShowDevelopPackages() const { return fShowDevelopPackages; } + void SetCanShareAnonymousUsageData(bool value); + bool CanShareAnonymousUsageData() const + { return fCanShareAnonymousUsageData; } // Retrieve package information static const uint32 POPULATE_CACHED_RATING = 1 << 0; @@ -150,8 +153,8 @@ public: const BString& passwordClear, bool storePassword); - const WebAppInterface& - GetWebAppInterface() const + WebAppInterface& + GetWebAppInterface() { return fWebAppInterface; } status_t IconTarPath(BPath& path) const; @@ -205,6 +208,7 @@ private: bool fShowInstalledPackages; bool fShowSourcePackages; bool fShowDevelopPackages; + bool fCanShareAnonymousUsageData; LanguageModel fLanguageModel; PackageIconTarRepository diff --git a/src/apps/haikudepot/model/PackageInfo.cpp b/src/apps/haikudepot/model/PackageInfo.cpp index 8af55349b7..a203502ff4 100644 --- a/src/apps/haikudepot/model/PackageInfo.cpp +++ b/src/apps/haikudepot/model/PackageInfo.cpp @@ -453,6 +453,7 @@ PackageInfo::PackageInfo() fFileName(), fSize(0), fDepotName(""), + fViewed(false), fIsCollatingChanges(false), fCollatedChanges(0) { @@ -483,6 +484,7 @@ PackageInfo::PackageInfo(const BPackageInfo& info) fFileName(info.FileName()), fSize(0), // TODO: Retrieve local file size fDepotName(""), + fViewed(false), fIsCollatingChanges(false), fCollatedChanges(0) { @@ -529,6 +531,7 @@ PackageInfo::PackageInfo(const BString& name, fFileName(), fSize(0), fDepotName(""), + fViewed(false), fIsCollatingChanges(false), fCollatedChanges(0) { @@ -561,6 +564,7 @@ PackageInfo::PackageInfo(const PackageInfo& other) fFileName(other.fFileName), fSize(other.fSize), fDepotName(other.fDepotName), + fViewed(other.fViewed), fIsCollatingChanges(false), fCollatedChanges(0) { @@ -593,6 +597,8 @@ PackageInfo::operator=(const PackageInfo& other) fLocalFilePath = other.fLocalFilePath; fFileName = other.fFileName; fSize = other.fSize; + fDepotName = other.fDepotName; + fViewed = other.fViewed; return *this; } @@ -987,6 +993,13 @@ PackageInfo::SetSize(int64 size) } +void +PackageInfo::SetViewed() +{ + fViewed = true; +} + + void PackageInfo::SetDepotName(const BString& depotName) { diff --git a/src/apps/haikudepot/model/PackageInfo.h b/src/apps/haikudepot/model/PackageInfo.h index 6895479db1..edc1c8f54e 100644 --- a/src/apps/haikudepot/model/PackageInfo.h +++ b/src/apps/haikudepot/model/PackageInfo.h @@ -253,7 +253,8 @@ public: { return fName; } void SetTitle(const BString& title); const BString& Title() const; - const BPackageVersion& Version() const + const BPackageVersion& + Version() const { return fVersion; } void SetShortDescription(const BString& description); const BString& ShortDescription() const @@ -336,6 +337,10 @@ public: int64 Size() const { return fSize; } + void SetViewed(); + bool Viewed() const + { return fViewed; } + void SetDepotName(const BString& depotName); const BString& DepotName() const { return fDepotName; } @@ -387,6 +392,7 @@ private: BString fFileName; int64 fSize; BString fDepotName; + bool fViewed; bool fIsCollatingChanges; uint32 fCollatedChanges; diff --git a/src/apps/haikudepot/server/IncrementViewCounterProcess.cpp b/src/apps/haikudepot/server/IncrementViewCounterProcess.cpp new file mode 100644 index 0000000000..367a57129a --- /dev/null +++ b/src/apps/haikudepot/server/IncrementViewCounterProcess.cpp @@ -0,0 +1,119 @@ +/* + * Copyright 2021, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#include "IncrementViewCounterProcess.h" + +#include + +#include "Logger.h" +#include "ServerHelper.h" +#include "WebAppInterface.h" + + +#define ATTEMPTS 3 +#define SPIN_BETWEEN_ATTEMPTS_DELAY_MI 5 * 1000 * 1000 + // 5 seconds + +#undef B_TRANSLATION_CONTEXT +#define B_TRANSLATION_CONTEXT "IncrementViewCounterProcess" + + +IncrementViewCounterProcess::IncrementViewCounterProcess( + Model* model, const PackageInfoRef package) + : + fPackage(package), + fModel(model) +{ + fDescription = BString(B_TRANSLATE("Recording view of \"%PackageName%\"")) + .ReplaceAll("%PackageName%", fPackage->Name()); +} + + +IncrementViewCounterProcess::~IncrementViewCounterProcess() +{ +} + + +const char* +IncrementViewCounterProcess::Name() const +{ + return "IncrementViewCounterProcess"; +} + + +const char* +IncrementViewCounterProcess::Description() const +{ + return fDescription.String(); +} + + +status_t +IncrementViewCounterProcess::RunInternal() +{ + if (!ServerHelper::IsNetworkAvailable()) { + HDINFO("no network so will not increment view counter"); + return B_OK; + } + + if (!fPackage.IsSet()) { + HDERROR("the package is not present to increment the view counter"); + return B_ERROR; + } + + DepotInfoRef depot = fModel->DepotForName(fPackage->DepotName()); + + if (!depot.IsSet()) { + HDERROR("the package's depot is not present to increment the view " + "counter"); + return B_ERROR; + } + + int32 attempts = ATTEMPTS; + status_t result; + + while (attempts > 0 && !WasStopped()) { + BMessage resultEnvelope; + WebAppInterface& webAppInterface = fModel->GetWebAppInterface(); + result = webAppInterface.IncrementViewCounter(fPackage, depot, + resultEnvelope); + + if (result == B_OK) { + int32 errorCode = webAppInterface.ErrorCodeFromResponse( + resultEnvelope); + switch (errorCode) { + case ERROR_CODE_NONE: + HDINFO("did increment the view counter for [%s]", + fPackage->Name().String()); + return result; + case ERROR_CODE_OBJECTNOTFOUND: + HDINFO("server was not able to find the package [%s]", + fPackage->Name().String()); + return B_NAME_NOT_FOUND; + default: + HDERROR("a problem has arisen incrementing the view " + "counter for pkg [%s] w/ error code %" B_PRId32, + fPackage->Name().String(), errorCode); + result = B_ERROR; + break; + } + } else + HDERROR("an error has arisen incrementing the view counter"); + + attempts--; + _SpinBetweenAttempts(); + } + + return result; +} + + +void +IncrementViewCounterProcess::_SpinBetweenAttempts() +{ + useconds_t miniSpinDelays = SPIN_BETWEEN_ATTEMPTS_DELAY_MI / 10; + for (int32 i = 0; i < 10 && !WasStopped(); i++) + usleep(miniSpinDelays); +} + diff --git a/src/apps/haikudepot/server/IncrementViewCounterProcess.h b/src/apps/haikudepot/server/IncrementViewCounterProcess.h new file mode 100644 index 0000000000..d36aec4838 --- /dev/null +++ b/src/apps/haikudepot/server/IncrementViewCounterProcess.h @@ -0,0 +1,38 @@ +/* + * Copyright 2021, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef INCREMENT_VIEW_COUNTER_PROCESS_H +#define INCREMENT_VIEW_COUNTER_PROCESS_H + +#include "AbstractProcess.h" +#include "Model.h" +#include "PackageInfo.h" + + +class Model; + + +class IncrementViewCounterProcess : public AbstractProcess { +public: + IncrementViewCounterProcess( + Model* model, + const PackageInfoRef package); + virtual ~IncrementViewCounterProcess(); + + const char* Name() const; + const char* Description() const; + +protected: + virtual status_t RunInternal(); + +private: + void _SpinBetweenAttempts(); + +private: + BString fDescription; + PackageInfoRef fPackage; + Model* fModel; +}; + +#endif // INCREMENT_VIEW_COUNTER_PROCESS_H diff --git a/src/apps/haikudepot/server/ProcessCoordinatorFactory.cpp b/src/apps/haikudepot/server/ProcessCoordinatorFactory.cpp index 8c65869cd7..3c933d4746 100644 --- a/src/apps/haikudepot/server/ProcessCoordinatorFactory.cpp +++ b/src/apps/haikudepot/server/ProcessCoordinatorFactory.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2018-2020, Andrew Lindesay . + * Copyright 2018-2021, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ @@ -14,6 +14,7 @@ #include "AbstractServerProcess.h" #include "HaikuDepotConstants.h" +#include "IncrementViewCounterProcess.h" #include "LocalPkgDataLoadProcess.h" #include "LocalRepositoryUpdateProcess.h" #include "Logger.h" @@ -34,6 +35,21 @@ using namespace BPackageKit; +/*static*/ ProcessCoordinator* +ProcessCoordinatorFactory::CreateIncrementViewCounter( + ProcessCoordinatorListener* processCoordinatorListener, + Model* model, const PackageInfoRef package) +{ + ProcessCoordinator* processCoordinator = new ProcessCoordinator( + "IncrementViewCounter", + processCoordinatorListener); + ProcessNode* node = new ProcessNode( + new IncrementViewCounterProcess(model, package)); + processCoordinator->AddNode(node); + return processCoordinator; +} + + /*static*/ ProcessCoordinator* ProcessCoordinatorFactory::CreateUserDetailVerifierCoordinator( UserDetailVerifierListener* userDetailVerifierListener, diff --git a/src/apps/haikudepot/server/ProcessCoordinatorFactory.h b/src/apps/haikudepot/server/ProcessCoordinatorFactory.h index 41ca2710ab..8766ca2fc2 100644 --- a/src/apps/haikudepot/server/ProcessCoordinatorFactory.h +++ b/src/apps/haikudepot/server/ProcessCoordinatorFactory.h @@ -1,5 +1,5 @@ /* - * Copyright 2018, Andrew Lindesay . + * Copyright 2018-2021, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ @@ -9,6 +9,8 @@ #include +#include "PackageInfo.h" + class Model; class PackageInfoListener; class ProcessCoordinator; @@ -21,6 +23,11 @@ class UserDetailVerifierListener; class ProcessCoordinatorFactory { public: + static ProcessCoordinator* CreateIncrementViewCounter( + ProcessCoordinatorListener* + processCoordinatorListener, + Model* model, const PackageInfoRef package); + static ProcessCoordinator* CreateBulkLoadCoordinator( PackageInfoListener *packageInfoListener, ProcessCoordinatorListener* @@ -38,4 +45,4 @@ private: }; -#endif // PROCESS_COORDINATOR_FACTORY_H \ No newline at end of file +#endif // PROCESS_COORDINATOR_FACTORY_H diff --git a/src/apps/haikudepot/server/WebAppInterface.cpp b/src/apps/haikudepot/server/WebAppInterface.cpp index 9977f2291f..2a9f0ba944 100644 --- a/src/apps/haikudepot/server/WebAppInterface.cpp +++ b/src/apps/haikudepot/server/WebAppInterface.cpp @@ -1,6 +1,6 @@ /* * Copyright 2014, Stephan Aßmus . - * Copyright 2016-2020, Andrew Lindesay . + * Copyright 2016-2021, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ @@ -758,6 +758,61 @@ WebAppInterface::AuthenticateUser(const BString& nickName, } +status_t +WebAppInterface::IncrementViewCounter(const PackageInfoRef package, + const DepotInfoRef depot, BMessage& message) +{ + BMallocIO* requestEnvelopeData = new BMallocIO(); + // BHttpRequest later takes ownership of this. + BJsonTextWriter requestEnvelopeWriter(requestEnvelopeData); + + requestEnvelopeWriter.WriteObjectStart(); + _WriteStandardJsonRpcEnvelopeValues(requestEnvelopeWriter, + "incrementViewCounter"); + requestEnvelopeWriter.WriteObjectName("params"); + requestEnvelopeWriter.WriteArrayStart(); + requestEnvelopeWriter.WriteObjectStart(); + + requestEnvelopeWriter.WriteObjectName("architectureCode"); + requestEnvelopeWriter.WriteString(package->Architecture()); + requestEnvelopeWriter.WriteObjectName("repositoryCode"); + requestEnvelopeWriter.WriteString(depot->WebAppRepositoryCode()); + requestEnvelopeWriter.WriteObjectName("name"); + requestEnvelopeWriter.WriteString(package->Name()); + + const BPackageVersion version = package->Version(); + if (!version.Major().IsEmpty()) { + requestEnvelopeWriter.WriteObjectName("major"); + requestEnvelopeWriter.WriteString(version.Major()); + } + if (!version.Minor().IsEmpty()) { + requestEnvelopeWriter.WriteObjectName("minor"); + requestEnvelopeWriter.WriteString(version.Minor()); + } + if (!version.Micro().IsEmpty()) { + requestEnvelopeWriter.WriteObjectName("micro"); + requestEnvelopeWriter.WriteString(version.Micro()); + } + if (!version.PreRelease().IsEmpty()) { + requestEnvelopeWriter.WriteObjectName("preRelease"); + requestEnvelopeWriter.WriteString(version.PreRelease()); + } + if (version.Revision() != 0) { + requestEnvelopeWriter.WriteObjectName("revision"); + requestEnvelopeWriter.WriteInteger( + static_cast(version.Revision())); + } + + requestEnvelopeWriter.WriteObjectEnd(); + requestEnvelopeWriter.WriteArrayEnd(); + requestEnvelopeWriter.WriteObjectEnd(); + + return _SendJsonRequest("pkg", requestEnvelopeData, + _LengthAndSeekToZero(requestEnvelopeData), 0, + message); +} + + /*! JSON-RPC invocations return a response. The response may be either a result or it may be an error depending on the response structure. If it is an error then there may be additional detail that is the diff --git a/src/apps/haikudepot/server/WebAppInterface.h b/src/apps/haikudepot/server/WebAppInterface.h index d36adcfcc7..1f2d76842d 100644 --- a/src/apps/haikudepot/server/WebAppInterface.h +++ b/src/apps/haikudepot/server/WebAppInterface.h @@ -1,6 +1,6 @@ /* * Copyright 2014, Stephan Aßmus . - * Copyright 2016-2020, Andrew Lindesay . + * Copyright 2016-2021, Andrew Lindesay . * All rights reserved. Distributed under the terms of the MIT License. */ #ifndef WEB_APP_INTERFACE_H @@ -12,6 +12,7 @@ #include #include +#include "PackageInfo.h" #include "UserCredentials.h" #include "UserDetail.h" #include "UserUsageConditions.h" @@ -120,6 +121,11 @@ public: const BString& passwordClear, BMessage& message); + status_t IncrementViewCounter( + const PackageInfoRef package, + const DepotInfoRef depot, + BMessage& message); + static int32 ErrorCodeFromResponse( BMessage& responseEnvelopeMessage); diff --git a/src/apps/haikudepot/ui/MainWindow.cpp b/src/apps/haikudepot/ui/MainWindow.cpp index b444055182..b567f798ee 100644 --- a/src/apps/haikudepot/ui/MainWindow.cpp +++ b/src/apps/haikudepot/ui/MainWindow.cpp @@ -47,6 +47,7 @@ #include "RatePackageWindow.h" #include "support.h" #include "ScreenshotWindow.h" +#include "SettingsWindow.h" #include "ToLatestUserUsageConditionsWindow.h" #include "UserLoginWindow.h" #include "UserUsageConditionsWindow.h" @@ -61,6 +62,7 @@ enum { MSG_REFRESH_REPOS = 'mrrp', MSG_MANAGE_REPOS = 'mmrp', MSG_SOFTWARE_UPDATER = 'mswu', + MSG_SETTINGS = 'stgs', MSG_LOG_IN = 'lgin', MSG_AUTHORIZATION_CHANGED = 'athc', MSG_CATEGORIES_LIST_CHANGED = 'clic', @@ -76,7 +78,6 @@ enum { }; #define KEY_ERROR_STATUS "errorStatus" -#define KEY_PACKAGE_LIST_VIEW_MODE "packageListViewMode" #define TAB_PROMINENT_PACKAGES 0 #define TAB_ALL_PACKAGES 1 @@ -195,6 +196,7 @@ MainWindow::MainWindow(const BMessage& settings) fPackageListView->LoadState(&columnSettings); _RestoreModelSettings(settings); + _MaybePromptCanShareAnonymousUserData(settings); if (fModel.PackageListViewMode() == PROMINENT) fListTabs->Select(TAB_PROMINENT_PACKAGES); @@ -343,6 +345,10 @@ MainWindow::MessageReceived(BMessage* message) _OpenLoginWindow(BMessage()); break; + case MSG_SETTINGS: + _OpenSettingsWindow(); + break; + case MSG_LOG_OUT: fModel.SetNickname(""); break; @@ -431,8 +437,10 @@ MainWindow::MessageReceived(BMessage* message) } if (!package.IsSet() || name != package->Name()) debugger("unable to find the named package"); - else + else { _AdoptPackage(package); + _IncrementViewCounter(package); + } } else { _ClearPackage(); } @@ -577,15 +585,19 @@ MainWindow::StoreSettings(BMessage& settings) const settings.AddMessage("column settings", &columnSettings); - settings.AddString(KEY_PACKAGE_LIST_VIEW_MODE, + settings.AddString(SETTING_PACKAGE_LIST_VIEW_MODE, main_window_package_list_view_mode_str( fModel.PackageListViewMode())); - settings.AddBool("show available packages", + settings.AddBool(SETTING_SHOW_AVAILABLE_PACKAGES, fModel.ShowAvailablePackages()); - settings.AddBool("show installed packages", + settings.AddBool(SETTING_SHOW_INSTALLED_PACKAGES, fModel.ShowInstalledPackages()); - settings.AddBool("show develop packages", fModel.ShowDevelopPackages()); - settings.AddBool("show source packages", fModel.ShowSourcePackages()); + settings.AddBool(SETTING_SHOW_DEVELOP_PACKAGES, + fModel.ShowDevelopPackages()); + settings.AddBool(SETTING_SHOW_SOURCE_PACKAGES, + fModel.ShowSourcePackages()); + settings.AddBool(SETTING_CAN_SHARE_ANONYMOUS_USER_DATA, + fModel.CanShareAnonymousUsageData()); } settings.AddString("username", fModel.Nickname()); @@ -627,6 +639,14 @@ MainWindow::GetModel() void MainWindow::_BuildMenu(BMenuBar* menuBar) { + BMenu* windowMenu = new BMenu(B_TRANSLATE("Window")); + windowMenu->AddItem(new BMenuItem(B_TRANSLATE("Settings" B_UTF8_ELLIPSIS), + new BMessage(MSG_SETTINGS))); + windowMenu->AddSeparatorItem(); + windowMenu->AddItem(new BMenuItem(B_TRANSLATE("Quit" B_UTF8_ELLIPSIS), + new BMessage(B_QUIT_REQUESTED), 'Q')); + menuBar->AddItem(windowMenu); + BMenu* menu = new BMenu(B_TRANSLATE("Tools")); fRefreshRepositoriesItem = new BMenuItem( B_TRANSLATE("Refresh repositories"), new BMessage(MSG_REFRESH_REPOS)); @@ -635,7 +655,6 @@ MainWindow::_BuildMenu(BMenuBar* menuBar) B_UTF8_ELLIPSIS), new BMessage(MSG_MANAGE_REPOS))); menu->AddItem(new BMenuItem(B_TRANSLATE("Check for updates" B_UTF8_ELLIPSIS), new BMessage(MSG_SOFTWARE_UPDATER))); - menuBar->AddItem(menu); fRepositoryMenu = new BMenu(B_TRANSLATE("Repositories")); @@ -756,21 +775,52 @@ void MainWindow::_RestoreModelSettings(const BMessage& settings) { BString packageListViewMode; - if (settings.FindString(KEY_PACKAGE_LIST_VIEW_MODE, + if (settings.FindString(SETTING_PACKAGE_LIST_VIEW_MODE, &packageListViewMode) == B_OK) { fModel.SetPackageListViewMode( main_window_str_to_package_list_view_mode(packageListViewMode)); } bool showOption; - if (settings.FindBool("show available packages", &showOption) == B_OK) + if (settings.FindBool(SETTING_SHOW_AVAILABLE_PACKAGES, &showOption) == B_OK) fModel.SetShowAvailablePackages(showOption); - if (settings.FindBool("show installed packages", &showOption) == B_OK) + if (settings.FindBool(SETTING_SHOW_INSTALLED_PACKAGES, &showOption) == B_OK) fModel.SetShowInstalledPackages(showOption); - if (settings.FindBool("show develop packages", &showOption) == B_OK) + if (settings.FindBool(SETTING_SHOW_DEVELOP_PACKAGES, &showOption) == B_OK) fModel.SetShowDevelopPackages(showOption); - if (settings.FindBool("show source packages", &showOption) == B_OK) + if (settings.FindBool(SETTING_SHOW_SOURCE_PACKAGES, &showOption) == B_OK) fModel.SetShowSourcePackages(showOption); + if (settings.FindBool(SETTING_CAN_SHARE_ANONYMOUS_USER_DATA, + &showOption) == B_OK) { + fModel.SetCanShareAnonymousUsageData(showOption); + } +} + + +void +MainWindow::_MaybePromptCanShareAnonymousUserData(const BMessage& settings) +{ + bool showOption; + if (settings.FindBool(SETTING_CAN_SHARE_ANONYMOUS_USER_DATA, + &showOption) == B_NAME_NOT_FOUND) { + _PromptCanShareAnonymousUserData(); + } +} + + +void +MainWindow::_PromptCanShareAnonymousUserData() +{ + BAlert* alert = new(std::nothrow) BAlert( + B_TRANSLATE("Sending anonymous usage data"), + B_TRANSLATE("Would it be acceptable to send anonymous usage data to the" + " HaikuDepotServer system from this computer? You can change your" + " preference in the \"Settings\" window later."), + B_TRANSLATE("No"), + B_TRANSLATE("Yes")); + + int32 result = alert->Go(); + fModel.SetCanShareAnonymousUsageData(1 == result); } @@ -866,6 +916,31 @@ MainWindow::_AddRemovePackageFromLists(const PackageInfoRef& package) } +void +MainWindow::_IncrementViewCounter(const PackageInfoRef& package) +{ + bool shouldIncrementViewCounter = false; + + { + AutoLocker modelLocker(fModel.Lock()); + bool canShareAnonymousUsageData = fModel.CanShareAnonymousUsageData(); + if (canShareAnonymousUsageData && !package->Viewed()) { + package->SetViewed(); + shouldIncrementViewCounter = true; + } + } + + if (shouldIncrementViewCounter) { + ProcessCoordinator* bulkLoadCoordinator = + ProcessCoordinatorFactory::CreateIncrementViewCounter( + this, + // ProcessCoordinatorListener + &fModel, package); + _AddProcessCoordinator(bulkLoadCoordinator); + } +} + + void MainWindow::_AdoptPackage(const PackageInfoRef& package) { @@ -1082,6 +1157,14 @@ MainWindow::_PopulatePackageWorker(void* arg) } +void +MainWindow::_OpenSettingsWindow() +{ + SettingsWindow* window = new SettingsWindow(this, &fModel); + window->Show(); +} + + void MainWindow::_OpenLoginWindow(const BMessage& onSuccessMessage) { diff --git a/src/apps/haikudepot/ui/MainWindow.h b/src/apps/haikudepot/ui/MainWindow.h index b50d4d6eb2..dbaf1960dc 100644 --- a/src/apps/haikudepot/ui/MainWindow.h +++ b/src/apps/haikudepot/ui/MainWindow.h @@ -88,6 +88,10 @@ private: void _RestoreWindowFrame(const BMessage& settings); void _RestoreModelSettings(const BMessage& settings); + void _MaybePromptCanShareAnonymousUserData( + const BMessage& settings); + void _PromptCanShareAnonymousUserData(); + void _InitWorkerThreads(); void _AdoptModelControls(); void _AdoptModel(); @@ -97,6 +101,9 @@ private: void _AdoptPackage(const PackageInfoRef& package); void _ClearPackage(); + void _IncrementViewCounter( + const PackageInfoRef& package); + void _PopulatePackageAsync(bool forcePopulate); void _StartBulkLoad(bool force = false); void _BulkLoadCompleteReceived(status_t errorStatus); @@ -118,6 +125,7 @@ private: void _OpenLoginWindow( const BMessage& onSuccessMessage); + void _OpenSettingsWindow(); void _StartUserVerify(); void _UpdateAuthorization(); void _UpdateAvailableRepositories(); diff --git a/src/apps/haikudepot/ui/SettingsWindow.cpp b/src/apps/haikudepot/ui/SettingsWindow.cpp new file mode 100644 index 0000000000..baeaef2310 --- /dev/null +++ b/src/apps/haikudepot/ui/SettingsWindow.cpp @@ -0,0 +1,111 @@ +/* + * Copyright 2021, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#include "SettingsWindow.h" + +#include +#include +#include +#include +#include +#include + +#include "Logger.h" +#include "Model.h" +#include "UserUsageConditionsWindow.h" +#include "ServerHelper.h" +#include "WebAppInterface.h" + + +#undef B_TRANSLATION_CONTEXT +#define B_TRANSLATION_CONTEXT "SettingsWindow" + +#define WINDOW_FRAME BRect(0, 0, 500, 280) + + +enum { + MSG_APPLY = 'aply', +}; + + +SettingsWindow::SettingsWindow(BWindow* parent, Model* model) + : + BWindow(WINDOW_FRAME, B_TRANSLATE("Settings"), + B_FLOATING_WINDOW_LOOK, B_MODAL_SUBSET_WINDOW_FEEL, + B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS + | B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_NOT_CLOSABLE ), + fModel(model) +{ + AddToSubset(parent); + _InitUiControls(); + _UpdateUiFromModel(); + + BLayoutBuilder::Group<>(this, B_VERTICAL, 0) + .AddGroup(B_VERTICAL, 0) + .SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING, + B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING) + .Add(fCanShareAnonymousUsageDataCheckBox) + .End() + .Add(new BSeparatorView(B_HORIZONTAL)) + // rule off + .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING) + .SetInsets(0, B_USE_DEFAULT_SPACING, + B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING) + .AddGlue() + .Add(fCancelButton) + .Add(fApplyButton) + .End(); + + CenterOnScreen(); +} + + +SettingsWindow::~SettingsWindow() +{ +} + + +void +SettingsWindow::_InitUiControls() +{ + fCanShareAnonymousUsageDataCheckBox = new BCheckBox( + "share anonymous usage data", + B_TRANSLATE("Share anonymous usage data with HaikuDepotServer"), NULL); + + fApplyButton = new BButton("apply", B_TRANSLATE("Apply"), + new BMessage(MSG_APPLY)); + fCancelButton = new BButton("cancel", B_TRANSLATE("Cancel"), + new BMessage(B_QUIT_REQUESTED)); +} + + +void +SettingsWindow::_UpdateUiFromModel() +{ + fCanShareAnonymousUsageDataCheckBox->SetValue( + fModel->CanShareAnonymousUsageData() ? 1 : 0); +} + + +void +SettingsWindow::_UpdateModelFromUi() +{ + fModel->SetCanShareAnonymousUsageData( + 0 != fCanShareAnonymousUsageDataCheckBox->Value()); +} + + +void +SettingsWindow::MessageReceived(BMessage* message) +{ + switch (message->what) { + case MSG_APPLY: + _UpdateModelFromUi(); + BMessenger(this).SendMessage(B_QUIT_REQUESTED); + break; + default: + BWindow::MessageReceived(message); + break; + } +} diff --git a/src/apps/haikudepot/ui/SettingsWindow.h b/src/apps/haikudepot/ui/SettingsWindow.h new file mode 100644 index 0000000000..bcf580cbf3 --- /dev/null +++ b/src/apps/haikudepot/ui/SettingsWindow.h @@ -0,0 +1,45 @@ +/* + * Copyright 2021, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef SETTINGS_WINDOW_H +#define SETTINGS_WINDOW_H + +#include +#include +#include + +#include "BarberPole.h" +#include "HaikuDepotConstants.h" +#include "UserDetail.h" +#include "UserUsageConditions.h" + + +class BButton; +class BCheckBox; +class Model; + + +class SettingsWindow : public BWindow { +public: + SettingsWindow(BWindow* parent, Model* model); + virtual ~SettingsWindow(); + + virtual void MessageReceived(BMessage* message); + +private: + void _InitUiControls(); + void _UpdateUiFromModel(); + void _UpdateModelFromUi(); + +private: + Model* fModel; + + BCheckBox* fCanShareAnonymousUsageDataCheckBox; + + BButton* fApplyButton; + BButton* fCancelButton; +}; + + +#endif // SETTINGS_WINDOW_H