123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- #include "buycatalog.h"
- #include "productinfo.h"
- #include "mainwindow.h"
- #include "DownloadManager.h"
- #include <QtCore/QFile>
- #include <QtCore/QTextStream>
- #include <QtGui/QApplication>
- #include <QtCore/QDebug>
- #include <QtCore/QMetaType>
- #include <QtGui/QMessageBox>
- #include <QProgressDialog>
- #include <QSettings>
- #include <QDir>
- Q_DECLARE_METATYPE(IAPClient::ProductDataList) //to be able to transfer data this type with Qt::QueuedConnection
- #define INVALID_VALUE -1
- #define KSettingCompanyName "NokiaProjects"
- #define KSettingApplicationName "BuyAndDownloadExample"
- #define KSettingUserDighestKey "accountdigest"
- /*
- * BuyCatalog - a QDialog inherited class wich will display a list of available
- * products allowing the user to trigger the In-Application Purchase experience
- * at a click of a button.
- *
- * This constuctor reads a list of product IDs from the catalog.dat file and
- * asks for the corresponding product data (IAPClient::ProductData) which the
- * API will fetch from Ovi.
- */
- BuyCatalog::BuyCatalog(QWidget *parent)
- : QDialog(parent),
- productsRequested(0),
- current_requestId(INVALID_VALUE),
- infoDialog(NULL)
- {
- // required so that IAPClient::ProductData can be queued in the signal
- qRegisterMetaType<IAPClient::ProductDataHash>("IAPClient::ProductDataHash");
- qRegisterMetaType<IAPClient::UserAndDeviceDataHash>("IAPClient::ProductDataHash");
- iap_client = new IAPClient(this);
- // connect IAP API's signals to app's slots
- connect(iap_client, SIGNAL(productDataReceived( int, QString, IAPClient::ProductDataHash)), this,
- SLOT(productDataReceived(int, QString, IAPClient::ProductDataHash)), Qt::QueuedConnection);
- connect(iap_client, SIGNAL(purchaseCompleted( int , QString, QString)), this,
- SLOT(purchaseCompleted( int , QString, QString)),Qt::QueuedConnection);
- connect(iap_client, SIGNAL(purchaseFlowFinished(int)), this, SLOT(purchaseFlowFinished(int)), Qt::QueuedConnection);
- connect(iap_client, SIGNAL(restorableProductsReceived( int, QString, IAPClient::ProductDataList)), this,
- SLOT(restorableProductsReceived( int, QString, IAPClient::ProductDataList)));
- connect(iap_client, SIGNAL(restorationCompleted(int, QString, QString)), this,
- SLOT(restorationCompleted(int, QString, QString)),Qt::QueuedConnection);
- ui.setupUi(this);
- ui.listWidget->setStyleSheet(QString::fromUtf8("QListWidget{ background-color: grey; border-style: outset; border-width: 1px; border-radius: 1px; border-color: black; selection-color: black; selection-background-color: red; }" \
- "QPushButton{ background-color: green; border-style: outset; border-width: 2px; border-radius: 10px; border-color: beige; font: bold 14px; min-width: 4em; max-width:5em; padding: 6px; }" \
- "QPushButton:pressed { background-color: rgb(224, 0, 0); border-style: inset;}"));
- connect(ui.listWidget, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(moreProductInfo(QListWidgetItem*)));
- Q_ASSERT(parent);
- connect(ui.backButton, SIGNAL(clicked()), parent, SLOT(catalogClosed()));
- connect(ui.restoreButton, SIGNAL(clicked()), this, SLOT(restoreProducts()));
- ui.restoreButton->setEnabled(false);
- busyIndicator = new QProgressDialog("", "", 1, 1, this);
- busyIndicator->setLabelText("...please wait...");
- busyIndicator->setCancelButton(NULL);
- busyIndicator->setWindowModality(Qt::WindowModal);
- dm = new DownloadManager(this);
- connect(dm, SIGNAL(productListComplete()), this, SLOT(productListComplete()));
- connect(dm, SIGNAL(productComplete(QByteArray*)), this, SLOT(productComplete(QByteArray*)));
- connect(iap_client, SIGNAL(userAndDeviceDataReceived(int,
- QString, IAPClient::UserAndDeviceDataHash)),
- this, SLOT(userAndDeviceDataReceived(int,
- QString, IAPClient::UserAndDeviceDataHash)));
- QSettings settings(KSettingCompanyName, KSettingApplicationName);
- userAuth = settings.value(KSettingUserDighestKey, "").toString();
- if(isReadyToBuy()){
- dm->getProductList(products);
- }else{
- current_requestId = iap_client->getUserAndDeviceId(IAPClient::OnlyInSilentAuthentication);
- }
- }
- /*
- *
- *
- *
- */
- BuyCatalog::~BuyCatalog()
- {
- delete infoDialog;
- delete iap_client;
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::infoDialogClosed(int result)
- {
- if(result==QDialog::Accepted)
- {
- IAPClient::ProductDataHash info = available_products[current_product_index];
- current_productId = info.value("id").toString();
- dm->getProduct(userAuth, current_productId);
- }
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::moreProductInfo(QListWidgetItem* item)
- {
- if(isIAPclientBusy())
- return;
-
- qDebug() << "moreProductInfo";
- if(infoDialog!=NULL)
- {
- disconnect(infoDialog, SIGNAL(finished(int)), this, SLOT(infoDialogClosed(int)));
- delete infoDialog;
- infoDialog = NULL;
- }
- current_product_index = ui.listWidget->row(item);
- IAPClient::ProductDataHash info = available_products[current_product_index];
- // create a dialog for showing detailed product info
- infoDialog = new ProductInfo(info.value("info").toString(), info.value("description").toString(), info.value("price").toString(), this);
- // get notified when the product info dialog is closed
- connect(infoDialog, SIGNAL(finished(int)), this, SLOT(infoDialogClosed(int)));
- // show the dialog
- infoDialog->showFullScreen();
- }
- /*
- * Returns true if the product files can be accessed or false if the purchase/restore is needed
- */
- bool BuyCatalog::isProductActivated(QString product)
- {
- // TODO
- return false;
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::buyProduct()
- {
- if(isIAPclientBusy() || !isReadyToBuy())
- return;
- busyIndicator->show();
- qDebug() << "buyProduct";
- // locate the list item which holds the button that send a signal to this slot
- QWidget *clickedWidget = qobject_cast<QWidget *>(sender()->parent());
- for (int index = 0; index < ui.listWidget->count(); index++)
- {
- QListWidgetItem *runningItem = ui.listWidget->item(index);
- QWidget *widget = ui.listWidget->itemWidget(runningItem);
- if (clickedWidget == widget)
- {
- IAPClient::ProductDataHash info = available_products[index];
- // issue a buy request for the corresponding product
- // if the user has paid for this content already, restoration of the license is needed
- current_productId = info.value("id").toString();
- dm->getProduct(userAuth,current_productId);
- break;
- }
- }
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::productDataReceived( int requestId, QString status, IAPClient::ProductDataHash productData )
- {
- if(!resetIAPrequest(requestId))
- return;
- qDebug() << "productDataReceived";
- //Q_ASSERT(requestId == current_requestId);
-
- if(QString::compare(status, "OK", Qt::CaseInsensitive)==0)
- {
- available_products.append(productData);
- // add a new item to the products catalog UI's list
- QListWidgetItem *item = new QListWidgetItem();
- ui.listWidget->addItem(item);
- QLabel *labelTitle = new QLabel(productData.value("info").toString());
- QLabel *labelPrice = new QLabel(productData.value("price").toString());
- QHBoxLayout *layoutTitleAndPrice= new QHBoxLayout();
- layoutTitleAndPrice->addWidget(labelTitle);
- layoutTitleAndPrice->addWidget(labelPrice);
- QPushButton *button = new QPushButton("Buy...");
- QLabel *labelShort = new QLabel(productData.value("shortdescription").toString());
- QHBoxLayout *layoutShortBuy= new QHBoxLayout();
- layoutShortBuy->addWidget(labelShort);
- layoutShortBuy->addWidget(button);
- QVBoxLayout *layoutItem = new QVBoxLayout();
- layoutItem->addItem(layoutTitleAndPrice);
- layoutItem->addItem(layoutShortBuy);
- QWidget *widget = new QWidget();
- widget->setLayout(layoutItem);
- item->setSizeHint(widget->sizeHint());
- ui.listWidget->setItemWidget(item, widget);
- // if multiple products are available, let's highlight the one that triggered the shopping session
- if( productData.value("id").toString() == defaultProduct)
- ui.listWidget->setCurrentItem(item);
- connect(button, SIGNAL(clicked()), this, SLOT(buyProduct()));
- }
- else // what are all the possible status messages?
- {
- // the product is not available for one reason or another?
- // probably you can ignore this
- // or report it to your server for analysis
- qDebug() << "Requested product could not be retrived: " << status;
- }
- // if we don't have any more products ...
- if(productsRequested == products.count())
- {
- ui.progressBar->setVisible(false);
- // have we got any product data?
- if(!available_products.count())
- {
- QMessageBox message;
- message.setText("No products available. Please try again later!");
- message.exec();
- // close catalog
- reject();
- }
- }
- else
- {
- // let's look for the next product's data
- requestNextProduct();
- }
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::requestNextProduct()
- {
- qDebug() << "requestNextProduct";
- if(productsRequested < products.count())
- {
- QString prod = products[productsRequested++];
- current_requestId = iap_client->getProductData(prod);
- qDebug() << "+ Request for product" << prod << " returned id: " << current_requestId;
- ui.progressBar->setValue(productsRequested-1);
- }
- else
- ui.progressBar->setValue(productsRequested);
- if(productsRequested == products.count() - 1){
- ui.restoreButton->setEnabled(true);
- }
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::purchaseCompleted( int requestId, QString status, QString purchaseTicket )
- {
- qDebug() << "purchaseCompleted with status: " << status;
-
- if(!resetIAPrequest(requestId))
- return;
-
- if(QString::compare(status, "OK", Qt::CaseInsensitive)==0
- || QString::compare(status, "RestorableProduct", Qt::CaseInsensitive)==0)
- {
- dm->getProduct(userAuth, current_productId, &purchaseTicket);
- }
- else
- {
- // some error
- // error message already displayed by In-Application Purchase UI, may also be reflected in app's UI.
- }
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::userAndDeviceDataReceived(int requestId, QString status,
- IAPClient::UserAndDeviceDataHash response)
- {
- if(!resetIAPrequest(requestId))
- return;
- qDebug() << "userAndDeviceDataReceived : " << status;
- if(status.compare("SilentOperationFailed") == 0){
- current_requestId = iap_client->getUserAndDeviceId(IAPClient::DefaultAuthentication);
- // NOTE: you have to sign in to OVI Store client
- // to have getUserAndDeviceId(IAPClient::OnlyInSilentAuthentication)
- // working properly. Otherwise use explicit user authentication
- return;
- }
- if(status.compare("OK") != 0)
- return;
- userAuth = response.value("accountdigest").toString();
- QSettings settings(KSettingCompanyName, KSettingApplicationName);
- settings.setValue(KSettingUserDighestKey, userAuth);
- products.clear();
- dm->getProductList(products);
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::restorableProductsReceived( int requestId, QString status,
- IAPClient::ProductDataList items )
- {
- if(!resetIAPrequest(requestId))
- return;
- qDebug() << "restorableProductsReceived with status: " << status;
- current_requestId = INVALID_VALUE;
- if(!items.empty()){
- restorableProductItems.append(items);
- current_productId = restorableProductItems.takeFirst().value("id").toString();
- current_requestId = iap_client->restoreProduct(current_productId);
- }
- if(current_requestId == INVALID_VALUE){
- busyIndicator->close();
- ui.backButton->click();
- }else{
- ui.progressBar->setRange(0, restorableProductItems.count() + 1);
- ui.progressBar->setTextVisible(false);
- ui.progressBar->setVisible(true);
- }
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::restorationFlowFinished( int requestId )
- {
- resetIAPrequest(requestId);
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::restorationCompleted( int requestId, QString status, QString purchaseTicket )
- {
- if(!resetIAPrequest(requestId))
- return;
- qDebug() << "restorationCompleted with status: " << status;
- purchaseCompleted(requestId, status, purchaseTicket);
- if(restorableProductItems.empty()){
- busyIndicator->close();
- ui.backButton->click();
- }else{
- ui.progressBar->setValue(ui.progressBar->value() + 1);
- current_productId = restorableProductItems.takeFirst().value("id").toString();
- current_requestId = iap_client->restoreProduct(current_productId);
- }
- }
- /*
- *
- *
- *
- */
- void BuyCatalog::purchaseFlowFinished( int requestId )
- {
- resetIAPrequest(requestId);
- }
- void BuyCatalog::restoreProducts()
- {
- if(isIAPclientBusy())
- return;
-
- current_requestId = iap_client->getRestorableProducts();
- //lock ui
- busyIndicator->setLabelText("restoration in progress...");
- busyIndicator->show();
- restorableProductItems.clear();
- }
- void BuyCatalog::productListComplete()
- {
- qDebug() << "productListComplete count: " << products.count();
- if(products.count()>0)
- {
- ui.progressBar->setRange(0, products.count());
- ui.progressBar->setTextVisible(false);
- // request product data from Ovi for the products
- requestNextProduct();
- }
- else
- ui.progressBar->setVisible(false);
- }
- void BuyCatalog::productComplete(QByteArray* buf)
- {
- if(dm->isNeedToBuy(*buf)){
- qDebug() << "start IAP experience";
- //in purchaseProduct call flag value 'IAPClient::NoForcedRestoration' does not fetches
- //ticket from OVI and there is no other API call to get already purchased item ticket except restoration -
- //-- so 'IAPClient::ForcedAutomaticRestoration' flag value is reverted back -- fix related to
- //https://support.preminetsolution.com/jira/browse/TECHPUBS-2
- //https://support.preminetsolution.com/jira/browse/JUNIPERQA-24766
- current_requestId = iap_client->purchaseProduct(current_productId,
- IAPClient::ForcedAutomaticRestoration);
- }else{
- QString fname = MainWindow::getPurchaseUri(current_productId);
- QFile file(fname);
- qDebug() << " BuyCatalog::productComplete : saving to file : " << fname;
- QString privatedir(MainWindow::getPurchaseDir());
- if (!QDir(privatedir).exists())
- QDir().mkdir(privatedir);
- if(file.open(QIODevice::WriteOnly)) {
- file.write(*buf);
- file.close();
- qDebug() << "content " << current_productId << "saved";
- busyIndicator->close();
- ui.backButton->click();
- }
- }
- }
- bool BuyCatalog::isIAPclientBusy()
- {
- qDebug() << "IAPclient is busy";
- return (current_requestId != INVALID_VALUE);
- }
- bool BuyCatalog::resetIAPrequest(int requestId)
- {
- if(requestId != current_requestId)
- return false;
- busyIndicator->close();
- current_requestId = INVALID_VALUE;
- return true;
- }
- bool BuyCatalog::isReadyToBuy()
- {
- return (!userAuth.isEmpty());
- }
|