From 73c2c7b4b5044a152e9d194364578c1c9a13d8c1 Mon Sep 17 00:00:00 2001 From: Brian Hill Date: Thu, 20 Apr 2017 17:58:46 -0400 Subject: [PATCH] SoftwareUpdater updates *Added a check-only option to check for updates and display a notification when updates are available. *Added argv commands to specify an action when using Terminal or BRoster to launch app: commands [update | check | full-sync] *Draw a download progress bar on the package icon *Increased the size of the package icon to improve the bar look *Added package counts to each list category --- src/apps/softwareupdater/CheckAction.cpp | 76 +++++ src/apps/softwareupdater/CheckAction.h | 26 ++ src/apps/softwareupdater/CheckManager.cpp | 297 ++++++++++++++++++ src/apps/softwareupdater/CheckManager.h | 75 +++++ src/apps/softwareupdater/Jamfile | 2 + .../softwareupdater/SoftwareUpdaterApp.cpp | 55 +++- src/apps/softwareupdater/SoftwareUpdaterApp.h | 4 +- .../softwareupdater/SoftwareUpdaterWindow.cpp | 145 ++++++++- .../softwareupdater/SoftwareUpdaterWindow.h | 24 +- src/apps/softwareupdater/UpdateAction.cpp | 23 +- src/apps/softwareupdater/UpdateAction.h | 2 +- src/apps/softwareupdater/UpdateManager.cpp | 66 ++-- src/apps/softwareupdater/UpdateManager.h | 11 +- src/apps/softwareupdater/WorkingLooper.cpp | 22 +- src/apps/softwareupdater/WorkingLooper.h | 9 +- src/apps/softwareupdater/constants.h | 5 +- 16 files changed, 761 insertions(+), 81 deletions(-) create mode 100644 src/apps/softwareupdater/CheckAction.cpp create mode 100644 src/apps/softwareupdater/CheckAction.h create mode 100644 src/apps/softwareupdater/CheckManager.cpp create mode 100644 src/apps/softwareupdater/CheckManager.h diff --git a/src/apps/softwareupdater/CheckAction.cpp b/src/apps/softwareupdater/CheckAction.cpp new file mode 100644 index 0000000000..d298ae0baf --- /dev/null +++ b/src/apps/softwareupdater/CheckAction.cpp @@ -0,0 +1,76 @@ +/* + * Copyright 2017, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Brian Hill + */ + + +#include "CheckAction.h" + +#include +#include +#include + + +#undef B_TRANSLATION_CONTEXT +#define B_TRANSLATION_CONTEXT "CheckAction" + + +using namespace BPackageKit; +//using namespace BPackageKit::BPrivate; +using namespace BPackageKit::BManager::BPrivate; + + +CheckAction::CheckAction() +{ + fCheckManager = new(std::nothrow) + CheckManager(B_PACKAGE_INSTALLATION_LOCATION_SYSTEM); +} + + +CheckAction::~CheckAction() +{ + delete fCheckManager; +} + + +status_t +CheckAction::Perform() +{ + try { + fCheckManager->CheckNetworkConnection(); + + fCheckManager->Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES + | BPackageManager::B_ADD_REMOTE_REPOSITORIES + | BPackageManager::B_REFRESH_REPOSITORIES); + +// fUpdateManager->SetDebugLevel(1); + // These values indicate that all updates should be installed + int packageCount = 0; + const char* const packages = ""; + fCheckManager->Update(&packages, packageCount); + } catch (BFatalErrorException ex) { + fprintf(stderr, B_TRANSLATE( + "Fatal error while checking for updates: %s\n"), + ex.Message().String()); + be_app->PostMessage(kMsgFinalQuit); + return ex.Error(); + } catch (BAbortedByUserException ex) { + be_app->PostMessage(kMsgFinalQuit); + return B_OK; + } catch (BNothingToDoException ex) { + fprintf(stdout, B_TRANSLATE("There were no updates found.")); + be_app->PostMessage(kMsgFinalQuit); + return B_OK; + } catch (BException ex) { + fprintf(stderr, B_TRANSLATE( + "Exception occurred while checking for updates: %s\n"), + ex.Message().String()); + be_app->PostMessage(kMsgFinalQuit); + return B_ERROR; + } + + return B_OK; +} diff --git a/src/apps/softwareupdater/CheckAction.h b/src/apps/softwareupdater/CheckAction.h new file mode 100644 index 0000000000..98a4619aaa --- /dev/null +++ b/src/apps/softwareupdater/CheckAction.h @@ -0,0 +1,26 @@ +/* + * Copyright 2017, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Brian Hill + */ +#ifndef CHECK_ACTION_H +#define CHECK_ACTION_H + + +#include "CheckManager.h" + + +class CheckAction { +public: + CheckAction(); + ~CheckAction(); + status_t Perform(); + +private: + CheckManager* fCheckManager; +}; + + +#endif // CHECK_ACTION_H diff --git a/src/apps/softwareupdater/CheckManager.cpp b/src/apps/softwareupdater/CheckManager.cpp new file mode 100644 index 0000000000..b3220dcdc8 --- /dev/null +++ b/src/apps/softwareupdater/CheckManager.cpp @@ -0,0 +1,297 @@ +/* + * Copyright 2013-2017, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler + * Rene Gollent + * Ingo Weinhold + * Brian Hill + */ + + +#include "CheckManager.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "constants.h" + +using namespace BPackageKit; +using namespace BPackageKit::BManager::BPrivate; + +#undef B_TRANSLATION_CONTEXT +#define B_TRANSLATION_CONTEXT "CheckManager" + + +CheckManager::CheckManager(BPackageInstallationLocation location) + : + BPackageManager(location, &fClientInstallationInterface, this), + BPackageManager::UserInteractionHandler(), + fClientInstallationInterface() +{ +} + + +void +CheckManager::CheckNetworkConnection() +{ + BNetworkRoster& roster = BNetworkRoster::Default(); + BNetworkInterface interface; + uint32 cookie = 0; + while (roster.GetNextInterface(&cookie, interface) == B_OK) { + uint32 flags = interface.Flags(); + if ((flags & IFF_LOOPBACK) == 0 + && (flags & (IFF_UP | IFF_LINK)) == (IFF_UP | IFF_LINK)) { + return; + } + } + + // No network connection detected, cannot continue + fprintf(stderr, B_TRANSLATE("No active network connection was found.\n")); + throw BAbortedByUserException(); +} + + +void +CheckManager::JobFailed(BSupportKit::BJob* job) +{ + BString error = job->ErrorString(); + if (error.Length() > 0) { + error.ReplaceAll("\n", "\n*** "); + fprintf(stderr, "%s", error.String()); + } +} + + +void +CheckManager::JobAborted(BSupportKit::BJob* job) +{ + printf("Job aborted\n"); + BString error = job->ErrorString(); + if (error.Length() > 0) { + error.ReplaceAll("\n", "\n*** "); + fprintf(stderr, "%s", error.String()); + } +} + + +void +CheckManager::HandleProblems() +{ + printf("Encountered problems:\n"); + + int32 problemCount = fSolver->CountProblems(); + for (int32 i = 0; i < problemCount; i++) { + BSolverProblem* problem = fSolver->ProblemAt(i); + printf("problem %" B_PRId32 ": %s\n", i + 1, + problem->ToString().String()); + } + + BString title(B_TRANSLATE("Available updates found")); + BString text(B_TRANSLATE("Click here to run SoftwareUpdater. Some updates " + "will require a problem solution to be selected.")); + _SendNotification(title, text); + + throw BAbortedByUserException(); +} + + +void +CheckManager::ConfirmChanges(bool fromMostSpecific) +{ + int32 count = fInstalledRepositories.CountItems(); + int32 updateCount = 0; + + if (fromMostSpecific) { + for (int32 i = count - 1; i >= 0; i--) + _CountUpdates(*fInstalledRepositories.ItemAt(i), updateCount); + } else { + for (int32 i = 0; i < count; i++) + _CountUpdates(*fInstalledRepositories.ItemAt(i), updateCount); + } + + printf("Update count=%" B_PRId32 "\n", updateCount); + if (updateCount > 0) { + BString title(B_TRANSLATE("%count% packages have available updates")); + BString count; + count << updateCount; + title.ReplaceFirst("%count%", count); + BString text(B_TRANSLATE("Click here to install updates.")); + _SendNotification(title, text); + } + throw BAbortedByUserException(); +} + + +void +CheckManager::Warn(status_t error, const char* format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + + if (error == B_OK) + printf("\n"); + else + printf(": %s\n", strerror(error)); +} + + +void +CheckManager::ProgressPackageDownloadStarted(const char* packageName) +{ + printf("Downloading %s...\n", packageName); +} + + +void +CheckManager::ProgressPackageDownloadActive(const char* packageName, + float completionPercentage, off_t bytes, off_t totalBytes) +{ + static const char* progressChars[] = { + "\xE2\x96\x8F", + "\xE2\x96\x8E", + "\xE2\x96\x8D", + "\xE2\x96\x8C", + "\xE2\x96\x8B", + "\xE2\x96\x8A", + "\xE2\x96\x89", + "\xE2\x96\x88", + }; + + int width = 70; + + struct winsize winSize; + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winSize) == 0 + && winSize.ws_col < 77) { + // We need 7 characters for the percent display + width = winSize.ws_col - 7; + } + + int position; + int ipart = (int)(completionPercentage * width); + int fpart = (int)(((completionPercentage * width) - ipart) * 8); + + printf("\r"); // return to the beginning of the line + + for (position = 0; position < width; position++) { + if (position < ipart) { + // This part is fully downloaded, show a full block + printf(progressChars[7]); + } else if (position > ipart) { + // This part is not downloaded, show a space + printf(" "); + } else { + // This part is partially downloaded + printf(progressChars[fpart]); + } + } + + // Also print the progress percentage + printf(" %3d%%", (int)(completionPercentage * 100)); + + fflush(stdout); + +} + + +void +CheckManager::ProgressPackageDownloadComplete(const char* packageName) +{ + // Overwrite the progress bar with whitespace + printf("\r"); + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + for (int i = 0; i < (w.ws_col); i++) + printf(" "); + printf("\r\x1b[1A"); // Go to previous line. + + printf("Downloading %s...done.\n", packageName); +} + + +void +CheckManager::ProgressPackageChecksumStarted(const char* title) +{ + printf("%s...", title); +} + + +void +CheckManager::ProgressPackageChecksumComplete(const char* title) +{ + printf("done.\n"); +} + + +void +CheckManager::_CountUpdates(InstalledRepository& installationRepository, + int32& updateCount) +{ + if (!installationRepository.HasChanges()) + return; + + PackageList& packagesToActivate + = installationRepository.PackagesToActivate(); + PackageList& packagesToDeactivate + = installationRepository.PackagesToDeactivate(); + + for (int32 i = 0; + BSolverPackage* installPackage = packagesToActivate.ItemAt(i); + i++) { + for (int32 j = 0; + BSolverPackage* uninstallPackage = packagesToDeactivate.ItemAt(j); + j++) { + if (installPackage->Info().Name() == uninstallPackage->Info().Name()) { + updateCount++; + break; + } + } + } +} + + +void +CheckManager::_SendNotification(const char* title, const char* text) +{ + BNotification notification(B_INFORMATION_NOTIFICATION); + notification.SetGroup("SoftwareUpdater"); + notification.SetTitle(title); + notification.SetContent(text); + notification.SetOnClickApp(kAppSignature); + BBitmap icon(_GetIcon()); + if (icon.IsValid()) + notification.SetIcon(&icon); + bigtime_t timeout = 30000000; + notification.Send(timeout); +} + + +BBitmap +CheckManager::_GetIcon() +{ + int32 iconSize = B_LARGE_ICON; + BBitmap icon(BRect(0, 0, iconSize - 1, iconSize - 1), 0, B_RGBA32); + team_info teamInfo; + get_team_info(B_CURRENT_TEAM, &teamInfo); + app_info appInfo; + be_roster->GetRunningAppInfo(teamInfo.team, &appInfo); + BNodeInfo::GetTrackerIcon(&appInfo.ref, &icon, icon_size(iconSize)); + return icon; +} diff --git a/src/apps/softwareupdater/CheckManager.h b/src/apps/softwareupdater/CheckManager.h new file mode 100644 index 0000000000..50e6b2b11b --- /dev/null +++ b/src/apps/softwareupdater/CheckManager.h @@ -0,0 +1,75 @@ +/* + * Copyright 2013-2017, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + * Rene Gollent + * Brian Hill + */ +#ifndef CHECK_MANAGER_H +#define CHECK_MANAGER_H + + +#include + +#include +#include + +#include "constants.h" +#include "SoftwareUpdaterWindow.h" + +class ProblemWindow; + +//using namespace BPackageKit; +using BPackageKit::BPackageInstallationLocation; +using BPackageKit::BPrivate::BDaemonClient; +using BPackageKit::BManager::BPrivate::BPackageManager; + + +class CheckManager : public BPackageManager, + private BPackageManager::UserInteractionHandler { +public: + CheckManager( + BPackageInstallationLocation location); + + void CheckNetworkConnection(); + virtual void JobFailed(BSupportKit::BJob* job); + virtual void JobAborted(BSupportKit::BJob* job); + +private: + // UserInteractionHandler + virtual void HandleProblems(); + virtual void ConfirmChanges(bool fromMostSpecific); + virtual void Warn(status_t error, const char* format, ...); + + virtual void ProgressPackageDownloadStarted( + const char* packageName); + virtual void ProgressPackageDownloadActive( + const char* packageName, + float completionPercentage, + off_t bytes, off_t totalBytes); + virtual void ProgressPackageDownloadComplete( + const char* packageName); + virtual void ProgressPackageChecksumStarted( + const char* packageName); + virtual void ProgressPackageChecksumComplete( + const char* packageName); + +private: + void _CountUpdates(InstalledRepository& + installationRepository, + int32& updateCount); + void _SendNotification(const char* title, + const char* text); + BBitmap _GetIcon(); + +private: + BPackageManager::ClientInstallationInterface + fClientInstallationInterface; + + ProblemWindow* fProblemWindow; +}; + + +#endif // CHECK_MANAGER_H diff --git a/src/apps/softwareupdater/Jamfile b/src/apps/softwareupdater/Jamfile index 0e38bbdafd..5523d7c688 100644 --- a/src/apps/softwareupdater/Jamfile +++ b/src/apps/softwareupdater/Jamfile @@ -10,6 +10,8 @@ Application SoftwareUpdater : StripeView.cpp UpdateAction.cpp UpdateManager.cpp + CheckAction.cpp + CheckManager.cpp WorkingLooper.cpp # package_daemon diff --git a/src/apps/softwareupdater/SoftwareUpdaterApp.cpp b/src/apps/softwareupdater/SoftwareUpdaterApp.cpp index 988f2d4029..85ef91f170 100644 --- a/src/apps/softwareupdater/SoftwareUpdaterApp.cpp +++ b/src/apps/softwareupdater/SoftwareUpdaterApp.cpp @@ -9,14 +9,31 @@ #include "SoftwareUpdaterApp.h" -#include "constants.h" +#include + +#undef B_TRANSLATION_CONTEXT +#define B_TRANSLATION_CONTEXT "SoftwareUpdaterApp" + + +static const char* const kUsage = B_TRANSLATE_COMMENT( + "Usage: SoftwareUpdater \n" + "Updates installed packages.\n" + "\n" + "Commands:\n" + " update - Search repositories for updates on all packages.\n" + " check - Check for available updates but only display a " + "notification with results.\n" + " full-sync - Synchronize the installed packages with the " + "repositories.\n", "Command line usage help") +; SoftwareUpdaterApp::SoftwareUpdaterApp() : - BApplication("application/x-vnd.haiku-softwareupdater"), + BApplication(kAppSignature), fWorker(NULL), - fFinalQuitFlag(false) + fFinalQuitFlag(false), + fActionRequested(UPDATE) { } @@ -50,7 +67,30 @@ SoftwareUpdaterApp::QuitRequested() void SoftwareUpdaterApp::ReadyToRun() { - fWorker = new WorkingLooper(); + fWorker = new WorkingLooper(fActionRequested); +} + + +void +SoftwareUpdaterApp::ArgvReceived(int32 argc, char **argv) +{ + // Only one command allowed + if (argc > 2) { + fprintf(stderr, kUsage); + PostMessage(B_QUIT_REQUESTED); + return; + } else if (argc == 2) { + fActionRequested = USER_SELECTION_NEEDED; + const char* command = argv[1]; + if (strcmp("update", command) == 0) + fActionRequested = UPDATE; + else if (strcmp("check", command) == 0) + fActionRequested = UPDATE_CHECK_ONLY; + else if (strcmp("full-sync", command) == 0) + fActionRequested = FULLSYNC; + else + fprintf(stderr, kUsage); + } } @@ -74,8 +114,13 @@ SoftwareUpdaterApp::MessageReceived(BMessage* message) int -main() +main(int argc, const char* const* argv) { + if (argc > 2) { + fprintf(stderr, kUsage); + return 1; + } + SoftwareUpdaterApp app; return app.Run(); } diff --git a/src/apps/softwareupdater/SoftwareUpdaterApp.h b/src/apps/softwareupdater/SoftwareUpdaterApp.h index dd233a0aec..a1c16f0609 100644 --- a/src/apps/softwareupdater/SoftwareUpdaterApp.h +++ b/src/apps/softwareupdater/SoftwareUpdaterApp.h @@ -20,13 +20,15 @@ public: SoftwareUpdaterApp(); ~SoftwareUpdaterApp(); virtual bool QuitRequested(); - void ReadyToRun(); + virtual void ReadyToRun(); + virtual void ArgvReceived(int32 argc, char **argv); void MessageReceived(BMessage* message); private: WorkingLooper* fWorker; BMessenger fWindowMessenger; bool fFinalQuitFlag; + update_type fActionRequested; }; diff --git a/src/apps/softwareupdater/SoftwareUpdaterWindow.cpp b/src/apps/softwareupdater/SoftwareUpdaterWindow.cpp index c5881926e6..f6853f9b8b 100644 --- a/src/apps/softwareupdater/SoftwareUpdaterWindow.cpp +++ b/src/apps/softwareupdater/SoftwareUpdaterWindow.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include @@ -63,7 +62,7 @@ SoftwareUpdaterWindow::SoftwareUpdaterWindow() fDetailView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); fDetailView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP)); fStatusBar = new BStatusBar("progress"); - fStatusBar->SetMaxValue(1.0); + fStatusBar->SetMaxValue(100); fListView = new PackageListView(); fScrollView = new BScrollView("scrollview", fListView, B_WILL_DRAW, @@ -175,6 +174,8 @@ SoftwareUpdaterWindow::MessageReceived(BMessage* message) fStatusBar->SetTo(percent, packageName.String(), packageCount.String()); Unlock(); + + fListView->UpdatePackageProgress(packageName.String(), percent); break; } @@ -332,11 +333,11 @@ SoftwareUpdaterWindow::UserCancelRequested() void SoftwareUpdaterWindow::AddPackageInfo(uint32 install_type, const char* package_name, const char* cur_ver, const char* new_ver, - const char* summary, const char* repository) + const char* summary, const char* repository, const char* file_name) { Lock(); fListView->AddPackage(install_type, package_name, cur_ver, new_ver, - summary, repository); + summary, repository, file_name); Unlock(); } @@ -481,7 +482,8 @@ SuperItem::SuperItem(const char* label) BListItem(), fLabel(label), fShowMoreDetails(false), - fPackageIcon(NULL) + fPackageIcon(NULL), + fItemCount(0) { } @@ -495,15 +497,21 @@ SuperItem::~SuperItem() void SuperItem::DrawItem(BView* owner, BRect item_rect, bool complete) { + owner->PushState(); + float width; owner->GetPreferredSize(&width, NULL); BString label(fLabel); + label.Append(" ("); + label << fItemCount; + label.Append(")"); owner->TruncateString(&label, B_TRUNCATE_END, width); owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR)); owner->SetFont(&fBoldFont); owner->DrawString(label.String(), BPoint(item_rect.left, item_rect.bottom - fFontHeight.descent - 1)); - owner->SetFont(&fRegularFont); + + owner->PopState(); } @@ -548,7 +556,7 @@ void SuperItem::_GetPackageIcon() { delete fPackageIcon; - fIconSize = int(fPackageItemHeight * .7); + fIconSize = int(fPackageItemHeight * .8); status_t result = B_ERROR; BRect iconRect(0, 0, fIconSize - 1, fIconSize - 1); @@ -571,7 +579,7 @@ SuperItem::_GetPackageIcon() PackageItem::PackageItem(const char* name, const char* simple_version, const char* detailed_version, const char* repository, const char* summary, - SuperItem* super) + const char* file_name, SuperItem* super) : BListItem(), fName(name), @@ -579,7 +587,10 @@ PackageItem::PackageItem(const char* name, const char* simple_version, fDetailedVersion(detailed_version), fRepository(repository), fSummary(summary), - fSuperItem(super) + fSuperItem(super), + fFileName(file_name), + fDownloadProgress(0), + fDrawBarFlag(false) { fLabelOffset = be_control_look->DefaultLabelSpacing(); } @@ -588,6 +599,8 @@ PackageItem::PackageItem(const char* name, const char* simple_version, void PackageItem::DrawItem(BView* owner, BRect item_rect, bool complete) { + owner->PushState(); + float width; owner->GetPreferredSize(&width, NULL); float nameWidth = width / 2.0; @@ -601,13 +614,20 @@ PackageItem::DrawItem(BView* owner, BRect item_rect, bool complete) //owner->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); owner->SetDrawingMode(B_OP_ALPHA); - owner->DrawBitmap(icon, BPoint(item_rect.left, - item_rect.top + offsetMarginHeight)); + BPoint location = BPoint(item_rect.left, + item_rect.top + offsetMarginHeight); + owner->DrawBitmap(icon, location); owner->SetDrawingMode(B_OP_COPY); + + if (fDrawBarFlag) + _DrawBar(location, owner, icon_size(iconSize)); + offset_width += iconSize + fLabelOffset; } + owner->SetFont(&fRegularFont); owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR)); + // Package name font_height fontHeight = fSuperItem->GetFontHeight(); BString name(fName); @@ -647,7 +667,61 @@ PackageItem::DrawItem(BView* owner, BRect item_rect, bool complete) owner->DrawString(version.String(), cursor); } - owner->SetFont(&fRegularFont); + owner->PopState(); +} + + +// Modified slightly from Tracker's BPose::DrawBar +void +PackageItem::_DrawBar(BPoint where, BView* view, icon_size which) +{ + int32 yOffset; + int32 size = which - 1; + int32 barWidth = (int32)(7.0f / 32.0f * (float)which); + if (barWidth < 4) { + barWidth = 4; + yOffset = 0; + } else + yOffset = 2; + int32 barHeight = size - 3 - 2 * yOffset; + + + // the black shadowed line + view->SetHighColor(32, 32, 32, 92); + view->MovePenTo(BPoint(where.x + size, where.y + 1 + yOffset)); + view->StrokeLine(BPoint(where.x + size, where.y + size - yOffset)); + view->StrokeLine(BPoint(where.x + size - barWidth + 1, + where.y + size - yOffset)); + + view->SetDrawingMode(B_OP_ALPHA); + + // the gray frame + view->SetHighColor(76, 76, 76, 192); + BRect rect(where.x + size - barWidth,where.y + yOffset, + where.x + size - 1,where.y + size - 1 - yOffset); + view->StrokeRect(rect); + + // calculate bar height + int32 barPos = barHeight - int32(barHeight * fDownloadProgress / 100.0); + if (barPos < 0) + barPos = 0; + else if (barPos > barHeight) + barPos = barHeight; + + // the free space bar + view->SetHighColor(255, 255, 255, 192); + + rect.InsetBy(1,1); + BRect bar(rect); + bar.bottom = bar.top + barPos - 1; + if (barPos > 0) + view->FillRect(bar); + + // the used space bar + bar.top = bar.bottom + 1; + bar.bottom = rect.bottom; + view->SetHighColor(0, 203, 0, 192); + view->FillRect(bar); } @@ -681,6 +755,13 @@ PackageItem::NameCompare(PackageItem* item) } +void +PackageItem::SetDownloadProgress(float percent) +{ + fDownloadProgress = percent; +} + + int SortPackageItems(const BListItem* item1, const BListItem* item2) { @@ -696,7 +777,9 @@ PackageListView::PackageListView() fSuperUpdateItem(NULL), fSuperInstallItem(NULL), fSuperUninstallItem(NULL), - fShowMoreDetails(false) + fShowMoreDetails(false), + fLastProgressItem(NULL), + fLastProgressValue(-1) { fMenu = new BPopUpMenu(B_EMPTY_STRING, false, false); fDetailMenuItem = new BMenuItem("Show more details", new BMessage()); @@ -778,7 +861,7 @@ PackageListView::MouseDown(BPoint where) void PackageListView::AddPackage(uint32 install_type, const char* name, const char* cur_ver, const char* new_ver, const char* summary, - const char* repository) + const char* repository, const char* file_name) { SuperItem* super; BString simpleVersion; @@ -844,11 +927,43 @@ PackageListView::AddPackage(uint32 install_type, const char* name, } PackageItem* item = new PackageItem(name, simpleVersion.String(), - detailedVersion.String(), repositoryText.String(), summary, super); + detailedVersion.String(), repositoryText.String(), summary, file_name, + super); AddUnder(item, super); + super->SetItemCount(CountItemsUnder(super, true)); } +void +PackageListView::UpdatePackageProgress(const char* packageName, float percent) +{ + // Update only every 1 percent change + int16 wholePercent = int16(percent); + if (wholePercent == fLastProgressValue) + return; + fLastProgressValue = wholePercent; + + // A new package started downloading, find the PackageItem by name + if (percent == 0) { + fLastProgressItem = NULL; + int32 count = FullListCountItems(); + for (int32 i = 0; i < count; i++) { + PackageItem* item = dynamic_cast(FullListItemAt(i)); + if (item != NULL && strcmp(item->FileName(), packageName) == 0) { + fLastProgressItem = item; + fLastProgressItem->ShowProgressBar(); + break; + } + } + } + + if (fLastProgressItem != NULL) { + fLastProgressItem->SetDownloadProgress(percent); + Invalidate(); + } +} + + void PackageListView::SortItems() { diff --git a/src/apps/softwareupdater/SoftwareUpdaterWindow.h b/src/apps/softwareupdater/SoftwareUpdaterWindow.h index 698552b2c8..33f6be9476 100644 --- a/src/apps/softwareupdater/SoftwareUpdaterWindow.h +++ b/src/apps/softwareupdater/SoftwareUpdaterWindow.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,8 @@ public: int16 GetIconSize() { return fIconSize; }; void SetDetailLevel(bool showMoreDetails); bool GetDetailLevel() { return fShowMoreDetails; }; + void SetItemCount(int32 count) + { fItemCount = count; }; private: void _SetHeights(); @@ -58,6 +61,7 @@ private: float fPackageItemHeight; BBitmap* fPackageIcon; int16 fIconSize; + int32 fItemCount; }; @@ -68,13 +72,20 @@ public: const char* detailed_version, const char* repository, const char* summary, + const char* file_name, SuperItem* super); virtual void DrawItem(BView*, BRect, bool); virtual void Update(BView *owner, const BFont *font); void SetItemHeight(const BFont* font); int NameCompare(PackageItem* item); + const char* FileName() { return fFileName.String(); }; + void SetDownloadProgress(float percent); + void ShowProgressBar() { fDrawBarFlag = true; }; private: + void _DrawBar(BPoint where, BView* view, + icon_size which); + BString fName; BString fSimpleVersion; BString fDetailedVersion; @@ -86,6 +97,9 @@ private: float fSmallTotalHeight; float fLabelOffset; SuperItem* fSuperItem; + BString fFileName; + float fDownloadProgress; + bool fDrawBarFlag; }; @@ -101,7 +115,10 @@ public: const char* cur_ver, const char* new_ver, const char* summary, - const char* repository); + const char* repository, + const char* file_name); + void UpdatePackageProgress(const char* packageName, + float percent); void SortItems(); float ItemHeight(); @@ -114,6 +131,8 @@ private: bool fShowMoreDetails; BPopUpMenu *fMenu; BMenuItem *fDetailMenuItem; + PackageItem* fLastProgressItem; + int16 fLastProgressValue; }; @@ -130,7 +149,8 @@ public: const char* cur_ver, const char* new_ver, const char* summary, - const char* repository); + const char* repository, + const char* file_name); void ShowWarningAlert(const char* text); BBitmap GetIcon(int32 iconSize); BBitmap GetNotificationIcon(); diff --git a/src/apps/softwareupdater/UpdateAction.cpp b/src/apps/softwareupdater/UpdateAction.cpp index d1a6ff9683..687f9db50c 100644 --- a/src/apps/softwareupdater/UpdateAction.cpp +++ b/src/apps/softwareupdater/UpdateAction.cpp @@ -13,8 +13,6 @@ #include #include -#include "constants.h" - #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "UpdateAction" @@ -39,15 +37,21 @@ UpdateAction::~UpdateAction() status_t -UpdateAction::Perform() +UpdateAction::Perform(update_type action_request) { try { fUpdateManager->CheckNetworkConnection(); - int32 action = fUpdateManager->GetUpdateType(); + + update_type action = action_request; + // Prompt the user if needed + if (action == USER_SELECTION_NEEDED) + action = fUpdateManager->GetUpdateType(); + if (action == CANCEL_UPDATE) throw BAbortedByUserException(); else if (action <= INVALID_SELECTION || action >= UPDATE_TYPE_END) - throw BException("Invalid update type, cannot continue with updates"); + throw BException(B_TRANSLATE( + "Invalid update type, cannot continue with updates")); fUpdateManager->Init(BPackageManager::B_ADD_INSTALLED_REPOSITORIES | BPackageManager::B_ADD_REMOTE_REPOSITORIES @@ -61,7 +65,11 @@ UpdateAction::Perform() fUpdateManager->Update(&packages, packageCount); } else if (action == FULLSYNC) fUpdateManager->FullSync(); - + else + // Should not happen but just in case + throw BException(B_TRANSLATE( + "Invalid update type, cannot continue with updates")); + } catch (BFatalErrorException ex) { fUpdateManager->FinalUpdate(B_TRANSLATE("Updates did not complete"), ex.Message()); @@ -79,7 +87,8 @@ UpdateAction::Perform() B_TRANSLATE("There were no updates found.")); return B_OK; } catch (BException ex) { - fprintf(stderr, "Exception occurred while updating packages : %s\n", + fprintf(stderr, B_TRANSLATE( + "Exception occurred while updating packages : %s\n"), ex.Message().String()); fUpdateManager->FinalUpdate(B_TRANSLATE("Updates did not complete"), ex.Message()); diff --git a/src/apps/softwareupdater/UpdateAction.h b/src/apps/softwareupdater/UpdateAction.h index 95043ed86a..bb05c781bf 100644 --- a/src/apps/softwareupdater/UpdateAction.h +++ b/src/apps/softwareupdater/UpdateAction.h @@ -16,7 +16,7 @@ class UpdateAction { public: UpdateAction(); ~UpdateAction(); - status_t Perform(); + status_t Perform(update_type action_request); private: UpdateManager* fUpdateManager; diff --git a/src/apps/softwareupdater/UpdateManager.cpp b/src/apps/softwareupdater/UpdateManager.cpp index 46232f64eb..12fa3b046e 100644 --- a/src/apps/softwareupdater/UpdateManager.cpp +++ b/src/apps/softwareupdater/UpdateManager.cpp @@ -23,18 +23,11 @@ #include #include -#include -#include -#include #include #include -#include -#include #include "constants.h" -#include "AutoDeleter.h" #include "ProblemWindow.h" -#include "ResultWindow.h" using namespace BPackageKit; using namespace BPackageKit::BManager::BPrivate; @@ -102,7 +95,7 @@ UpdateManager::CheckNetworkConnection() } -int32 +update_type UpdateManager::GetUpdateType() { int32 action = USER_SELECTION_NEEDED; @@ -113,7 +106,7 @@ UpdateManager::GetUpdateType() messenger.SendMessage(&message, &reply); reply.FindInt32(kKeyAlertResult, &action); } - return action; + return (update_type)action; } @@ -278,13 +271,7 @@ UpdateManager::ProgressPackageDownloadStarted(const char* packageName) { if (fCurrentStep == ACTION_STEP_DOWNLOAD) { BString header(B_TRANSLATE("Downloading packages")); - BString packageCount; - packageCount.SetToFormat( - B_TRANSLATE_COMMENT("%i of %i", "Do not translate %i"), - fPackageDownloadsCount, - fPackageDownloadsTotal); - _UpdateDownloadProgress(header.String(), packageName, packageCount, - 0.0); + _UpdateDownloadProgress(header.String(), packageName, 0.0); fNewDownloadStarted = false; } @@ -294,24 +281,18 @@ UpdateManager::ProgressPackageDownloadStarted(const char* packageName) void UpdateManager::ProgressPackageDownloadActive(const char* packageName, - float completionPercentage, off_t bytes, off_t totalBytes) + float completionValue, off_t bytes, off_t totalBytes) { if (fCurrentStep == ACTION_STEP_DOWNLOAD) { // Fix a bug where a 100% completion percentage gets sent at the start // of a package download if (!fNewDownloadStarted) { - if (completionPercentage > 0 && completionPercentage < 1) + if (completionValue > 0 && completionValue < 1) fNewDownloadStarted = true; else - completionPercentage = 0.0; + completionValue = 0.0; } - BString packageCount; - packageCount.SetToFormat( - B_TRANSLATE_COMMENT("%i of %i", "Do not translate %i"), - fPackageDownloadsCount, - fPackageDownloadsTotal); - _UpdateDownloadProgress(NULL, packageName, packageCount, - completionPercentage); + _UpdateDownloadProgress(NULL, packageName, completionValue * 100.0); } static const char* progressChars[] = { @@ -335,8 +316,8 @@ UpdateManager::ProgressPackageDownloadActive(const char* packageName, } int position; - int ipart = (int)(completionPercentage * width); - int fpart = (int)(((completionPercentage * width) - ipart) * 8); + int ipart = (int)(completionValue * width); + int fpart = (int)(((completionValue * width) - ipart) * 8); printf("\r"); // return to the beginning of the line @@ -354,7 +335,7 @@ UpdateManager::ProgressPackageDownloadActive(const char* packageName, } // Also print the progress percentage - printf(" %3d%%", (int)(completionPercentage * 100)); + printf(" %3d%%", (int)(completionValue * 100)); fflush(stdout); @@ -364,8 +345,10 @@ UpdateManager::ProgressPackageDownloadActive(const char* packageName, void UpdateManager::ProgressPackageDownloadComplete(const char* packageName) { - if (fCurrentStep == ACTION_STEP_DOWNLOAD) + if (fCurrentStep == ACTION_STEP_DOWNLOAD) { + _UpdateDownloadProgress(NULL, packageName, 100.0); fPackageDownloadsCount++; + } // Overwrite the progress bar with whitespace printf("\r"); @@ -497,7 +480,8 @@ UpdateManager::_PrintResult(InstalledRepository& installationRepository, upgradedPackageVersions.StringAt(position).String(), package->Info().Version().ToString().String(), package->Info().Summary().String(), - package->Repository()->Name().String()); + package->Repository()->Name().String(), + package->Info().FileName().String()); upgradeCount++; } else { printf(" install package %s-%s from %s\n", @@ -509,7 +493,8 @@ UpdateManager::_PrintResult(InstalledRepository& installationRepository, NULL, package->Info().Version().ToString().String(), package->Info().Summary().String(), - package->Repository()->Name().String()); + package->Repository()->Name().String(), + package->Info().FileName().String()); installCount++; } } @@ -525,7 +510,8 @@ UpdateManager::_PrintResult(InstalledRepository& installationRepository, package->Info().Version().ToString(), NULL, package->Info().Summary().String(), - package->Repository()->Name().String()); + package->Repository()->Name().String(), + package->Info().FileName().String()); uninstallCount++; } } @@ -551,21 +537,25 @@ UpdateManager::_UpdateStatusWindow(const char* header, const char* detail) void UpdateManager::_UpdateDownloadProgress(const char* header, - const char* packageName, const char* packageCount, - float completionPercentage) + const char* packageName, float percentageComplete) { - if (packageName == NULL || packageCount == NULL) + if (packageName == NULL) return; if (fStatusWindow->UserCancelRequested()) throw BAbortedByUserException(); + BString packageCount; + packageCount.SetToFormat( + B_TRANSLATE_COMMENT("%i of %i", "Do not translate %i"), + fPackageDownloadsCount, + fPackageDownloadsTotal); BMessage message(kMsgProgressUpdate); if (header != NULL) message.AddString(kKeyHeader, header); message.AddString(kKeyPackageName, packageName); - message.AddString(kKeyPackageCount, packageCount); - message.AddFloat(kKeyPercentage, completionPercentage); + message.AddString(kKeyPackageCount, packageCount.String()); + message.AddFloat(kKeyPercentage, percentageComplete); fStatusWindow->PostMessage(&message); } diff --git a/src/apps/softwareupdater/UpdateManager.h b/src/apps/softwareupdater/UpdateManager.h index 277b82e8f5..bad133c10a 100644 --- a/src/apps/softwareupdater/UpdateManager.h +++ b/src/apps/softwareupdater/UpdateManager.h @@ -14,11 +14,13 @@ #include #include +#include "constants.h" #include "SoftwareUpdaterWindow.h" class ProblemWindow; //using namespace BPackageKit; +using BPackageKit::BPackageInstallationLocation; using BPackageKit::BPrivate::BDaemonClient; using BPackageKit::BManager::BPrivate::BPackageManager; @@ -27,11 +29,11 @@ class UpdateManager : public BPackageManager, private BPackageManager::UserInteractionHandler { public: UpdateManager( - BPackageKit::BPackageInstallationLocation location); + BPackageInstallationLocation location); ~UpdateManager(); void CheckNetworkConnection(); - int32 GetUpdateType(); + update_type GetUpdateType(); virtual void JobFailed(BSupportKit::BJob* job); virtual void JobAborted(BSupportKit::BJob* job); void FinalUpdate(const char* header, @@ -46,7 +48,7 @@ private: const char* packageName); virtual void ProgressPackageDownloadActive( const char* packageName, - float completionPercentage, + float completionValue, off_t bytes, off_t totalBytes); virtual void ProgressPackageDownloadComplete( const char* packageName); @@ -73,8 +75,7 @@ private: const char* detail); void _UpdateDownloadProgress(const char* header, const char* packageName, - const char* packageCount, - float completionPercentage); + float percentageComplete); void _FinalUpdate(const char* header, const char* text); void _SetCurrentStep(int32 step); diff --git a/src/apps/softwareupdater/WorkingLooper.cpp b/src/apps/softwareupdater/WorkingLooper.cpp index 0bb38ade20..c3f30729ae 100644 --- a/src/apps/softwareupdater/WorkingLooper.cpp +++ b/src/apps/softwareupdater/WorkingLooper.cpp @@ -10,22 +10,38 @@ #include "WorkingLooper.h" -WorkingLooper::WorkingLooper() +WorkingLooper::WorkingLooper(update_type action) : - BLooper("WorkingLooper") + BLooper("WorkingLooper"), + fUpdateAction(NULL), + fCheckAction(NULL), + fActionRequested(action) { Run(); PostMessage(kMsgStart); } +WorkingLooper::~WorkingLooper() +{ + delete fUpdateAction; + delete fCheckAction; +} + + void WorkingLooper::MessageReceived(BMessage* message) { switch (message->what) { case kMsgStart: { - fAction.Perform(); + if (fActionRequested == UPDATE_CHECK_ONLY) { + fCheckAction = new CheckAction; + fCheckAction->Perform(); + } else { + fUpdateAction = new UpdateAction(); + fUpdateAction->Perform(fActionRequested); + } break; } diff --git a/src/apps/softwareupdater/WorkingLooper.h b/src/apps/softwareupdater/WorkingLooper.h index 61c366ca37..46309cd932 100644 --- a/src/apps/softwareupdater/WorkingLooper.h +++ b/src/apps/softwareupdater/WorkingLooper.h @@ -12,6 +12,7 @@ #include #include +#include "CheckAction.h" #include "UpdateAction.h" @@ -20,11 +21,15 @@ const uint32 kMsgStart = 'iSTA'; class WorkingLooper : public BLooper { public: - WorkingLooper(); + WorkingLooper(update_type action); + ~WorkingLooper(); void MessageReceived(BMessage* message); private: - UpdateAction fAction; + UpdateAction* fUpdateAction; + CheckAction* fCheckAction; + update_type fActionRequested; + }; diff --git a/src/apps/softwareupdater/constants.h b/src/apps/softwareupdater/constants.h index a2cd34185f..6945be54dd 100644 --- a/src/apps/softwareupdater/constants.h +++ b/src/apps/softwareupdater/constants.h @@ -8,6 +8,8 @@ #ifndef CONSTANTS_H #define CONSTANTS_H +#define kAppSignature "application/x-vnd.haiku-softwareupdater" + enum { ACTION_STEP_INIT = 0, ACTION_STEP_START, @@ -34,9 +36,8 @@ enum update_type { UPDATE, UPDATE_CHECK_ONLY, FULLSYNC, - FULLSYNC_CHECK_ONLY, UPDATE_TYPE_END -} ; +}; // Message what values static const uint32 kMsgTextUpdate = 'iUPD';